diff --git a/.circleci/config.yml b/.circleci/config.yml index 74357441127d0..e525a549b62b2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -53,7 +53,7 @@ parameters: # Build machines configs. docker-image: &docker-image docker: - - image: electronbuilds/electron:0.0.10 + - image: electronjs/build:d09fd95029bd8c1c73069888231b29688ef385ed machine-linux-medium: &machine-linux-medium <<: *docker-image @@ -61,22 +61,18 @@ machine-linux-medium: &machine-linux-medium machine-linux-2xlarge: &machine-linux-2xlarge <<: *docker-image - resource_class: 2xlarge + resource_class: 2xlarge+ machine-mac: &machine-mac macos: - xcode: "9.4.1" + xcode: "11.1.0" machine-mac-large: &machine-mac-large resource_class: large macos: - xcode: "9.4.1" + xcode: "11.1.0" # Build configurations options. -env-debug-build: &env-debug-build - GN_CONFIG: //electron/build/args/debug.gn - SKIP_DIST_ZIP: '1' - env-testing-build: &env-testing-build GN_CONFIG: //electron/build/args/testing.gn CHECK_DIST_MANIFEST: '1' @@ -85,6 +81,7 @@ env-release-build: &env-release-build GN_CONFIG: //electron/build/args/release.gn STRIP_BINARIES: true GENERATE_SYMBOLS: true + CHECK_DIST_MANIFEST: '1' env-headless-testing: &env-headless-testing DISPLAY: ':99.0' @@ -131,22 +128,31 @@ env-enable-sccache: &env-enable-sccache env-send-slack-notifications: &env-send-slack-notifications NOTIFY_SLACK: true +env-global: &env-global + ELECTRON_OUT_DIR: Default + env-linux-medium: &env-linux-medium + <<: *env-global NUMBER_OF_NINJA_PROCESSES: 3 env-linux-2xlarge: &env-linux-2xlarge + <<: *env-global NUMBER_OF_NINJA_PROCESSES: 34 env-linux-2xlarge-release: &env-linux-2xlarge-release + <<: *env-global NUMBER_OF_NINJA_PROCESSES: 16 env-machine-mac: &env-machine-mac + <<: *env-global NUMBER_OF_NINJA_PROCESSES: 6 env-mac-large: &env-mac-large + <<: *env-global NUMBER_OF_NINJA_PROCESSES: 18 env-mac-large-release: &env-mac-large-release + <<: *env-global NUMBER_OF_NINJA_PROCESSES: 8 env-ninja-status: &env-ninja-status @@ -155,6 +161,10 @@ env-ninja-status: &env-ninja-status env-disable-run-as-node: &env-disable-run-as-node GN_BUILDFLAG_ARGS: 'enable_run_as_node = false' +env-32bit-release: &env-32bit-release + # Set symbol level to 1 for 32 bit releases because of https://crbug.com/648948 + GN_BUILDFLAG_ARGS: 'symbol_level = 1' + # Individual (shared) steps. step-maybe-notify-slack-failure: &step-maybe-notify-slack-failure run: @@ -205,7 +215,25 @@ step-gclient-sync: &step-gclient-sync $GCLIENT_EXTRA_ARGS \ "$CIRCLE_REPOSITORY_URL" - gclient sync --with_branch_heads --with_tags + ELECTRON_USE_THREE_WAY_MERGE_FOR_PATCHES=1 gclient sync --with_branch_heads --with_tags + # Re-export all the patches to check if there were changes. + python src/electron/script/export_all_patches.py src/electron/patches/config.json + cd src/electron + git update-index --refresh || true + if ! git diff-index --quiet HEAD --; then + # There are changes to the patches. Make a git commit with the updated patches + git add patches + GIT_COMMITTER_NAME="Electron Bot" GIT_COMMITTER_EMAIL="anonymous@electronjs.org" git commit -m "update patches" --author="Electron Bot " + # Export it + mkdir -p ../../patches + git format-patch -1 --stdout --keep-subject --no-stat --full-index > ../../patches/update-patches.patch + echo + echo "======================================================================" + echo "There were changes to the patches when applying." + echo "Check the CI artifacts for a patch you can apply to fix it." + echo "======================================================================" + exit 1 + fi fi step-setup-env-for-build: &step-setup-env-for-build @@ -226,6 +254,29 @@ step-setup-env-for-build: &step-setup-env-for-build fi fi +step-setup-goma-for-build: &step-setup-goma-for-build + run: + name: Setup Goma + command: | + echo 'export USE_GOMA=true' >> $BASH_ENV + if [ "`uname`" == "Linux" ]; then + echo 'export NUMBER_OF_NINJA_PROCESSES=300' >> $BASH_ENV + else + echo 'export NUMBER_OF_NINJA_PROCESSES=25' >> $BASH_ENV + fi + if [ ! -z "$RAW_GOMA_AUTH" ]; then + echo $RAW_GOMA_AUTH > ~/.goma_oauth2_config + fi + git clone https://github.com/electron/build-tools.git + cd build-tools + npm install + mkdir third_party + node -e "require('./src/utils/goma.js').downloadAndPrepare()" + node -e "require('./src/utils/goma.js').ensure()" + 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 + cd .. + step-restore-brew-cache: &step-restore-brew-cache restore_cache: paths: @@ -246,14 +297,21 @@ step-get-more-space-on-mac: &step-get-more-space-on-mac command: | if [ "`uname`" == "Darwin" ]; then sudo rm -rf /Library/Developer/CoreSimulator + sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform + sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform + sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform + sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/WatchSimulator.platform + sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/AppleTVSimulator.platform + sudo rm -rf /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform fi step-delete-git-directories: &step-delete-git-directories run: - name: Delete src/.git directory on MacOS to free space + name: Delete all .git directories under src on MacOS to free space command: | if [ "`uname`" == "Darwin" ]; then - sudo rm -rf src/.git + cd src + ( find . -type d -name ".git" ) | xargs rm -rf fi # On macOS the yarn install command during gclient sync was run on a linux @@ -290,7 +348,7 @@ step-install-signing-cert-on-mac: &step-install-signing-cert-on-mac command: | if [ "`uname`" == "Darwin" ]; then cd src/electron - ./script/codesign/import-testing-cert-ci.sh + ./script/codesign/generate-identity.sh fi step-install-gnutar-on-mac: &step-install-gnutar-on-mac @@ -308,7 +366,11 @@ step-gn-gen-default: &step-gn-gen-default name: Default GN gen command: | cd src - gn gen out/Default --args='import("'$GN_CONFIG'") cc_wrapper="'"$SCCACHE_PATH"'"'" $GN_EXTRA_ARGS $GN_BUILDFLAG_ARGS" + 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 step-gn-check: &step-gn-check run: @@ -319,6 +381,8 @@ step-gn-check: &step-gn-check gn check out/Default //electron:electron_app gn check out/Default //electron:manifests gn check out/Default //electron/shell/common/api:mojo + # Check the hunspell filenames + node electron/script/gen-hunspell-filenames.js --check step-electron-build: &step-electron-build run: @@ -340,9 +404,18 @@ step-maybe-electron-dist-strip: &step-maybe-electron-dist-strip run: name: Strip electron binaries command: | - if [ "$STRIP_BINARIES" == "true" ] && [ "`uname`" != "Darwin" ]; then + if [ "$STRIP_BINARIES" == "true" ] && [ "`uname`" == "Linux" ]; then + if [ x"$TARGET_ARCH" == x ]; then + target_cpu=x64 + elif [ "$TARGET_ARCH" == "ia32" ]; then + target_cpu=x86 + else + target_cpu="$TARGET_ARCH" + fi cd src - electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" + electron/script/copy-debug-symbols.py --target-cpu="$target_cpu" --out-dir=out/Default/debug --compress + electron/script/strip-binaries.py --target-cpu="$target_cpu" + electron/script/add-debug-link.py --target-cpu="$target_cpu" --debug-dir=out/Default/debug fi step-electron-dist-build: &step-electron-dist-build @@ -387,7 +460,9 @@ step-electron-chromedriver-build: &step-electron-chromedriver-build command: | cd src ninja -C out/Default chrome/test/chromedriver -j $NUMBER_OF_NINJA_PROCESSES - electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/out/Default/chromedriver + if [ "`uname`" == "Linux" ]; then + electron/script/strip-binaries.py --target-cpu="$TARGET_ARCH" --file $PWD/out/Default/chromedriver + fi ninja -C out/Default electron:electron_chromedriver_zip step-electron-chromedriver-store: &step-electron-chromedriver-store @@ -416,6 +491,9 @@ step-electron-publish: &step-electron-publish run: name: Publish Electron Dist command: | + if [ "`uname`" == "Darwin" ]; then + rm -rf src/out/Default/obj + fi cd src/electron if [ "$UPLOAD_TO_S3" == "1" ]; then echo 'Uploading Electron release distribution to S3' @@ -467,7 +545,11 @@ step-ffmpeg-gn-gen: &step-ffmpeg-gn-gen name: ffmpeg GN gen command: | cd src - gn gen out/ffmpeg --args='import("//electron/build/args/ffmpeg.gn") cc_wrapper="'"$SCCACHE_PATH"'"'" $GN_EXTRA_ARGS" + 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 step-ffmpeg-build: &step-ffmpeg-build run: @@ -505,24 +587,31 @@ step-setup-linux-for-headless-testing: &step-setup-linux-for-headless-testing step-show-sccache-stats: &step-show-sccache-stats run: - name: Check sccache stats after build + name: Check sccache/goma stats after build command: | if [ "$SCCACHE_PATH" != "" ]; then $SCCACHE_PATH -s fi + if [ "$USE_GOMA" == "true" ]; then + $LOCAL_GOMA_DIR/goma_ctl.py stat + fi step-mksnapshot-build: &step-mksnapshot-build run: name: mksnapshot build command: | cd src + ninja -C out/Default electron:electron_mksnapshot -j $NUMBER_OF_NINJA_PROCESSES if [ "`uname`" != "Darwin" ]; then if [ "$TARGET_ARCH" == "arm" ]; then electron/script/strip-binaries.py --file $PWD/out/Default/clang_x86_v8_arm/mksnapshot + electron/script/strip-binaries.py --file $PWD/out/Default/clang_x86_v8_arm/v8_context_snapshot_generator elif [ "$TARGET_ARCH" == "arm64" ]; then electron/script/strip-binaries.py --file $PWD/out/Default/clang_x64_v8_arm64/mksnapshot + electron/script/strip-binaries.py --file $PWD/out/Default/clang_x64_v8_arm64/v8_context_snapshot_generator else electron/script/strip-binaries.py --file $PWD/out/Default/mksnapshot + electron/script/strip-binaries.py --file $PWD/out/Default/v8_context_snapshot_generator fi fi if [ "$SKIP_DIST_ZIP" != "1" ]; then @@ -534,6 +623,20 @@ step-mksnapshot-store: &step-mksnapshot-store path: src/out/Default/mksnapshot.zip destination: mksnapshot.zip +step-hunspell-build: &step-hunspell-build + run: + name: hunspell build + command: | + cd src + if [ "$SKIP_DIST_ZIP" != "1" ]; then + ninja -C out/Default electron:hunspell_dictionaries_zip -j $NUMBER_OF_NINJA_PROCESSES + fi + +step-hunspell-store: &step-hunspell-store + store_artifacts: + path: src/out/Default/hunspell_dictionaries.zip + destination: hunspell_dictionaries.zip + step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols run: name: Generate breakpad symbols @@ -541,6 +644,8 @@ step-maybe-generate-breakpad-symbols: &step-maybe-generate-breakpad-symbols if [ "$GENERATE_SYMBOLS" == "true" ]; then cd src ninja -C out/Default electron:electron_symbols + cd out/Default/breakpad_symbols + find . -name \*.sym -print0 | xargs -0 npx @sentry/cli@1.51.1 difutil bundle-sources fi step-maybe-zip-symbols: &step-maybe-zip-symbols @@ -633,10 +738,8 @@ step-touch-sync-done: &step-touch-sync-done # If a cache is matched EXACTLY then the .circle-sync-done file contains "done" step-maybe-restore-src-cache: &step-maybe-restore-src-cache restore_cache: - paths: - - ./src keys: - - v5-src-cache-{{ arch }}-{{ checksum "src/electron/.depshash" }} + - v7-src-cache-{{ checksum "src/electron/.depshash" }} name: Restoring src cache # Restore exact or closest git cache based on the hash of DEPS and .circle-sync-done @@ -647,8 +750,8 @@ step-maybe-restore-git-cache: &step-maybe-restore-git-cache paths: - ~/.gclient-cache keys: - - v2-gclient-cache-{{ arch }}-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }} - - v2-gclient-cache-{{ arch }}-{{ checksum "src/electron/.circle-sync-done" }} + - v2-gclient-cache-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }} + - v2-gclient-cache-{{ checksum "src/electron/.circle-sync-done" }} name: Conditionally restoring git cache step-set-git-cache-path: &step-set-git-cache-path @@ -665,7 +768,7 @@ step-save-git-cache: &step-save-git-cache save_cache: paths: - ~/.gclient-cache - key: v2-gclient-cache-{{ arch }}-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }} + key: v2-gclient-cache-{{ checksum "src/electron/.circle-sync-done" }}-{{ checksum "src/electron/DEPS" }} name: Persisting git cache step-run-electron-only-hooks: &step-run-electron-only-hooks @@ -693,17 +796,61 @@ step-minimize-workspace-size-from-checkout: &step-minimize-workspace-size-from-c rm -rf src/ios rm -rf src/third_party/blink/web_tests rm -rf src/third_party/blink/perf_tests - rm -rf src/third_party/hunspell_dictionaries rm -rf src/third_party/WebKit/LayoutTests # Save the src cache based on the deps hash step-save-src-cache: &step-save-src-cache save_cache: paths: - - ./src - key: v5-src-cache-{{ arch }}-{{ checksum "src/electron/.depshash" }} + - /portal + key: v7-src-cache-{{ checksum "/portal/src/electron/.depshash" }} name: Persisting src cache +# Check for doc only change +step-check-for-doc-only-change: &step-check-for-doc-only-change + run: + name: Check if commit is doc only change + command: | + cd src/electron + node script/yarn install --frozen-lockfile + if node script/doc-only-change.js --prNumber=$CIRCLE_PR_NUMBER --prURL=$CIRCLE_PULL_REQUEST --prBranch=$CIRCLE_BRANCH; then + #PR is doc only change; save file with value true to indicate doc only change + echo "true" > .skip-ci-build + else + #PR is not a doc only change; create empty file to indicate check has been done + touch .skip-ci-build + fi + +step-persist-doc-only-change: &step-persist-doc-only-change + persist_to_workspace: + root: . + paths: + - src/electron/.skip-ci-build + +step-maybe-early-exit-doc-only-change: &step-maybe-early-exit-doc-only-change + run: + name: Shortcircuit build if doc only change + command: | + if [ -s src/electron/.skip-ci-build ]; then + circleci-agent step halt + fi + +step-maybe-early-exit-no-doc-change: &step-maybe-early-exit-no-doc-change + run: + name: Shortcircuit job if change is not doc only + command: | + if [ ! -s src/electron/.skip-ci-build ]; then + circleci-agent step halt + fi + +step-ts-compile: &step-ts-compile + run: + name: Run TS/JS compile on doc only change + command: | + cd src + ninja -C out/Default electron:default_app_js -j $NUMBER_OF_NINJA_PROCESSES + ninja -C out/Default electron:electron_js2c -j $NUMBER_OF_NINJA_PROCESSES + # Lists of steps. steps-lint: &steps-lint steps: @@ -749,40 +896,12 @@ steps-lint: &steps-lint node script/yarn install --frozen-lockfile node script/yarn lint -steps-checkout-fast: &steps-checkout-fast - steps: - - *step-checkout-electron - - *step-depot-tools-get - - *step-depot-tools-add-to-path - - *step-restore-brew-cache - - *step-get-more-space-on-mac - - *step-install-gnutar-on-mac - - - *step-generate-deps-hash - - *step-touch-sync-done - - *step-maybe-restore-src-cache - - *step-maybe-restore-git-cache - - *step-set-git-cache-path - # This sync call only runs if .circle-sync-done is an EMPTY file - - *step-gclient-sync - # These next few steps reset Electron to the correct commit regardless of which cache was restored - - run: - name: Wipe Electron - command: rm -rf src/electron - - *step-checkout-electron - - *step-run-electron-only-hooks - - *step-generate-deps-hash-cleanly - - *step-mark-sync-done - - *step-minimize-workspace-size-from-checkout - - persist_to_workspace: - root: . - paths: - - depot_tools - - src - steps-checkout-and-save-cache: &steps-checkout-and-save-cache steps: - *step-checkout-electron + - *step-check-for-doc-only-change + - *step-persist-doc-only-change + - *step-maybe-early-exit-doc-only-change - *step-depot-tools-get - *step-depot-tools-add-to-path - *step-restore-brew-cache @@ -791,11 +910,13 @@ steps-checkout-and-save-cache: &steps-checkout-and-save-cache - *step-generate-deps-hash - *step-touch-sync-done - - *step-maybe-restore-src-cache + - maybe-restore-portaled-src-cache - *step-maybe-restore-git-cache - *step-set-git-cache-path # This sync call only runs if .circle-sync-done is an EMPTY file - *step-gclient-sync + - store_artifacts: + path: patches - *step-save-git-cache # These next few steps reset Electron to the correct commit regardless of which cache was restored - run: @@ -806,6 +927,13 @@ steps-checkout-and-save-cache: &steps-checkout-and-save-cache - *step-generate-deps-hash-cleanly - *step-mark-sync-done - *step-minimize-workspace-size-from-checkout + - *step-delete-git-directories + - run: + name: Move src folder to the cross-OS portal + command: | + sudo mkdir -p /portal + sudo chown -R $(id -u):$(id -g) /portal + mv ./src /portal - *step-save-src-cache - *step-save-brew-cache @@ -813,68 +941,18 @@ steps-electron-gn-check: &steps-electron-gn-check steps: - attach_workspace: at: . + - *step-maybe-early-exit-doc-only-change - *step-depot-tools-add-to-path - *step-setup-env-for-build - *step-gn-gen-default - *step-gn-check -steps-electron-build: &steps-electron-build - steps: - - attach_workspace: - at: . - - *step-depot-tools-add-to-path - - *step-setup-env-for-build - - *step-restore-brew-cache - - *step-get-more-space-on-mac - - *step-install-npm-deps-on-mac - - *step-fix-sync-on-mac - - *step-gn-gen-default - - *step-delete-git-directories - - # Electron app - - *step-electron-build - - *step-maybe-electron-dist-strip - - *step-electron-dist-build - - *step-electron-dist-store - - *step-ninja-summary - - # Native test targets - - *step-native-unittests-build - - *step-native-unittests-store - - # Node.js headers - - *step-nodejs-headers-build - - *step-nodejs-headers-store - - - *step-show-sccache-stats - - # mksnapshot - - *step-mksnapshot-build - - *step-mksnapshot-store - - *step-maybe-cross-arch-snapshot - - *step-maybe-cross-arch-snapshot-store - - # ffmpeg - - *step-ffmpeg-gn-gen - - *step-ffmpeg-build - - *step-ffmpeg-store - - # Save all data needed for a further tests run. - - *step-persist-data-for-tests - - - *step-maybe-generate-breakpad-symbols - - *step-maybe-zip-symbols - - *step-symbols-store - - # Trigger tests on arm hardware if needed - - *step-maybe-trigger-arm-test - - - *step-maybe-notify-slack-failure - -steps-electron-build-with-inline-checkout-for-tests: &steps-electron-build-with-inline-checkout-for-tests +steps-electron-ts-compile-for-doc-change: &steps-electron-ts-compile-for-doc-change steps: # Checkout - Copied ffrom steps-checkout - *step-checkout-electron + - *step-check-for-doc-only-change + - *step-maybe-early-exit-no-doc-change - *step-depot-tools-get - *step-depot-tools-add-to-path - *step-restore-brew-cache @@ -882,7 +960,7 @@ steps-electron-build-with-inline-checkout-for-tests: &steps-electron-build-with- - *step-install-gnutar-on-mac - *step-generate-deps-hash - *step-touch-sync-done - - *step-maybe-restore-src-cache + - maybe-restore-portaled-src-cache - *step-maybe-restore-git-cache - *step-set-git-cache-path # This sync call only runs if .circle-sync-done is an EMPTY file @@ -904,47 +982,9 @@ steps-electron-build-with-inline-checkout-for-tests: &steps-electron-build-with- - *step-install-npm-deps-on-mac - *step-fix-sync-on-mac - *step-gn-gen-default - - *step-delete-git-directories - # Electron app - - *step-electron-build - - *step-maybe-electron-dist-strip - - *step-electron-dist-build - - *step-electron-dist-store - - *step-ninja-summary - - # Native test targets - - *step-native-unittests-build - - *step-native-unittests-store - - # Node.js headers - - *step-nodejs-headers-build - - *step-nodejs-headers-store - - - *step-show-sccache-stats - - # mksnapshot - - *step-mksnapshot-build - - *step-mksnapshot-store - - *step-maybe-cross-arch-snapshot - - *step-maybe-cross-arch-snapshot-store - - # ffmpeg - - *step-ffmpeg-gn-gen - - *step-ffmpeg-build - - *step-ffmpeg-store - - # Save all data needed for a further tests run. - - *step-persist-data-for-tests - - - *step-maybe-generate-breakpad-symbols - - *step-maybe-zip-symbols - - *step-symbols-store - - # Trigger tests on arm hardware if needed - - *step-maybe-trigger-arm-test - - - *step-maybe-notify-slack-failure + #Compile ts/js to verify doc change didn't break anything + - *step-ts-compile steps-electron-build-for-publish: &steps-electron-build-for-publish steps: @@ -955,8 +995,9 @@ steps-electron-build-for-publish: &steps-electron-build-for-publish - *step-get-more-space-on-mac - *step-gclient-sync - *step-setup-env-for-build - - *step-gn-gen-default - *step-delete-git-directories + - *step-minimize-workspace-size-from-checkout + - *step-gn-gen-default # Electron app - *step-electron-build @@ -984,6 +1025,10 @@ steps-electron-build-for-publish: &steps-electron-build-for-publish - *step-ffmpeg-build - *step-ffmpeg-store + # hunspell + - *step-hunspell-build + - *step-hunspell-store + # typescript defs - *step-maybe-generate-typescript-defs @@ -1064,6 +1109,7 @@ steps-tests: &steps-tests steps: - attach_workspace: at: . + - *step-maybe-early-exit-doc-only-change - *step-depot-tools-add-to-path - *step-electron-dist-unzip - *step-mksnapshot-unzip @@ -1081,7 +1127,6 @@ steps-tests: &steps-tests ELECTRON_DISABLE_SECURITY_WARNINGS: 1 command: | cd src - export ELECTRON_OUT_DIR=Default (cd electron && node script/yarn test -- --ci --enable-logging) - run: name: Check test results existence @@ -1106,6 +1151,7 @@ steps-test-nan: &steps-test-nan steps: - attach_workspace: at: . + - *step-maybe-early-exit-doc-only-change - *step-depot-tools-add-to-path - *step-electron-dist-unzip - *step-setup-linux-for-headless-testing @@ -1114,13 +1160,13 @@ steps-test-nan: &steps-test-nan name: Run Nan Tests command: | cd src - export ELECTRON_OUT_DIR=Default node electron/script/nan-spec-runner.js steps-test-node: &steps-test-node steps: - attach_workspace: at: . + - *step-maybe-early-exit-doc-only-change - *step-depot-tools-add-to-path - *step-electron-dist-unzip - *step-setup-linux-for-headless-testing @@ -1129,14 +1175,296 @@ steps-test-node: &steps-test-node name: Run Node Tests command: | cd src - export ELECTRON_OUT_DIR=Default - node electron/script/node-spec-runner.js junit + node electron/script/node-spec-runner.js --default --jUnitDir=junit - store_test_results: path: src/junit chromium-upgrade-branches: &chromium-upgrade-branches /chromium\-upgrade\/[0-9]+/ +# Command Aliases +commands: + maybe-restore-portaled-src-cache: + steps: + - run: + name: Prepare for cross-OS sync restore + command: | + sudo mkdir -p /portal + sudo chown -R $(id -u):$(id -g) /portal + - *step-maybe-restore-src-cache + - run: + name: Fix the src cache restore point on macOS + command: | + if [ -d "/portal/src" ]; then + echo Relocating Cache + rm -rf src + mv /portal/src ./ + fi + checkout-from-cache: + steps: + - *step-checkout-electron + - *step-maybe-early-exit-doc-only-change + - *step-depot-tools-get + - *step-depot-tools-add-to-path + - *step-generate-deps-hash + - maybe-restore-portaled-src-cache + - run: + name: Ensure src checkout worked + command: | + if [ ! -d "src/third_party/blink" ]; then + echo src cache was not restored for some reason, idk what happened here... + exit 1 + fi + - run: + name: Wipe Electron + command: rm -rf src/electron + - *step-checkout-electron + - *step-run-electron-only-hooks + - *step-generate-deps-hash-cleanly + electron-build: + parameters: + attach: + type: boolean + default: false + persist: + type: boolean + default: true + persist-checkout: + type: boolean + default: false + checkout: + type: boolean + default: true + checkout-and-assume-cache: + type: boolean + default: false + build: + type: boolean + default: true + restore-src-cache: + type: boolean + default: true + preserve-vendor-dirs: + type: boolean + default: false + steps: + - when: + condition: << parameters.attach >> + steps: + - attach_workspace: + at: . + - *step-restore-brew-cache + - *step-install-gnutar-on-mac + - when: + condition: << parameters.checkout-and-assume-cache >> + steps: + - checkout-from-cache + - when: + condition: << parameters.checkout >> + steps: + # Checkout - Copied ffrom steps-checkout + - *step-checkout-electron + - *step-check-for-doc-only-change + - *step-persist-doc-only-change + - *step-maybe-early-exit-doc-only-change + - *step-depot-tools-get + - *step-depot-tools-add-to-path + - *step-get-more-space-on-mac + - *step-generate-deps-hash + - *step-touch-sync-done + - when: + condition: << parameters.restore-src-cache >> + steps: + - maybe-restore-portaled-src-cache + - *step-maybe-restore-git-cache + - *step-set-git-cache-path + # This sync call only runs if .circle-sync-done is an EMPTY file + - *step-gclient-sync + - store_artifacts: + path: patches + # These next few steps reset Electron to the correct commit regardless of which cache was restored + - when: + condition: << parameters.preserve-vendor-dirs >> + steps: + - run: + name: Preserve vendor dirs for release + command: | + mv src/electron/vendor/boto . + mv src/electron/vendor/requests . + - run: + name: Wipe Electron + command: rm -rf src/electron + - *step-checkout-electron + - *step-run-electron-only-hooks + - when: + condition: << parameters.preserve-vendor-dirs >> + steps: + - run: + name: Preserve vendor dirs for release + command: | + rm -rf src/electron/vendor/boto + rm -rf src/electron/vendor/requests + mv boto src/electron/vendor + mv requests src/electron/vendor/requests + - *step-generate-deps-hash-cleanly + - *step-mark-sync-done + - *step-minimize-workspace-size-from-checkout + - when: + condition: << parameters.persist-checkout >> + steps: + - persist_to_workspace: + root: . + paths: + - depot_tools + - src + + - when: + condition: << parameters.build >> + steps: + - *step-depot-tools-add-to-path + - *step-setup-env-for-build + - *step-setup-goma-for-build + - *step-get-more-space-on-mac + - *step-fix-sync-on-mac + - *step-delete-git-directories + + # Electron app + - when: + - *step-gn-gen-default + - *step-electron-build + - *step-ninja-summary + - *step-maybe-electron-dist-strip + - *step-electron-dist-build + - *step-electron-dist-store + + # Native test targets + - *step-native-unittests-build + - *step-native-unittests-store + + # Node.js headers + - *step-nodejs-headers-build + - *step-nodejs-headers-store + + - *step-show-sccache-stats + + # mksnapshot + - *step-mksnapshot-build + - *step-mksnapshot-store + - *step-maybe-cross-arch-snapshot + - *step-maybe-cross-arch-snapshot-store + + # ffmpeg + - *step-ffmpeg-gn-gen + - *step-ffmpeg-build + - *step-ffmpeg-store + + # hunspell + - *step-hunspell-build + - *step-hunspell-store + + # Save all data needed for a further tests run. + - when: + condition: << parameters.persist >> + steps: + - *step-persist-data-for-tests + + - when: + condition: << parameters.build >> + steps: + - *step-maybe-generate-breakpad-symbols + - *step-maybe-zip-symbols + - *step-symbols-store + + - when: + condition: << parameters.build >> + steps: + - run: + name: Remove the big things on macOS, this seems to be better on average + command: | + if [ "`uname`" == "Darwin" ]; then + mkdir -p src/out/Default + cd src/out/Default + find . -type f -size +50M -delete + mkdir -p gen/electron + cd gen/electron + # These files do not seem to like being in a cache, let us remove them + find . -type f -name '*_pkg_info' -delete + fi + + # Trigger tests on arm hardware if needed + - *step-maybe-trigger-arm-test + + - *step-maybe-notify-slack-failure + + electron-publish: + parameters: + attach: + type: boolean + default: false + checkout: + type: boolean + default: true + steps: + - when: + condition: << parameters.attach >> + steps: + - attach_workspace: + at: . + - when: + condition: << parameters.checkout >> + steps: + - *step-depot-tools-get + - *step-depot-tools-add-to-path + - *step-restore-brew-cache + - *step-get-more-space-on-mac + - when: + condition: << parameters.checkout >> + steps: + - *step-checkout-electron + - *step-gclient-sync + - *step-delete-git-directories + - *step-minimize-workspace-size-from-checkout + - *step-fix-sync-on-mac + - *step-setup-env-for-build + - *step-gn-gen-default + + # Electron app + - *step-electron-build + - *step-show-sccache-stats + - *step-maybe-generate-breakpad-symbols + - *step-maybe-electron-dist-strip + - *step-electron-dist-build + - *step-electron-dist-store + - *step-maybe-zip-symbols + - *step-symbols-store + + # mksnapshot + - *step-mksnapshot-build + - *step-mksnapshot-store + + # chromedriver + - *step-electron-chromedriver-build + - *step-electron-chromedriver-store + + # Node.js headers + - *step-nodejs-headers-build + - *step-nodejs-headers-store + + # ffmpeg + - *step-ffmpeg-gn-gen + - *step-ffmpeg-build + - *step-ffmpeg-store + + # hunspell + - *step-hunspell-build + - *step-hunspell-store + + # typescript defs + - *step-maybe-generate-typescript-defs + + # Publish + - *step-electron-publish + # List of all jobs. jobs: # Layer 0: Lint. Standalone. @@ -1146,13 +1474,39 @@ jobs: <<: *env-linux-medium <<: *steps-lint + ts-compile-doc-change: + <<: *machine-linux-medium + environment: + <<: *env-linux-medium + <<: *env-testing-build + <<: *steps-electron-ts-compile-for-doc-change + # Layer 1: Checkout. + linux-checkout: + <<: *machine-linux-2xlarge + environment: + <<: *env-linux-2xlarge + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True --custom-var=checkout_boto=True --custom-var=checkout_requests=True' + steps: + - electron-build: + persist: false + build: false + checkout: true + persist-checkout: true + restore-src-cache: false + preserve-vendor-dirs: true + linux-checkout-fast: <<: *machine-linux-2xlarge environment: <<: *env-linux-2xlarge GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' - <<: *steps-checkout-fast + steps: + - electron-build: + persist: false + build: false + checkout: true + persist-checkout: true linux-checkout-and-save-cache: <<: *machine-linux-2xlarge @@ -1166,67 +1520,86 @@ jobs: environment: <<: *env-linux-2xlarge GCLIENT_EXTRA_ARGS: '--custom-var=checkout_pyyaml=True' - <<: *steps-checkout-fast + steps: + - electron-build: + persist: false + build: false + checkout: true + persist-checkout: true linux-checkout-for-native-tests-with-no-patches: <<: *machine-linux-2xlarge environment: <<: *env-linux-2xlarge GCLIENT_EXTRA_ARGS: '--custom-var=apply_patches=False --custom-var=checkout_pyyaml=True' - <<: *steps-checkout-fast + steps: + - electron-build: + persist: false + build: false + checkout: true + persist-checkout: true + + mac-checkout: + <<: *machine-linux-2xlarge + environment: + <<: *env-linux-2xlarge + <<: *env-testing-build + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac --custom-var=checkout_boto=True --custom-var=checkout_requests=True' + steps: + - electron-build: + persist: false + build: false + checkout: true + persist-checkout: true + restore-src-cache: false + preserve-vendor-dirs: true mac-checkout-fast: <<: *machine-linux-2xlarge environment: <<: *env-linux-2xlarge + <<: *env-testing-build GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' - <<: *steps-checkout-fast + steps: + - electron-build: + persist: false + build: false + checkout: true + persist-checkout: true mac-checkout-and-save-cache: <<: *machine-linux-2xlarge environment: <<: *env-linux-2xlarge + <<: *env-testing-build GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' <<: *steps-checkout-and-save-cache # Layer 2: Builds. - linux-x64-debug: - <<: *machine-linux-2xlarge - environment: - <<: *env-linux-2xlarge - <<: *env-debug-build - <<: *env-enable-sccache - <<: *env-ninja-status - GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' - <<: *steps-electron-build-with-inline-checkout-for-tests - - linux-x64-debug-gn-check: - <<: *machine-linux-medium - environment: - <<: *env-linux-medium - <<: *env-debug-build - <<: *steps-electron-gn-check - linux-x64-testing: <<: *machine-linux-2xlarge environment: <<: *env-linux-2xlarge <<: *env-testing-build - <<: *env-enable-sccache <<: *env-ninja-status GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' - <<: *steps-electron-build-with-inline-checkout-for-tests + steps: + - electron-build: + persist: true + checkout: true linux-x64-testing-no-run-as-node: <<: *machine-linux-2xlarge environment: <<: *env-linux-2xlarge <<: *env-testing-build - <<: *env-enable-sccache <<: *env-ninja-status <<: *env-disable-run-as-node GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' - <<: *steps-electron-build-with-inline-checkout-for-tests + steps: + - electron-build: + persist: false + checkout: true linux-x64-testing-gn-check: <<: *machine-linux-medium @@ -1240,7 +1613,6 @@ jobs: environment: <<: *env-linux-medium <<: *env-release-build - <<: *env-enable-sccache <<: *env-send-slack-notifications <<: *steps-chromedriver-build @@ -1249,10 +1621,13 @@ jobs: environment: <<: *env-linux-2xlarge-release <<: *env-release-build - <<: *env-enable-sccache <<: *env-send-slack-notifications <<: *env-ninja-status - <<: *steps-electron-build + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' + steps: + - electron-build: + persist: true + checkout: true linux-x64-publish: <<: *machine-linux-2xlarge @@ -1264,27 +1639,18 @@ jobs: UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> <<: *steps-electron-build-for-publish - linux-ia32-debug: - <<: *machine-linux-2xlarge - environment: - <<: *env-linux-2xlarge - <<: *env-ia32 - <<: *env-debug-build - <<: *env-enable-sccache - <<: *env-ninja-status - GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' - <<: *steps-electron-build-with-inline-checkout-for-tests - linux-ia32-testing: <<: *machine-linux-2xlarge environment: <<: *env-linux-2xlarge <<: *env-ia32 <<: *env-testing-build - <<: *env-enable-sccache <<: *env-ninja-status GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' - <<: *steps-electron-build-with-inline-checkout-for-tests + steps: + - electron-build: + persist: true + checkout: true linux-ia32-chromedriver: <<: *machine-linux-medium @@ -1292,7 +1658,6 @@ jobs: <<: *env-linux-medium <<: *env-ia32 <<: *env-release-build - <<: *env-enable-sccache <<: *env-send-slack-notifications <<: *steps-chromedriver-build @@ -1302,10 +1667,13 @@ jobs: <<: *env-linux-2xlarge-release <<: *env-ia32 <<: *env-release-build - <<: *env-enable-sccache <<: *env-send-slack-notifications <<: *env-ninja-status - <<: *steps-electron-build + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' + steps: + - electron-build: + persist: true + checkout: true linux-ia32-publish: <<: *machine-linux-2xlarge @@ -1315,31 +1683,23 @@ jobs: <<: *env-ia32 <<: *env-release-build <<: *env-enable-sccache + <<: *env-32bit-release UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >> <<: *steps-electron-build-for-publish - linux-arm-debug: - <<: *machine-linux-2xlarge - environment: - <<: *env-linux-2xlarge - <<: *env-arm - <<: *env-debug-build - <<: *env-enable-sccache - <<: *env-ninja-status - GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' - <<: *steps-electron-build-with-inline-checkout-for-tests - linux-arm-testing: <<: *machine-linux-2xlarge environment: <<: *env-linux-2xlarge <<: *env-arm <<: *env-testing-build - <<: *env-enable-sccache <<: *env-ninja-status TRIGGER_ARM_TEST: true GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' - <<: *steps-electron-build-with-inline-checkout-for-tests + steps: + - electron-build: + persist: false + checkout: true linux-arm-chromedriver: <<: *machine-linux-medium @@ -1347,7 +1707,6 @@ jobs: <<: *env-linux-medium <<: *env-arm <<: *env-release-build - <<: *env-enable-sccache <<: *env-send-slack-notifications <<: *steps-chromedriver-build @@ -1357,10 +1716,13 @@ jobs: <<: *env-linux-2xlarge-release <<: *env-arm <<: *env-release-build - <<: *env-enable-sccache <<: *env-send-slack-notifications <<: *env-ninja-status - <<: *steps-electron-build + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' + steps: + - electron-build: + persist: false + checkout: true linux-arm-publish: <<: *machine-linux-2xlarge @@ -1369,40 +1731,24 @@ jobs: <<: *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 >> <<: *steps-electron-build-for-publish - linux-arm64-debug: - <<: *machine-linux-2xlarge - environment: - <<: *env-linux-2xlarge - <<: *env-arm64 - <<: *env-debug-build - <<: *env-enable-sccache - <<: *env-ninja-status - GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' - <<: *steps-electron-build-with-inline-checkout-for-tests - - linux-arm64-debug-gn-check: - <<: *machine-linux-medium - environment: - <<: *env-linux-medium - <<: *env-arm64 - <<: *env-debug-build - <<: *steps-electron-gn-check - linux-arm64-testing: <<: *machine-linux-2xlarge environment: <<: *env-linux-2xlarge <<: *env-arm64 <<: *env-testing-build - <<: *env-enable-sccache <<: *env-ninja-status TRIGGER_ARM_TEST: true GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' - <<: *steps-electron-build-with-inline-checkout-for-tests + steps: + - electron-build: + persist: false + checkout: true linux-arm64-testing-gn-check: <<: *machine-linux-medium @@ -1418,7 +1764,6 @@ jobs: <<: *env-linux-medium <<: *env-arm64 <<: *env-release-build - <<: *env-enable-sccache <<: *env-send-slack-notifications <<: *steps-chromedriver-build @@ -1428,10 +1773,13 @@ jobs: <<: *env-linux-2xlarge-release <<: *env-arm64 <<: *env-release-build - <<: *env-enable-sccache <<: *env-send-slack-notifications <<: *env-ninja-status - <<: *steps-electron-build + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' + steps: + - electron-build: + persist: false + checkout: true linux-arm64-publish: <<: *machine-linux-2xlarge @@ -1449,25 +1797,14 @@ jobs: environment: <<: *env-mac-large <<: *env-testing-build - <<: *env-enable-sccache <<: *env-ninja-status - <<: *steps-electron-build - - osx-debug: - <<: *machine-mac-large - environment: - <<: *env-mac-large - <<: *env-debug-build - <<: *env-enable-sccache - <<: *env-ninja-status - <<: *steps-electron-build - - osx-debug-gn-check: - <<: *machine-mac - environment: - <<: *env-machine-mac - <<: *env-debug-build - <<: *steps-electron-gn-check + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' + steps: + - electron-build: + persist: true + checkout: false + checkout-and-assume-cache: true + attach: false osx-testing-gn-check: <<: *machine-mac @@ -1481,7 +1818,6 @@ jobs: environment: <<: *env-machine-mac <<: *env-release-build - <<: *env-enable-sccache <<: *env-send-slack-notifications <<: *steps-chromedriver-build @@ -1490,9 +1826,14 @@ jobs: environment: <<: *env-mac-large <<: *env-release-build - <<: *env-enable-sccache <<: *env-ninja-status - <<: *steps-electron-build + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' + steps: + - electron-build: + persist: true + checkout: false + checkout-and-assume-cache: true + attach: false osx-publish: <<: *machine-mac-large @@ -1510,27 +1851,14 @@ jobs: <<: *env-mac-large <<: *env-mas <<: *env-testing-build - <<: *env-enable-sccache <<: *env-ninja-status - <<: *steps-electron-build - - mas-debug: - <<: *machine-mac-large - environment: - <<: *env-mac-large - <<: *env-mas - <<: *env-debug-build - <<: *env-enable-sccache - <<: *env-ninja-status - <<: *steps-electron-build - - mas-debug-gn-check: - <<: *machine-mac - environment: - <<: *env-machine-mac - <<: *env-mas - <<: *env-debug-build - <<: *steps-electron-gn-check + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' + steps: + - electron-build: + persist: true + checkout: false + checkout-and-assume-cache: true + attach: false mas-testing-gn-check: <<: *machine-mac @@ -1545,7 +1873,6 @@ jobs: environment: <<: *env-machine-mac <<: *env-release-build - <<: *env-enable-sccache <<: *env-send-slack-notifications <<: *steps-chromedriver-build @@ -1555,10 +1882,15 @@ jobs: <<: *env-mac-large <<: *env-mas <<: *env-release-build - <<: *env-enable-sccache <<: *env-ninja-status - <<: *steps-electron-build - + GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' + steps: + - electron-build: + persist: true + checkout: false + checkout-and-assume-cache: true + attach: false + mas-publish: <<: *machine-mac-large environment: @@ -1576,7 +1908,6 @@ jobs: environment: <<: *env-linux-2xlarge <<: *env-unittests - <<: *env-enable-sccache <<: *env-headless-testing <<: *steps-native-tests @@ -1585,7 +1916,6 @@ jobs: environment: <<: *env-linux-2xlarge <<: *env-unittests - <<: *env-enable-sccache <<: *env-headless-testing TESTS_ARGS: '--only-disabled-tests' <<: *steps-native-tests @@ -1595,7 +1925,6 @@ jobs: environment: <<: *env-linux-2xlarge <<: *env-unittests - <<: *env-enable-sccache <<: *env-headless-testing TESTS_ARGS: '--include-disabled-tests' <<: *steps-native-tests @@ -1606,7 +1935,6 @@ jobs: <<: *env-linux-2xlarge <<: *env-browsertests <<: *env-testing-build - <<: *env-enable-sccache <<: *env-headless-testing <<: *steps-native-tests @@ -1874,11 +2202,6 @@ workflows: jobs: - linux-checkout-fast - linux-checkout-and-save-cache - - - linux-x64-debug - - linux-x64-debug-gn-check: - requires: - - linux-checkout-fast - linux-x64-testing - linux-x64-testing-no-run-as-node - linux-x64-testing-gn-check: @@ -1894,7 +2217,6 @@ workflows: requires: - linux-x64-testing - - linux-ia32-debug - linux-ia32-testing - linux-ia32-testing-tests: requires: @@ -1906,17 +2228,13 @@ workflows: requires: - linux-ia32-testing - - linux-arm-debug - linux-arm-testing - - linux-arm64-debug - - linux-arm64-debug-gn-check: - requires: - - linux-checkout-fast - linux-arm64-testing - linux-arm64-testing-gn-check: requires: - linux-checkout-fast + - ts-compile-doc-change build-mac: when: << pipeline.parameters.run-build-mac >> @@ -1926,15 +2244,7 @@ workflows: - osx-testing: requires: - - mac-checkout-fast - - - osx-debug: - requires: - - mac-checkout-fast - - - osx-debug-gn-check: - requires: - - mac-checkout-fast + - mac-checkout-and-save-cache - osx-testing-gn-check: requires: @@ -1946,15 +2256,7 @@ workflows: - mas-testing: requires: - - mac-checkout-fast - - - mas-debug: - requires: - - mac-checkout-fast - - - mas-debug-gn-check: - requires: - - mac-checkout-fast + - mac-checkout-and-save-cache - mas-testing-gn-check: requires: @@ -1975,10 +2277,9 @@ workflows: - *chromium-upgrade-branches jobs: - linux-checkout-fast + - linux-checkout-and-save-cache - - linux-x64-release: - requires: - - linux-checkout-fast + - linux-x64-release - linux-x64-release-tests: requires: - linux-x64-release @@ -1998,9 +2299,7 @@ workflows: - linux-x64-verify-ffmpeg - linux-x64-chromedriver - - linux-ia32-release: - requires: - - linux-checkout-fast + - linux-ia32-release - linux-ia32-release-tests: requires: - linux-ia32-release @@ -2020,9 +2319,7 @@ workflows: - linux-ia32-verify-ffmpeg - linux-ia32-chromedriver - - linux-arm-release: - requires: - - linux-checkout-fast + - linux-arm-release - linux-arm-chromedriver: requires: - linux-checkout-fast @@ -2031,10 +2328,7 @@ workflows: - linux-arm-release - linux-arm-chromedriver - - - linux-arm64-release: - requires: - - linux-checkout-fast + - linux-arm64-release - linux-arm64-chromedriver: requires: - linux-checkout-fast @@ -2054,10 +2348,11 @@ workflows: - *chromium-upgrade-branches jobs: - mac-checkout-fast + - mac-checkout-and-save-cache - osx-release: requires: - - mac-checkout-fast + - mac-checkout-and-save-cache - osx-release-tests: requires: - osx-release @@ -2079,7 +2374,7 @@ workflows: - mas-release: requires: - - mac-checkout-fast + - mac-checkout-and-save-cache - mas-release-tests: requires: - mas-release diff --git a/.eslintrc.json b/.eslintrc.json index 08b7ff0923125..71ad42d578e1c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,9 +6,11 @@ "browser": true }, "rules": { + "semi": ["error", "always"], "no-var": "error", "no-unused-vars": 0, "no-global-assign": 0, + "guard-for-in": 2, "@typescript-eslint/no-unused-vars": ["error", { "vars": "all", "args": "after-used", diff --git a/.gitattributes b/.gitattributes index a8cc2eac7063f..9d6933d53b98a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ # `git apply` and friends don't understand CRLF, even on windows. Force those # files to be checked out with LF endings even if core.autocrlf is true. *.patch text eol=lf +patches/**/.patches merge=union diff --git a/BUILD.gn b/BUILD.gn index c4a3350610b80..926805b4a5a07 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1,6 +1,7 @@ import("//build/config/locales.gni") import("//build/config/ui.gni") import("//build/config/win/manifest.gni") +import("//components/spellcheck/spellcheck_build_features.gni") import("//content/public/app/mac_helpers.gni") import("//pdf/features.gni") import("//printing/buildflags/buildflags.gni") @@ -21,6 +22,7 @@ import("buildflags/buildflags.gni") import("electron_paks.gni") import("filenames.auto.gni") import("filenames.gni") +import("filenames.hunspell.gni") if (is_mac) { import("//build/config/mac/rules.gni") @@ -147,7 +149,7 @@ webpack_build("electron_content_script_bundle") { out_file = "$target_gen_dir/js2c/content_script_bundle.js" } -copy("atom_js2c_copy") { +copy("electron_js2c_copy") { sources = [ "lib/common/asar.js", "lib/common/asar_init.js", @@ -157,12 +159,12 @@ copy("atom_js2c_copy") { ] } -action("atom_js2c") { +action("electron_js2c") { deps = [ - ":atom_js2c_copy", ":electron_browser_bundle", ":electron_content_script_bundle", ":electron_isolated_renderer_bundle", + ":electron_js2c_copy", ":electron_renderer_bundle", ":electron_sandboxed_renderer_bundle", ":electron_worker_bundle", @@ -184,7 +186,7 @@ action("atom_js2c") { inputs = sources + [ "//third_party/electron_node/tools/js2c.py" ] outputs = [ - "$root_gen_dir/atom_natives.cc", + "$root_gen_dir/electron_natives.cc", ] script = "tools/js2c.py" @@ -345,12 +347,13 @@ source_set("electron_lib") { ] deps = [ - ":atom_js2c", + ":electron_js2c", ":electron_version_header", ":manifests", ":resources", "buildflags", "chromium_src:chrome", + "chromium_src:chrome_spellchecker", "native_mate", "shell/common/api:mojo", "//base:base_static", @@ -358,12 +361,13 @@ source_set("electron_lib") { "//chrome/app/resources:platform_locale_settings", "//chrome/services/printing/public/mojom", "//components/certificate_transparency", + "//components/language/core/browser", "//components/net_log", - "//components/network_hints/common", + "//components/network_hints/browser", + "//components/network_hints/common:mojo_bindings", "//components/network_hints/renderer", "//components/network_session_configurator/common", "//components/prefs", - "//components/spellcheck/renderer", "//components/viz/host", "//components/viz/service", "//content/public/browser", @@ -379,12 +383,10 @@ source_set("electron_lib") { "//media/mojo/mojom", "//net:extras", "//net:net_resources", - "//net:net_with_v8", "//ppapi/host", "//ppapi/proxy", "//ppapi/shared_impl", "//printing/buildflags", - "//services/audio/public/mojom:constants", "//services/device/public/cpp/geolocation", "//services/device/public/mojom", "//services/proxy_resolver:lib", @@ -396,7 +398,7 @@ source_set("electron_lib") { "//third_party/electron_node:node_lib", "//third_party/leveldatabase", "//third_party/libyuv", - "//third_party/webrtc_overrides:init_webrtc", + "//third_party/webrtc_overrides:webrtc_component", "//third_party/widevine/cdm:headers", "//ui/base/idle", "//ui/events:dom_keycode_converter", @@ -502,7 +504,7 @@ source_set("electron_lib") { "shell/browser/ui/views/autofill_popup_view.h", ] if (is_mas_build) { - sources += [ "shell/browser/api/atom_api_app_mas.mm" ] + sources += [ "shell/browser/api/electron_api_app_mas.mm" ] sources -= [ "shell/browser/auto_updater_mac.mm", "shell/common/crash_reporter/crash_reporter_mac.h", @@ -546,6 +548,10 @@ source_set("electron_lib") { ] sources += filenames.lib_sources_nss + sources += [ + "shell/browser/ui/gtk_util.cc", + "shell/browser/ui/gtk_util.h", + ] } if (is_win) { libs += [ "dwmapi.lib" ] @@ -621,27 +627,27 @@ source_set("electron_lib") { deps += [ "//third_party/webrtc/modules/desktop_capture" ] } sources += [ - "shell/browser/api/atom_api_desktop_capturer.cc", - "shell/browser/api/atom_api_desktop_capturer.h", + "shell/browser/api/electron_api_desktop_capturer.cc", + "shell/browser/api/electron_api_desktop_capturer.h", ] } if (enable_view_api) { sources += [ - "shell/browser/api/views/atom_api_box_layout.cc", - "shell/browser/api/views/atom_api_box_layout.h", - "shell/browser/api/views/atom_api_button.cc", - "shell/browser/api/views/atom_api_button.h", - "shell/browser/api/views/atom_api_label_button.cc", - "shell/browser/api/views/atom_api_label_button.h", - "shell/browser/api/views/atom_api_layout_manager.cc", - "shell/browser/api/views/atom_api_layout_manager.h", - "shell/browser/api/views/atom_api_md_text_button.cc", - "shell/browser/api/views/atom_api_md_text_button.h", - "shell/browser/api/views/atom_api_resize_area.cc", - "shell/browser/api/views/atom_api_resize_area.h", - "shell/browser/api/views/atom_api_text_field.cc", - "shell/browser/api/views/atom_api_text_field.h", + "shell/browser/api/views/electron_api_box_layout.cc", + "shell/browser/api/views/electron_api_box_layout.h", + "shell/browser/api/views/electron_api_button.cc", + "shell/browser/api/views/electron_api_button.h", + "shell/browser/api/views/electron_api_label_button.cc", + "shell/browser/api/views/electron_api_label_button.h", + "shell/browser/api/views/electron_api_layout_manager.cc", + "shell/browser/api/views/electron_api_layout_manager.h", + "shell/browser/api/views/electron_api_md_text_button.cc", + "shell/browser/api/views/electron_api_md_text_button.h", + "shell/browser/api/views/electron_api_resize_area.cc", + "shell/browser/api/views/electron_api_resize_area.h", + "shell/browser/api/views/electron_api_text_field.cc", + "shell/browser/api/views/electron_api_text_field.h", ] } @@ -705,7 +711,6 @@ if (is_mac) { public_deps += [ "//third_party/icu:icudata" ] } if (v8_use_external_startup_data) { - sources += [ "$root_out_dir/natives_blob.bin" ] public_deps += [ "//v8" ] if (use_v8_context_snapshot) { sources += [ "$root_out_dir/v8_context_snapshot.bin" ] @@ -824,6 +829,7 @@ if (is_mac) { include_dirs = [ "." ] sources = filenames.framework_sources + libs = [] if (enable_osr) { libs += [ "IOSurface.framework" ] @@ -857,7 +863,7 @@ if (is_mac) { } defines = [ "HELPER_EXECUTABLE" ] sources = filenames.app_sources - sources += [ "shell/common/atom_constants.cc" ] + sources += [ "shell/common/electron_constants.cc" ] include_dirs = [ "." ] info_plist = "shell/renderer/resources/mac/Info.plist" extra_substitutions = @@ -978,7 +984,7 @@ if (is_mac) { mac_app_bundle("electron_app") { output_name = electron_product_name sources = filenames.app_sources - sources += [ "shell/common/atom_constants.cc" ] + sources += [ "shell/common/electron_constants.cc" ] include_dirs = [ "." ] deps = [ ":electron_app_framework_bundle_data", @@ -1265,9 +1271,14 @@ template("dist_zip") { "outputs", "testonly", ]) + flatten = false + if (defined(invoker.flatten)) { + flatten = invoker.flatten + } args = rebase_path(outputs + [ _runtime_deps_file ], root_build_dir) + [ target_cpu, target_os, + "$flatten", ] } } @@ -1342,14 +1353,38 @@ dist_zip("electron_chromedriver_zip") { ] } +mksnapshot_deps = [ + ":licenses", + "//tools/v8_context_snapshot:v8_context_snapshot_generator($v8_snapshot_toolchain)", + "//v8:mksnapshot($v8_snapshot_toolchain)", +] + +group("electron_mksnapshot") { + public_deps = mksnapshot_deps +} + dist_zip("electron_mksnapshot_zip") { + data_deps = mksnapshot_deps + outputs = [ + "$root_build_dir/mksnapshot.zip", + ] +} + +copy("hunspell_dictionaries") { + sources = hunspell_dictionaries + hunspell_licenses + outputs = [ + "$target_gen_dir/electron_hunspell/{{source_file_part}}", + ] +} + +dist_zip("hunspell_dictionaries_zip") { data_deps = [ - "//v8:mksnapshot($v8_snapshot_toolchain)", - "//tools/v8_context_snapshot:v8_context_snapshot_generator", - ":licenses", + ":hunspell_dictionaries", ] + flatten = true + outputs = [ - "$root_build_dir/mksnapshot.zip", + "$root_build_dir/hunspell_dictionaries.zip", ] } diff --git a/DEPS b/DEPS index aeb55e2f30e99..d4ee40e280abf 100644 --- a/DEPS +++ b/DEPS @@ -11,7 +11,7 @@ gclient_gn_args = [ vars = { 'chromium_version': - 'c3a0220e7bde049d599a8332b9b2785b0178be74', + '80.0.3987.165', 'node_version': 'v12.13.0', 'nan_version': @@ -114,7 +114,7 @@ hooks = [ 'pattern': 'src/electron/script/update-external-binaries.py', 'condition': 'download_external_binaries', 'action': [ - 'python', + 'python3', 'src/electron/script/update-external-binaries.py', ], }, @@ -152,3 +152,5 @@ hooks = [ recursedeps = [ 'src', ] + +# Touch DEPS to bust cache diff --git a/ELECTRON_VERSION b/ELECTRON_VERSION index c251a7d3a2064..9f4a0fbc18f4d 100644 --- a/ELECTRON_VERSION +++ b/ELECTRON_VERSION @@ -1 +1 @@ -8.0.0-nightly.20191023 \ No newline at end of file +8.3.0 \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 1ab650fd36641..0d499cc0e7b32 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,7 +29,7 @@ version: 1.0.{build} build_cloud: libcc-20 -image: vs2017-15.9-10.0.18362 +image: vs2019-16.3-10.0.18362 environment: GIT_CACHE_PATH: C:\Users\electron\libcc_cache ELECTRON_OUT_DIR: Default @@ -50,6 +50,12 @@ build_script: - ps: >- if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) { Write-warning "Skipping PR build for branch"; Exit-AppveyorBuild + } else { + node script/yarn.js install --frozen-lockfile + + if ($(node script/doc-only-change.js --prNumber=$env:APPVEYOR_PULL_REQUEST_NUMBER --prBranch=$env:APPVEYOR_REPO_BRANCH;$LASTEXITCODE -eq 0)) { + Write-warning "Skipping build for doc only change"; Exit-AppveyorBuild + } } - echo "Building $env:GN_CONFIG build" - git config --global core.longpaths true @@ -70,23 +76,49 @@ build_script: --unmanaged %GCLIENT_EXTRA_ARGS% "https://github.com/electron/electron" - - gclient sync --with_branch_heads --with_tags --reset + - gclient sync --with_branch_heads --with_tags --ignore_locks --break_repo_locks + - ps: >- + if ($env:SAVE_GCLIENT_SRC -eq 'true') { + # archive current source for future use + # only run on x64/woa to avoid contention saving + if ($(7z a $zipfile src -xr!android_webview -xr!electron -xr'!*\.git' -xr!third_party\WebKit\LayoutTests! -xr!third_party\blink\web_tests -xr!third_party\blink\perf_tests -slp -t7z -mmt=30;$LASTEXITCODE -ne 0)) { + Write-warning "Could not save source to shared drive; continuing anyway" + } + } + - ps: >- + if ($env:GN_CONFIG -ne 'release') { + if (Test-Path 'env:RAW_GOMA_AUTH') { + $env:GOMA_OAUTH2_CONFIG_FILE = "$pwd\.goma_oauth2_config" + $env:RAW_GOMA_AUTH | Set-Content $env:GOMA_OAUTH2_CONFIG_FILE + } + git clone https://github.com/electron/build-tools.git + cd build-tools + npm install + mkdir third_party + node -e "require('./src/utils/goma.js').downloadAndPrepare()" + $env:GN_GOMA_FILE = node -e "console.log(require('./src/utils/goma.js').gnFilePath)" + $env:LOCAL_GOMA_DIR = node -e "console.log(require('./src/utils/goma.js').dir)" + cd .. + .\src\electron\script\start-goma.ps1 -gomaDir $env:LOCAL_GOMA_DIR + } - cd src - - ps: $env:BUILD_CONFIG_PATH="//electron/build/args/%GN_CONFIG%.gn" - - gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") %GN_EXTRA_ARGS%" + - set BUILD_CONFIG_PATH=//electron/build/args/%GN_CONFIG%.gn + - if DEFINED GN_GOMA_FILE (gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") import(\"%GN_GOMA_FILE%\") %GN_EXTRA_ARGS% ") else (gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") %GN_EXTRA_ARGS% cc_wrapper=\"%SCCACHE_PATH%\"") - gn check out/Default //electron:electron_lib - gn check out/Default //electron:electron_app - gn check out/Default //electron:manifests - gn check out/Default //electron/shell/common/api:mojo - - ninja -C out/Default electron:electron_app - - if "%GN_CONFIG%"=="testing" ( python C:\Users\electron\depot_tools\post_build_ninja_summary.py -C out\Default ) + - if DEFINED GN_GOMA_FILE (ninja -j 300 -C out/Default electron:electron_app) else (ninja -C out/Default electron:electron_app) + - if "%GN_CONFIG%"=="testing" ( python C:\Users\electron\depot_tools\post_build_ninja_summary.py -C out\Default ) - gn gen out/ffmpeg "--args=import(\"//electron/build/args/ffmpeg.gn\") %GN_EXTRA_ARGS%" - ninja -C out/ffmpeg electron:electron_ffmpeg_zip - ninja -C out/Default electron:electron_dist_zip - ninja -C out/Default shell_browser_ui_unittests - ninja -C out/Default electron:electron_mksnapshot_zip + - ninja -C out/Default electron:hunspell_dictionaries_zip - ninja -C out/Default electron:electron_chromedriver_zip - ninja -C out/Default third_party/electron_node:headers + - if "%GN_CONFIG%"=="testing" ( python %LOCAL_GOMA_DIR%\goma_ctl.py stat ) - appveyor PushArtifact out/Default/dist.zip - appveyor PushArtifact out/Default/shell_browser_ui_unittests.exe - appveyor PushArtifact out/Default/chromedriver.zip @@ -94,6 +126,7 @@ build_script: - 7z a node_headers.zip out\Default\gen\node_headers - appveyor PushArtifact node_headers.zip - appveyor PushArtifact out/Default/mksnapshot.zip + - appveyor PushArtifact out/Default/hunspell_dictionaries.zip - appveyor PushArtifact out/Default/electron.lib - ps: >- if ($env:GN_CONFIG -eq 'release') { diff --git a/azure-pipelines-woa.yml b/azure-pipelines-woa.yml index 4dde4afb0304c..a4dac6e861202 100644 --- a/azure-pipelines-woa.yml +++ b/azure-pipelines-woa.yml @@ -88,5 +88,6 @@ steps: - powershell: | Get-Process | Where Name –Like "electron*" | Stop-Process Get-Process | Where Name –Like "MicrosoftEdge*" | Stop-Process + Get-Process | Where Name –Like "msedge*" | Stop-Process displayName: 'Kill processes left running from last test run' condition: always() \ No newline at end of file diff --git a/build/args/all.gn b/build/args/all.gn index e9dffb46cd203..b89393f51c122 100644 --- a/build/args/all.gn +++ b/build/args/all.gn @@ -1,5 +1,4 @@ is_electron_build = true -use_jumbo_build = true root_extra_deps = [ "//electron" ] # Registry of NMVs --> https://github.com/nodejs/node/blob/master/doc/abi_version_registry.json @@ -9,6 +8,12 @@ v8_promise_internal_field_count = 1 v8_typed_array_max_size_in_heap = 0 v8_embedder_string = "-electron.0" +# TODO: this breaks native modules. See e.g. https://www.github.com/nodejs/node/pull/30463 +# We can probably enable this as long as we make sure node native modules +# also build with the relevant #defines (V8_COMPRESS_POINTERS etc.) +v8_enable_pointer_compression = false +v8_enable_31bit_smis_on_64bit_arch = false + # TODO: this breaks mksnapshot v8_enable_snapshot_native_code_counters = false @@ -18,12 +23,8 @@ ffmpeg_branding = "Chrome" enable_basic_printing = true angle_enable_vulkan_validation_layers = false +dawn_enable_vulkan_validation_layers = false is_cfi = false -# TODO: Remove this and update CI to contain 10.14 SDK once -# crbug.com/986701 is fixed. -mac_sdk_min = "10.13" - -# TODO: disabled due to crashes. re-enable. -enable_osr = false +enable_osr = true diff --git a/build/args/native_tests.gn b/build/args/native_tests.gn index 2559bbb3859db..416b9556cc19d 100644 --- a/build/args/native_tests.gn +++ b/build/args/native_tests.gn @@ -5,4 +5,3 @@ is_debug = false is_component_build = false is_component_ffmpeg = false symbol_level = 1 -use_jumbo_build = true diff --git a/build/rules.gni b/build/rules.gni index aab90eaea40ee..33d6750024097 100644 --- a/build/rules.gni +++ b/build/rules.gni @@ -1,9 +1,50 @@ import("//build/config/mac/mac_sdk.gni") -# This is imported from /ios becuase this functionality was moved -# after Chromium stopped using xib files for macOS menu functionality -# See https://chromium-review.googlesource.com/c/chromium/src/+/1648695 -import("//build/config/ios/rules.gni") +# Template to compile .xib and .storyboard files. +# (copied from src/build/config/ios/rules.gni) +# +# Arguments +# +# sources: +# list of string, sources to compile +# +# ibtool_flags: +# (optional) list of string, additional flags to pass to the ibtool +template("compile_ib_files") { + action_foreach(target_name) { + forward_variables_from(invoker, + [ + "testonly", + "visibility", + ]) + assert(defined(invoker.sources), + "sources must be specified for $target_name") + assert(defined(invoker.output_extension), + "output_extension must be specified for $target_name") + + ibtool_flags = [] + if (defined(invoker.ibtool_flags)) { + ibtool_flags = invoker.ibtool_flags + } + + _output_extension = invoker.output_extension + + script = "//build/config/ios/compile_ib_files.py" + sources = invoker.sources + outputs = [ + "$target_gen_dir/$target_name/{{source_name_part}}.$_output_extension", + ] + args = [ + "--input", + "{{source}}", + "--output", + rebase_path( + "$target_gen_dir/$target_name/{{source_name_part}}.$_output_extension", + root_build_dir), + ] + args += ibtool_flags + } +} # Template is copied here from Chromium but was removed in # https://chromium-review.googlesource.com/c/chromium/src/+/1637981 diff --git a/build/webpack/webpack.config.base.js b/build/webpack/webpack.config.base.js index 2afc874ac1f67..d267344c6757d 100644 --- a/build/webpack/webpack.config.base.js +++ b/build/webpack/webpack.config.base.js @@ -74,7 +74,10 @@ module.exports = ({ global: ['@electron/internal/renderer/webpack-provider', '_global'], Buffer: ['@electron/internal/renderer/webpack-provider', 'Buffer'], }) - ] : []) + ] : []), + new webpack.ProvidePlugin({ + Promise: ['@electron/internal/common/webpack-globals-provider', 'Promise'], + }), ] }) -} \ No newline at end of file +} diff --git a/build/zip.py b/build/zip.py index ff9c888d9143c..0e5c3697317c0 100644 --- a/build/zip.py +++ b/build/zip.py @@ -16,6 +16,10 @@ './libVkICD_mock_', #Skipping because these are outputs that we don't need './VkICD_mock_', #Skipping because these are outputs that we don't need + # Skipping because its an output of create_bundle from //build/config/mac/rules.gni + # that we don't need + 'Electron.dSYM', + # //chrome/browser:resources depends on this via # //chrome/browser/resources/ssl/ssl_error_assistant, but we don't need to # ship it. @@ -46,19 +50,19 @@ def execute(argv): raise e def main(argv): - dist_zip, runtime_deps, target_cpu, target_os = argv + dist_zip, runtime_deps, target_cpu, target_os, flatten_val = argv + should_flatten = flatten_val == "true" dist_files = set() with open(runtime_deps) as f: for dep in f.readlines(): dep = dep.strip() - dist_files.add(dep) - if sys.platform == 'darwin': + if not skip_path(dep, dist_zip, target_cpu): + dist_files.add(dep) + if sys.platform == 'darwin' and not should_flatten: execute(['zip', '-r', '-y', dist_zip] + list(dist_files)) else: with zipfile.ZipFile(dist_zip, 'w', zipfile.ZIP_DEFLATED, allowZip64=True) as z: for dep in dist_files: - if skip_path(dep, dist_zip, target_cpu): - continue if os.path.isdir(dep): for root, dirs, files in os.walk(dep): for file in files: @@ -67,7 +71,7 @@ def main(argv): basename = os.path.basename(dep) dirname = os.path.dirname(dep) arcname = os.path.join(dirname, 'chrome-sandbox') if basename == 'chrome_sandbox' else dep - z.write(dep, arcname) + z.write(dep, os.path.basename(arcname) if should_flatten else arcname) if __name__ == '__main__': sys.exit(main(sys.argv[1:])) diff --git a/buildflags/BUILD.gn b/buildflags/BUILD.gn index 2da174f3e9c62..6209a7de40d8c 100644 --- a/buildflags/BUILD.gn +++ b/buildflags/BUILD.gn @@ -19,6 +19,7 @@ buildflag_header("buildflags") { "ENABLE_TTS=$enable_tts", "ENABLE_COLOR_CHOOSER=$enable_color_chooser", "ENABLE_ELECTRON_EXTENSIONS=$enable_electron_extensions", + "ENABLE_BUILTIN_SPELLCHECKER=$enable_builtin_spellchecker", "ENABLE_PICTURE_IN_PICTURE=$enable_picture_in_picture", "OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider", ] diff --git a/buildflags/buildflags.gni b/buildflags/buildflags.gni index a4d4a487144e1..9bd6fe1ed6d8e 100644 --- a/buildflags/buildflags.gni +++ b/buildflags/buildflags.gni @@ -33,4 +33,7 @@ declare_args() { # Enable Chrome extensions support. enable_electron_extensions = false + + # Enable Spellchecker support + enable_builtin_spellchecker = true } diff --git a/chromium_src/BUILD.gn b/chromium_src/BUILD.gn index e8313cd8df989..4a391514c38dc 100644 --- a/chromium_src/BUILD.gn +++ b/chromium_src/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//build/config/ui.gni") +import("//components/spellcheck/spellcheck_build_features.gni") import("//electron/buildflags/buildflags.gni") import("//printing/buildflags/buildflags.gni") import("//third_party/widevine/cdm/widevine.gni") @@ -31,6 +32,8 @@ static_library("chrome") { "//chrome/browser/icon_loader_win.cc", "//chrome/browser/icon_manager.cc", "//chrome/browser/icon_manager.h", + "//chrome/browser/media/webrtc/system_media_capture_permissions_mac.h", + "//chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm", "//chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc", "//chrome/browser/net/chrome_mojo_proxy_resolver_factory.h", "//chrome/browser/net/proxy_config_monitor.cc", @@ -39,6 +42,8 @@ static_library("chrome") { "//chrome/browser/net/proxy_service_factory.h", "//chrome/browser/predictors/preconnect_manager.cc", "//chrome/browser/predictors/preconnect_manager.h", + "//chrome/browser/predictors/predictors_features.cc", + "//chrome/browser/predictors/predictors_features.h", "//chrome/browser/predictors/proxy_lookup_client_impl.cc", "//chrome/browser/predictors/proxy_lookup_client_impl.h", "//chrome/browser/predictors/resolve_host_client_impl.cc", @@ -225,3 +230,58 @@ static_library("chrome") { ] } } + +# This source set is just so we don't have to depend on all of //chrome/browser +# You may have to add new files here during the upgrade if //chrome/browser/spellchecker +# gets more files +source_set("chrome_spellchecker") { + sources = [] + deps = [] + libs = [] + + if (enable_builtin_spellchecker) { + sources += [ + "//chrome/browser/spellchecker/spell_check_host_chrome_impl.cc", + "//chrome/browser/spellchecker/spell_check_host_chrome_impl.h", + "//chrome/browser/spellchecker/spellcheck_custom_dictionary.cc", + "//chrome/browser/spellchecker/spellcheck_custom_dictionary.h", + "//chrome/browser/spellchecker/spellcheck_factory.cc", + "//chrome/browser/spellchecker/spellcheck_factory.h", + "//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc", + "//chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h", + "//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.cc", + "//chrome/browser/spellchecker/spellcheck_language_blacklist_policy_handler.h", + "//chrome/browser/spellchecker/spellcheck_language_policy_handler.cc", + "//chrome/browser/spellchecker/spellcheck_language_policy_handler.h", + "//chrome/browser/spellchecker/spellcheck_service.cc", + "//chrome/browser/spellchecker/spellcheck_service.h", + ] + + if (has_spellcheck_panel) { + sources += [ + "//chrome/browser/spellchecker/spell_check_panel_host_impl.cc", + "//chrome/browser/spellchecker/spell_check_panel_host_impl.h", + ] + } + + if (use_browser_spellchecker) { + sources += [ + "//chrome/browser/spellchecker/spelling_request.cc", + "//chrome/browser/spellchecker/spelling_request.h", + ] + } + + deps += [ + "//base:base_static", + "//components/language/core/browser", + "//components/spellcheck:buildflags", + "//components/sync", + ] + } + + public_deps = [ + "//components/spellcheck/browser", + "//components/spellcheck/common", + "//components/spellcheck/renderer", + ] +} diff --git a/chromium_src/chrome/browser/certificate_manager_model.cc b/chromium_src/chrome/browser/certificate_manager_model.cc index ff3a45f5cade0..4766578bc8468 100644 --- a/chromium_src/chrome/browser/certificate_manager_model.cc +++ b/chromium_src/chrome/browser/certificate_manager_model.cc @@ -37,10 +37,8 @@ net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext( // public and private slot. // Redirect any slot usage to this persistent slot on Linux. g_nss_cert_database = new net::NSSCertDatabase( - crypto::ScopedPK11Slot( - crypto::GetPersistentNSSKeySlot()) /* public slot */, - crypto::ScopedPK11Slot( - crypto::GetPersistentNSSKeySlot()) /* private slot */); + crypto::ScopedPK11Slot(PK11_GetInternalKeySlot()) /* public slot */, + crypto::ScopedPK11Slot(PK11_GetInternalKeySlot()) /* private slot */); } return g_nss_cert_database; } diff --git a/chromium_src/chrome/browser/process_singleton_posix.cc b/chromium_src/chrome/browser/process_singleton_posix.cc index 9df2c7a4e6cb0..c2181827552c4 100644 --- a/chromium_src/chrome/browser/process_singleton_posix.cc +++ b/chromium_src/chrome/browser/process_singleton_posix.cc @@ -56,7 +56,7 @@ #include #include "shell/browser/browser.h" -#include "shell/common/atom_command_line.h" +#include "shell/common/electron_command_line.h" #include "base/base_paths.h" #include "base/bind.h" @@ -826,7 +826,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout( return PROCESS_NONE; to_send.append(current_dir.value()); - const std::vector& argv = electron::AtomCommandLine::argv(); + const std::vector& argv = electron::ElectronCommandLine::argv(); for (std::vector::const_iterator it = argv.begin(); it != argv.end(); ++it) { to_send.push_back(kTokenDelimiter); diff --git a/default_app/default_app.ts b/default_app/default_app.ts index d517aeb351256..494b38ce20b05 100644 --- a/default_app/default_app.ts +++ b/default_app/default_app.ts @@ -1,48 +1,50 @@ -import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron' -import * as path from 'path' +import { app, dialog, BrowserWindow, shell, ipcMain } from 'electron'; +import * as path from 'path'; -let mainWindow: BrowserWindow | null = null +let mainWindow: BrowserWindow | null = null; + +app.allowRendererProcessReuse = true; // Quit when all windows are closed. app.on('window-all-closed', () => { - app.quit() -}) + app.quit(); +}); function decorateURL (url: string) { // safely add `?utm_source=default_app - const parsedUrl = new URL(url) - parsedUrl.searchParams.append('utm_source', 'default_app') - return parsedUrl.toString() + const parsedUrl = new URL(url); + parsedUrl.searchParams.append('utm_source', 'default_app'); + return parsedUrl.toString(); } // Find the shortest path to the electron binary -const absoluteElectronPath = process.execPath -const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath) +const absoluteElectronPath = process.execPath; +const relativeElectronPath = path.relative(process.cwd(), absoluteElectronPath); const electronPath = absoluteElectronPath.length < relativeElectronPath.length ? absoluteElectronPath - : relativeElectronPath + : relativeElectronPath; -const indexPath = path.resolve(app.getAppPath(), 'index.html') +const indexPath = path.resolve(app.getAppPath(), 'index.html'); function isTrustedSender (webContents: Electron.WebContents) { if (webContents !== (mainWindow && mainWindow.webContents)) { - return false + return false; } - const parsedUrl = new URL(webContents.getURL()) + const parsedUrl = new URL(webContents.getURL()); const urlPath = process.platform === 'win32' // Strip the prefixed "/" that occurs on windows ? path.resolve(parsedUrl.pathname.substr(1)) - : parsedUrl.pathname - return parsedUrl.protocol === 'file:' && urlPath === indexPath + : parsedUrl.pathname; + return parsedUrl.protocol === 'file:' && urlPath === indexPath; } ipcMain.handle('bootstrap', (event) => { - return isTrustedSender(event.sender) ? electronPath : null -}) + return isTrustedSender(event.sender) ? electronPath : null; +}); async function createWindow () { - await app.whenReady() + await app.whenReady(); const options: Electron.BrowserWindowConstructorOptions = { width: 900, @@ -57,46 +59,46 @@ async function createWindow () { }, useContentSize: true, show: false - } + }; if (process.platform === 'linux') { - options.icon = path.join(__dirname, 'icon.png') + options.icon = path.join(__dirname, 'icon.png'); } - mainWindow = new BrowserWindow(options) - mainWindow.on('ready-to-show', () => mainWindow!.show()) + mainWindow = new BrowserWindow(options); + mainWindow.on('ready-to-show', () => mainWindow!.show()); mainWindow.webContents.on('new-window', (event, url) => { - event.preventDefault() - shell.openExternal(decorateURL(url)) - }) + event.preventDefault(); + shell.openExternal(decorateURL(url)); + }); mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, done) => { - const parsedUrl = new URL(webContents.getURL()) + const parsedUrl = new URL(webContents.getURL()); const options: Electron.MessageBoxOptions = { title: 'Permission Request', message: `Allow '${parsedUrl.origin}' to access '${permission}'?`, buttons: ['OK', 'Cancel'], cancelId: 1 - } + }; dialog.showMessageBox(mainWindow!, options).then(({ response }) => { - done(response === 0) - }) - }) + done(response === 0); + }); + }); - return mainWindow + return mainWindow; } export const loadURL = async (appUrl: string) => { - mainWindow = await createWindow() - mainWindow.loadURL(appUrl) - mainWindow.focus() -} + mainWindow = await createWindow(); + mainWindow.loadURL(appUrl); + mainWindow.focus(); +}; export const loadFile = async (appPath: string) => { - mainWindow = await createWindow() - mainWindow.loadFile(appPath) - mainWindow.focus() -} + mainWindow = await createWindow(); + mainWindow.loadFile(appPath); + mainWindow.focus(); +}; diff --git a/default_app/main.ts b/default_app/main.ts index fba9562165fe9..aa652595d33a2 100644 --- a/default_app/main.ts +++ b/default_app/main.ts @@ -1,8 +1,8 @@ -import { app, dialog } from 'electron' +import { app, dialog } from 'electron'; -import * as fs from 'fs' -import * as path from 'path' -import * as url from 'url' +import * as fs from 'fs'; +import * as path from 'path'; +import * as url from 'url'; type DefaultAppOptions = { file: null | string; @@ -14,10 +14,10 @@ type DefaultAppOptions = { modules: string[]; } -const Module = require('module') +const Module = require('module'); // Parse command line options. -const argv = process.argv.slice(1) +const argv = process.argv.slice(1); const option: DefaultAppOptions = { file: null, @@ -27,50 +27,50 @@ const option: DefaultAppOptions = { interactive: false, abi: false, modules: [] -} +}; -let nextArgIsRequire = false +let nextArgIsRequire = false; for (const arg of argv) { if (nextArgIsRequire) { - option.modules.push(arg) - nextArgIsRequire = false - continue + option.modules.push(arg); + nextArgIsRequire = false; + continue; } else if (arg === '--version' || arg === '-v') { - option.version = true - break + option.version = true; + break; } else if (arg.match(/^--app=/)) { - option.file = arg.split('=')[1] - break + option.file = arg.split('=')[1]; + break; } else if (arg === '--interactive' || arg === '-i' || arg === '-repl') { - option.interactive = true + option.interactive = true; } else if (arg === '--test-type=webdriver') { - option.webdriver = true + option.webdriver = true; } else if (arg === '--require' || arg === '-r') { - nextArgIsRequire = true - continue + nextArgIsRequire = true; + continue; } else if (arg === '--abi' || arg === '-a') { - option.abi = true - continue + option.abi = true; + continue; } else if (arg === '--no-help') { - option.noHelp = true - continue + option.noHelp = true; + continue; } else if (arg[0] === '-') { - continue + continue; } else { - option.file = arg - break + option.file = arg; + break; } } if (nextArgIsRequire) { - console.error('Invalid Usage: --require [file]\n\n"file" is required') - process.exit(1) + console.error('Invalid Usage: --require [file]\n\n"file" is required'); + process.exit(1); } // Set up preload modules if (option.modules.length > 0) { - Module._preloadModules(option.modules) + Module._preloadModules(option.modules); } function loadApplicationPackage (packagePath: string) { @@ -79,102 +79,102 @@ function loadApplicationPackage (packagePath: string) { configurable: false, enumerable: true, value: true - }) + }); try { // Override app name and version. - packagePath = path.resolve(packagePath) - const packageJsonPath = path.join(packagePath, 'package.json') - let appPath + packagePath = path.resolve(packagePath); + const packageJsonPath = path.join(packagePath, 'package.json'); + let appPath; if (fs.existsSync(packageJsonPath)) { - let packageJson + let packageJson; try { - packageJson = require(packageJsonPath) + packageJson = require(packageJsonPath); } catch (e) { - showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`) - return + showErrorMessage(`Unable to parse ${packageJsonPath}\n\n${e.message}`); + return; } if (packageJson.version) { - app.setVersion(packageJson.version) + app.setVersion(packageJson.version); } if (packageJson.productName) { - app.name = packageJson.productName + app.name = packageJson.productName; } else if (packageJson.name) { - app.name = packageJson.name + app.name = packageJson.name; } - appPath = packagePath + appPath = packagePath; } try { - const filePath = Module._resolveFilename(packagePath, module, true) - app._setDefaultAppPaths(appPath || path.dirname(filePath)) + const filePath = Module._resolveFilename(packagePath, module, true); + app._setDefaultAppPaths(appPath || path.dirname(filePath)); } catch (e) { - showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`) - return + showErrorMessage(`Unable to find Electron app at ${packagePath}\n\n${e.message}`); + return; } // Run the app. - Module._load(packagePath, module, true) + Module._load(packagePath, module, true); } catch (e) { - console.error('App threw an error during load') - console.error(e.stack || e) - throw e + console.error('App threw an error during load'); + console.error(e.stack || e); + throw e; } } function showErrorMessage (message: string) { - app.focus() - dialog.showErrorBox('Error launching app', message) - process.exit(1) + app.focus(); + dialog.showErrorBox('Error launching app', message); + process.exit(1); } async function loadApplicationByURL (appUrl: string) { - const { loadURL } = await import('./default_app') - loadURL(appUrl) + const { loadURL } = await import('./default_app'); + loadURL(appUrl); } async function loadApplicationByFile (appPath: string) { - const { loadFile } = await import('./default_app') - loadFile(appPath) + const { loadFile } = await import('./default_app'); + loadFile(appPath); } function startRepl () { if (process.platform === 'win32') { - console.error('Electron REPL not currently supported on Windows') - process.exit(1) + console.error('Electron REPL not currently supported on Windows'); + process.exit(1); } // prevent quitting - app.on('window-all-closed', () => {}) + app.on('window-all-closed', () => {}); - const repl = require('repl') + const repl = require('repl'); repl.start('> ').on('exit', () => { - process.exit(0) - }) + process.exit(0); + }); } // Start the specified app if there is one specified in command line, otherwise // start the default app. if (option.file && !option.webdriver) { - const file = option.file - const protocol = url.parse(file).protocol - const extension = path.extname(file) + const file = option.file; + const protocol = url.parse(file).protocol; + const extension = path.extname(file); if (protocol === 'http:' || protocol === 'https:' || protocol === 'file:' || protocol === 'chrome:') { - loadApplicationByURL(file) + loadApplicationByURL(file); } else if (extension === '.html' || extension === '.htm') { - loadApplicationByFile(path.resolve(file)) + loadApplicationByFile(path.resolve(file)); } else { - loadApplicationPackage(file) + loadApplicationPackage(file); } } else if (option.version) { - console.log('v' + process.versions.electron) - process.exit(0) + console.log('v' + process.versions.electron); + process.exit(0); } else if (option.abi) { - console.log(process.versions.modules) - process.exit(0) + console.log(process.versions.modules); + process.exit(0); } else if (option.interactive) { - startRepl() + startRepl(); } else { if (!option.noHelp) { const welcomeMessage = ` @@ -192,10 +192,10 @@ Options: -i, --interactive Open a REPL to the main process. -r, --require Module to preload (option can be repeated). -v, --version Print the version. - -a, --abi Print the Node ABI version.` + -a, --abi Print the Node ABI version.`; - console.log(welcomeMessage) + console.log(welcomeMessage); } - loadApplicationByFile('index.html') + loadApplicationByFile('index.html'); } diff --git a/default_app/preload.ts b/default_app/preload.ts index 855aa26ca3ba9..b18130a1d0f73 100644 --- a/default_app/preload.ts +++ b/default_app/preload.ts @@ -1,53 +1,53 @@ -import { ipcRenderer, contextBridge } from 'electron' +import { ipcRenderer, contextBridge } from 'electron'; async function getOcticonSvg (name: string) { try { - const response = await fetch(`octicon/${name}.svg`) - const div = document.createElement('div') - div.innerHTML = await response.text() - return div + const response = await fetch(`octicon/${name}.svg`); + const div = document.createElement('div'); + div.innerHTML = await response.text(); + return div; } catch { - return null + return null; } } async function loadSVG (element: HTMLSpanElement) { for (const cssClass of element.classList) { if (cssClass.startsWith('octicon-')) { - const icon = await getOcticonSvg(cssClass.substr(8)) + const icon = await getOcticonSvg(cssClass.substr(8)); if (icon) { for (const elemClass of element.classList) { - icon.classList.add(elemClass) + icon.classList.add(elemClass); } - element.before(icon) - element.remove() - break + element.before(icon); + element.remove(); + break; } } } } async function initialize () { - const electronPath = await ipcRenderer.invoke('bootstrap') + const electronPath = await ipcRenderer.invoke('bootstrap'); function replaceText (selector: string, text: string) { - const element = document.querySelector(selector) + const element = document.querySelector(selector); if (element) { - element.innerText = text + element.innerText = text; } } - replaceText('.electron-version', `Electron v${process.versions.electron}`) - replaceText('.chrome-version', `Chromium v${process.versions.chrome}`) - replaceText('.node-version', `Node v${process.versions.node}`) - replaceText('.v8-version', `v8 v${process.versions.v8}`) - replaceText('.command-example', `${electronPath} path-to-app`) + replaceText('.electron-version', `Electron v${process.versions.electron}`); + replaceText('.chrome-version', `Chromium v${process.versions.chrome}`); + replaceText('.node-version', `Node v${process.versions.node}`); + replaceText('.v8-version', `v8 v${process.versions.v8}`); + replaceText('.command-example', `${electronPath} path-to-app`); for (const element of document.querySelectorAll('.octicon')) { - loadSVG(element) + loadSVG(element); } } contextBridge.exposeInMainWorld('electronDefaultApp', { initialize -}) +}); diff --git a/docs/api/app.md b/docs/api/app.md index 7252b5b355704..d3f9e3da126d9 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -314,10 +314,8 @@ Returns: * `event` Event * `webContents` [WebContents](web-contents.md) -* `request` Object - * `method` String +* `authenticationResponseDetails` Object * `url` URL - * `referrer` URL * `authInfo` Object * `isProxy` Boolean * `scheme` String @@ -325,8 +323,8 @@ Returns: * `port` Integer * `realm` String * `callback` Function - * `username` String - * `password` String + * `username` String (optional) + * `password` String (optional) Emitted when `webContents` wants to do basic auth. @@ -337,12 +335,16 @@ should prevent the default behavior with `event.preventDefault()` and call ```javascript const { app } = require('electron') -app.on('login', (event, webContents, request, authInfo, callback) => { +app.on('login', (event, webContents, details, authInfo, callback) => { event.preventDefault() callback('username', 'secret') }) ``` +If `callback` is called without a username or password, the authentication +request will be cancelled and the authentication error will be returned to the +page. + ### Event: 'gpu-info-update' Emitted whenever there is a GPU info update. @@ -562,11 +564,17 @@ Returns `Promise` - fulfilled when Electron is initialized. May be used as a convenient alternative to checking `app.isReady()` and subscribing to the `ready` event if the app is not ready yet. -### `app.focus()` +### `app.focus([options])` + +* `options` Object (optional) + * `steal` Boolean _macOS_ - Make the receiver the active app even if another app is + currently active. On Linux, focuses on the first visible window. On macOS, makes the application the active app. On Windows, focuses on the application's first window. +You should seek to use the `steal` option as sparingly as possible. + ### `app.hide()` _macOS_ Hides all application windows without minimizing them. @@ -711,34 +719,34 @@ Clears the recent documents list. ### `app.setAsDefaultProtocolClient(protocol[, path, args])` -* `protocol` String - The name of your protocol, without `://`. If you want your - app to handle `electron://` links, call this method with `electron` as the - parameter. -* `path` String (optional) _Windows_ - Defaults to `process.execPath` -* `args` String[] (optional) _Windows_ - Defaults to an empty array +* `protocol` String - The name of your protocol, without `://`. For example, + if you want your app to handle `electron://` links, call this method with + `electron` as the parameter. +* `path` String (optional) _Windows_ - The path to the Electron executable. + Defaults to `process.execPath` +* `args` String[] (optional) _Windows_ - Arguments passed to the executable. + Defaults to an empty array Returns `Boolean` - Whether the call succeeded. -This method sets the current executable as the default handler for a protocol -(aka URI scheme). It allows you to integrate your app deeper into the operating -system. Once registered, all links with `your-protocol://` will be opened with -the current executable. The whole link, including protocol, will be passed to -your application as a parameter. - -On Windows, you can provide optional parameters path, the path to your executable, -and args, an array of arguments to be passed to your executable when it launches. +Sets the current executable as the default handler for a protocol (aka URI +scheme). It allows you to integrate your app deeper into the operating system. +Once registered, all links with `your-protocol://` will be opened with the +current executable. The whole link, including protocol, will be passed to your +application as a parameter. **Note:** On macOS, you can only register protocols that have been added to -your app's `info.plist`, which can not be modified at runtime. You can however -change the file with a simple text editor or script during build time. -Please refer to [Apple's documentation][CFBundleURLTypes] for details. +your app's `info.plist`, which cannot be modified at runtime. However, you can +change the file during build time via [Electron Forge][electron-forge], +[Electron Packager][electron-packager], or by editing `info.plist` with a text +editor. Please refer to [Apple's documentation][CFBundleURLTypes] for details. **Note:** In a Windows Store environment (when packaged as an `appx`) this API will return `true` for all calls but the registry key it sets won't be accessible by other applications. In order to register your Windows Store application as a default protocol handler you must [declare the protocol in your manifest](https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap-protocol). -The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally. +The API uses the Windows Registry and `LSSetDefaultHandlerForURLScheme` internally. ### `app.removeAsDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_ @@ -757,10 +765,8 @@ protocol (aka URI scheme). If so, it will remove the app as the default handler. * `path` String (optional) _Windows_ - Defaults to `process.execPath` * `args` String[] (optional) _Windows_ - Defaults to an empty array -Returns `Boolean` - -This method checks if the current executable is the default handler for a protocol -(aka URI scheme). If so, it will return true. Otherwise, it will return false. +Returns `Boolean` - Whether the current executable is the default handler for a +protocol (aka URI scheme). **Note:** On macOS, you can use this method to check if the app has been registered as the default protocol handler for a protocol. You can also verify @@ -768,7 +774,22 @@ this by checking `~/Library/Preferences/com.apple.LaunchServices.plist` on the macOS machine. Please refer to [Apple's documentation][LSCopyDefaultHandlerForURLScheme] for details. -The API uses the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. +The API uses the Windows Registry and `LSCopyDefaultHandlerForURLScheme` internally. + +### `app.getApplicationNameForProtocol(url)` + +* `url` String - a URL with the protocol name to check. Unlike the other + methods in this family, this accepts an entire URL, including `://` at a + minimum (e.g. `https://`). + +Returns `String` - Name of the application handling the protocol, or an empty + string if there is no handler. For instance, if Electron is the default + handler of the URL, this could be `Electron` on Windows and Mac. However, + don't rely on the precise format which is not guaranteed to remain unchanged. + Expect a different format on Linux, possibly with a `.desktop` suffix. + +This method returns the application name of the default handler for the protocol +(aka URI scheme) of a URL. ### `app.setUserTasks(tasks)` _Windows_ @@ -1186,8 +1207,9 @@ Show the app's about panel options. These options can be overridden with `app.se * `website` String (optional) _Linux_ - The app's website. * `iconPath` String (optional) _Linux_ _Windows_ - Path to the app's icon. On Linux, will be shown as 64x64 pixels while retaining aspect ratio. -Set the about panel options. This will override the values defined in the app's -`.plist` file on MacOS. See the [Apple docs][about-panel-options] for more details. On Linux, values must be set in order to be shown; there are no defaults. +Set the about panel options. This will override the values defined in the app's `.plist` file on MacOS. See the [Apple docs][about-panel-options] for more details. On Linux, values must be set in order to be shown; there are no defaults. + +If you do not set `credits` but still wish to surface them in your app, AppKit will look for a file named "Credits.html", "Credits.rtf", and "Credits.rtfd", in that order, in the bundle returned by the NSBundle class method main. The first file found is used, and if none is found, the info area is left blank. See Apple [documentation](https://developer.apple.com/documentation/appkit/nsaboutpaneloptioncredits?language=objc) for more information. ### `app.isEmojiPanelSupported()` @@ -1308,6 +1330,8 @@ A `Boolean` property that returns `true` if the app is packaged, `false` otherw [dock-menu]:https://developer.apple.com/macos/human-interface-guidelines/menus/dock-menus/ [tasks]:https://msdn.microsoft.com/en-us/library/windows/desktop/dd378460(v=vs.85).aspx#tasks [app-user-model-id]: https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx +[electron-forge]: https://www.electronforge.io/ +[electron-packager]: https://github.com/electron/electron-packager [CFBundleURLTypes]: https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115 [LSCopyDefaultHandlerForURLScheme]: https://developer.apple.com/library/mac/documentation/Carbon/Reference/LaunchServicesReference/#//apple_ref/c/func/LSCopyDefaultHandlerForURLScheme [handoff]: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Handoff/HandoffFundamentals/HandoffFundamentals.html diff --git a/docs/api/breaking-changes.md b/docs/api/breaking-changes.md index 8e945438548d8..0f8ad6bc0456e 100644 --- a/docs/api/breaking-changes.md +++ b/docs/api/breaking-changes.md @@ -59,6 +59,55 @@ these kinds of objects will throw a 'could not be cloned' error. [SCA]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm +### `.getWebContents()` + +This API is implemented using the `remote` module, which has both performance +and security implications. Therefore its usage should be explicit. + +```js +// Deprecated +webview.getWebContents() +// Replace with +const { remote } = require('electron') +remote.webContents.fromId(webview.getWebContentsId()) +``` + +However, it is recommended to avoid using the `remote` module altogether. + +```js +// main +const { ipcMain, webContents } = require('electron') + +const getGuestForWebContents = function (webContentsId, contents) { + const guest = webContents.fromId(webContentsId) + if (!guest) { + throw new Error(`Invalid webContentsId: ${webContentsId}`) + } + if (guest.hostWebContents !== contents) { + throw new Error(`Access denied to webContents`) + } + return guest +} + +ipcMain.handle('openDevTools', (event, webContentsId) => { + const guest = getGuestForWebContents(webContentsId, event.sender) + guest.openDevTools() +}) + +// renderer +const { ipcRenderer } = require('electron') + +ipcRenderer.invoke('openDevTools', webview.getWebContentsId()) +``` + +### `webFrame.setLayoutZoomLevelLimits()` + +Chromium has removed support for changing the layout zoom level limits, and it +is beyond Electron's capacity to maintain it. The function will emit a warning +in Electron 8.x, and cease to exist in Electron 9.x. The layout zoom level +limits are now fixed at a minimum of 0.25 and a maximum of 5.0, as defined +[here](https://chromium.googlesource.com/chromium/src/+/938b37a6d2886bf8335fc7db792f1eb46c65b2ae/third_party/blink/common/page/page_zoom.cc#11). + ## Planned Breaking API Changes (7.0) ### Node Headers URL @@ -103,7 +152,7 @@ const idleTime = getSystemIdleTime() ### webFrame Isolated World APIs ```js -// Removed in Elecron 7.0 +// Removed in Electron 7.0 webFrame.setIsolatedWorldContentSecurityPolicy(worldId, csp) webFrame.setIsolatedWorldHumanReadableName(worldId, name) webFrame.setIsolatedWorldSecurityOrigin(worldId, securityOrigin) @@ -121,6 +170,40 @@ webFrame.setIsolatedWorldInfo( This property was removed in Chromium 77, and as such is no longer available. +### `webkitdirectory` attribute for `` + +The `webkitdirectory` property on HTML file inputs allows them to select folders. +Previous versions of Electron had an incorrect implementation where the `event.target.files` +of the input returned a `FileList` that returned one `File` corresponding to the selected folder. + +As of Electron 7, that `FileList` is now list of all files contained within +the folder, similarly to Chrome, Firefox, and Edge +([link to MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/webkitdirectory)). + +As an illustration, take a folder with this structure: +```console +folder +├── file1 +├── file2 +└── file3 +``` + +In Electron <=6, this would return a `FileList` with a `File` object for: +```console +path/to/folder +``` + +In Electron 7, this now returns a `FileList` with a `File` object for: +```console +/path/to/folder/file3 +/path/to/folder/file2 +/path/to/folder/file1 +``` + +Note that `webkitdirectory` no longer exposes the path to the selected folder. +If you require the path to the selected folder rather than the folder contents, +see the `dialog.showOpenDialog` API ([link](https://github.com/electron/electron/blob/master/docs/api/dialog.md#dialogshowopendialogbrowserwindow-options)). + ## Planned Breaking API Changes (6.0) ### `win.setMenu(null)` diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 928eff76009dc..f63da37d16c1e 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -229,6 +229,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. unless hovered over in the top left of the window. These custom buttons prevent issues with mouse events that occur with the standard window toolbar buttons. **Note:** This option is currently experimental. + * `trafficLightPosition` [Point](structures/point.md) (optional) - Set a custom position for the traffic light buttons. Can only be used with `titleBarStyle` set to `hidden` * `fullscreenWindowTitle` Boolean (optional) - Shows the title in the title bar in full screen mode on macOS for all `titleBarStyle` options. Default is `false`. @@ -272,8 +273,6 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. OS-level sandbox and disabling the Node.js engine. This is not the same as the `nodeIntegration` option and the APIs available to the preload script are more limited. Read more about the option [here](sandbox-option.md). - **Note:** This option is currently experimental and may change or be - removed in future Electron releases. * `enableRemoteModule` Boolean (optional) - Whether to enable the [`remote`](remote.md) module. Default is `true`. * `session` [Session](session.md#class-session) (optional) - Sets the session used by the @@ -373,6 +372,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. consecutive dialog protection is triggered. If not defined the default message would be used, note that currently the default message is in English and not localized. + * `disableDialogs` Boolean (optional) - Whether to disable dialogs + completely. Overrides `safeDialogs`. Default is `false`. * `navigateOnDragDrop` Boolean (optional) - Whether dragging and dropping a file or link onto the page causes a navigation. Default is `false`. * `autoplayPolicy` String (optional) - Autoplay policy to apply to @@ -385,6 +386,10 @@ It creates a new `BrowserWindow` with native properties as set by the `options`. * `accessibleTitle` String (optional) - An alternative title string provided only to accessibility tools such as screen readers. This string is not directly visible to users. + * `spellcheck` Boolean (optional) - Whether to enable the builtin spellchecker. + Default is `false`. + * `enableWebSQL` Boolean (optional) - Whether to enable the [WebSQL api](https://www.w3.org/TR/webdatabase/). + Default is `true`. When setting minimum or maximum window size with `minWidth`/`maxWidth`/ `minHeight`/`maxHeight`, it only constrains the users. It won't prevent you from @@ -515,7 +520,7 @@ Emitted when the window is restored from a minimized state. Returns: * `event` Event -* `newBounds` [`Rectangle`](structures/rectangle.md) - Size the window is being resized to. +* `newBounds` [Rectangle](structures/rectangle.md) - Size the window is being resized to. Emitted before the window is resized. Calling `event.preventDefault()` will prevent the window from being resized. @@ -530,7 +535,7 @@ Emitted after the window has been resized. Returns: * `event` Event -* `newBounds` [`Rectangle`](structures/rectangle.md) - Location the window is being moved to. +* `newBounds` [Rectangle](structures/rectangle.md) - Location the window is being moved to. Emitted before the window is moved. On Windows, calling `event.preventDefault()` will prevent the window from being moved. @@ -623,6 +628,12 @@ Returns: Emitted on 3-finger swipe. Possible directions are `up`, `right`, `down`, `left`. +The method underlying this event is built to handle older macOS-style trackpad swiping, +where the content on the screen doesn't move with the swipe. Most macOS trackpads are not +configured to allow this kind of swiping anymore, so in order for it to emit properly the +'Swipe between pages' preference in `System Preferences > Trackpad > More Gestures` must be +set to 'Swipe with two or three fingers'. + #### Event: 'rotate-gesture' _macOS_ Returns: @@ -1579,7 +1590,7 @@ Same as `webContents.showDefinitionForSelection()`. #### `win.setIcon(icon)` _Windows_ _Linux_ -* `icon` [NativeImage](native-image.md) +* `icon` [NativeImage](native-image.md) | String Changes window icon. @@ -1625,7 +1636,7 @@ Returns `Boolean` - Whether the menu bar is visible. * `visible` Boolean * `options` Object (optional) * `visibleOnFullScreen` Boolean (optional) _macOS_ - Sets whether - the window should be visible above fullscreen windows + the window should be visible above fullscreen windows _deprecated_ Sets whether the window should be visible on all workspaces. @@ -1733,6 +1744,17 @@ will remove the vibrancy effect on the window. Note that `appearance-based`, `light`, `dark`, `medium-light`, and `ultra-dark` have been deprecated and will be removed in an upcoming version of macOS. +#### `win.setTrafficLightPosition(position)` _macOS_ + +* `position` [Point](structures/point.md) + +Set a custom position for the traffic light buttons. Can only be used with `titleBarStyle` set to `hidden`. + +#### `win.getTrafficLightPosition()` _macOS_ + +Returns `Point` - The current position for the traffic light buttons. Can only be used with `titleBarStyle` +set to `hidden`. + #### `win.setTouchBar(touchBar)` _macOS_ _Experimental_ * `touchBar` TouchBar | null diff --git a/docs/api/client-request.md b/docs/api/client-request.md index 382fd04d3817c..fa260696e47bf 100644 --- a/docs/api/client-request.md +++ b/docs/api/client-request.md @@ -22,6 +22,9 @@ which the request is associated. with which the request is associated. Defaults to the empty string. The `session` option prevails on `partition`. Thus if a `session` is explicitly specified, `partition` is ignored. + * `useSessionCookies` Boolean (optional) - Whether to send cookies with this + request from the provided session. This will make the `net` request's + cookie behavior match a `fetch` request. Default is `false`. * `protocol` String (optional) - The protocol scheme in the form 'scheme:'. Currently supported values are 'http:' or 'https:'. Defaults to 'http:'. * `host` String (optional) - The server host provided as a concatenation of @@ -32,8 +35,8 @@ the hostname and the port number 'hostname:port'. * `redirect` String (optional) - The redirect mode for this request. Should be one of `follow`, `error` or `manual`. Defaults to `follow`. When mode is `error`, any redirection will be aborted. When mode is `manual` the redirection will be -deferred until [`request.followRedirect`](#requestfollowredirect) is invoked. Listen for the [`redirect`](#event-redirect) event in -this mode to get more details about the redirect request. +cancelled unless [`request.followRedirect`](#requestfollowredirect) is invoked +synchronously during the [`redirect`](#event-redirect) event. `options` properties such as `protocol`, `host`, `hostname`, `port` and `path` strictly follow the Node.js model as described in the @@ -70,8 +73,8 @@ Returns: * `port` Integer * `realm` String * `callback` Function - * `username` String - * `password` String + * `username` String (optional) + * `password` String (optional) Emitted when an authenticating proxy is asking for user credentials. @@ -136,8 +139,11 @@ Returns: * `redirectUrl` String * `responseHeaders` Record -Emitted when there is redirection and the mode is `manual`. Calling -[`request.followRedirect`](#requestfollowredirect) will continue with the redirection. +Emitted when the server returns a redirect response (e.g. 301 Moved +Permanently). Calling [`request.followRedirect`](#requestfollowredirect) will +continue with the redirection. If this event is handled, +[`request.followRedirect`](#requestfollowredirect) must be called +**synchronously**, otherwise the request will be cancelled. ### Instance Properties @@ -214,7 +220,8 @@ response object,it will emit the `aborted` event. #### `request.followRedirect()` -Continues any deferred redirection request when the redirection mode is `manual`. +Continues any pending redirection. Can only be called during a `'redirect'` +event. #### `request.getUploadProgress()` diff --git a/docs/api/context-bridge.md b/docs/api/context-bridge.md index a9932e059d536..cd5eb578c107c 100644 --- a/docs/api/context-bridge.md +++ b/docs/api/context-bridge.md @@ -28,13 +28,14 @@ window.electron.doThing() ### Main World -The "Main World" is the javascript context that your main renderer code runs in. By default the page you load in your renderer -executes code in this world. +The "Main World" is the JavaScript context that your main renderer code runs in. By default, the +page you load in your renderer executes code in this world. ### Isolated World -When `contextIsolation` is enabled in your `webPreferences` your `preload` scripts run in an "Isolated World". You can read more about -context isolation and what it affects in the [BrowserWindow](browser-window.md) docs. +When `contextIsolation` is enabled in your `webPreferences`, your `preload` scripts run in an +"Isolated World". You can read more about context isolation and what it affects in the +[security](../tutorial/security.md#3-enable-context-isolation-for-remote-content) docs. ## Methods @@ -50,12 +51,12 @@ The `contextBridge` module has the following methods: ### API Objects The `api` object provided to [`exposeInMainWorld`](#contextbridgeexposeinmainworldapikey-api-experimental) must be an object -whose keys are strings and values are a `Function`, `String`, `Number`, `Array`, `Boolean` or another nested object that meets the same conditions. +whose keys are strings and values are a `Function`, `String`, `Number`, `Array`, `Boolean`, or another nested object that meets the same conditions. -`Function` values are proxied to the other context and all other values are **copied** and **frozen**. I.e. Any data / primitives sent in +`Function` values are proxied to the other context and all other values are **copied** and **frozen**. Any data / primitives sent in the API object become immutable and updates on either side of the bridge do not result in an update on the other side. -An example of a complex API object is shown below. +An example of a complex API object is shown below: ```javascript const { contextBridge } = require('electron') @@ -90,22 +91,22 @@ results in some key limitations that we've outlined below. #### Parameter / Error / Return Type support -Because parameters, errors and return values are **copied** when they are sent over the bridge there are only certain types that can be used. -At a high level if the type you want to use can be serialized and un-serialized into the same object it will work. A table of type support -has been included below for completeness. +Because parameters, errors and return values are **copied** when they are sent over the bridge, there are only certain types that can be used. +At a high level, if the type you want to use can be serialized and deserialized into the same object it will work. A table of type support +has been included below for completeness: | Type | Complexity | Parameter Support | Return Value Support | Limitations | | ---- | ---------- | ----------------- | -------------------- | ----------- | | `String` | Simple | ✅ | ✅ | N/A | | `Number` | Simple | ✅ | ✅ | N/A | | `Boolean` | Simple | ✅ | ✅ | N/A | -| `Object` | Complex | ✅ | ✅ | Keys must be supported "Simple" types in this table. Values must be supported in this table. Prototype modifications are dropped. Sending custom classes will copy values but not the prototype. | +| `Object` | Complex | ✅ | ✅ | Keys must be supported using only "Simple" types in this table. Values must be supported in this table. Prototype modifications are dropped. Sending custom classes will copy values but not the prototype. | | `Array` | Complex | ✅ | ✅ | Same limitations as the `Object` type | | `Error` | Complex | ✅ | ✅ | Errors that are thrown are also copied, this can result in the message and stack trace of the error changing slightly due to being thrown in a different context | -| `Promise` | Complex | ✅ | ✅ | Promises are only proxied if they are a the return value or exact parameter. Promises nested in arrays or obejcts will be dropped. | +| `Promise` | Complex | ✅ | ✅ | Promises are only proxied if they are the return value or exact parameter. Promises nested in arrays or objects will be dropped. | | `Function` | Complex | ✅ | ✅ | Prototype modifications are dropped. Sending classes or constructors will not work. | | [Cloneable Types](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) | Simple | ✅ | ✅ | See the linked document on cloneable types | | `Symbol` | N/A | ❌ | ❌ | Symbols cannot be copied across contexts so they are dropped | -If the type you care about is not in the above table it is probably not supported. +If the type you care about is not in the above table, it is probably not supported. diff --git a/docs/api/desktop-capturer.md b/docs/api/desktop-capturer.md index f8e12d04d0788..bf376ce46a9a0 100644 --- a/docs/api/desktop-capturer.md +++ b/docs/api/desktop-capturer.md @@ -91,7 +91,11 @@ The `desktopCapturer` module has the following methods: Returns `Promise` - Resolves with an array of [`DesktopCapturerSource`](structures/desktop-capturer-source.md) objects, each `DesktopCapturerSource` represents a screen or an individual window that can be captured. +**Note** Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher, +which can detected by [`systemPreferences.getMediaAccessStatus`]. + [`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia +[`systemPreferences.getMediaAccessStatus`]: system-preferences.md#systempreferencesgetmediaaccessstatusmediatype-macos ## Caveats diff --git a/docs/api/dialog.md b/docs/api/dialog.md index db76ff0b48671..c3903ce73af6b 100644 --- a/docs/api/dialog.md +++ b/docs/api/dialog.md @@ -120,7 +120,7 @@ Returns `Promise` - Resolve with an object containing the following: * `canceled` Boolean - whether or not the dialog was canceled. * `filePaths` String[] - An array of file paths chosen by the user. If the dialog is cancelled this will be an empty array. -* `bookmarks` String[] (optional) _macOS_ _mas_ - An array matching the `filePaths` array of base64 encoded strings which contains security scoped bookmark data. `securityScopedBookmarks` must be enabled for this to be populated. +* `bookmarks` String[] (optional) _macOS_ _mas_ - An array matching the `filePaths` array of base64 encoded strings which contains security scoped bookmark data. `securityScopedBookmarks` must be enabled for this to be populated. (For return values, see [table here](#bookmarks-array).) The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal. @@ -215,7 +215,7 @@ The `filters` specifies an array of file types that can be displayed, see Returns `Promise` - Resolve with an object containing the following: * `canceled` Boolean - whether or not the dialog was canceled. * `filePath` String (optional) - If the dialog is canceled, this will be `undefined`. - * `bookmark` String (optional) _macOS_ _mas_ - Base64 encoded string which contains the security scoped bookmark data for the saved file. `securityScopedBookmarks` must be enabled for this to be present. + * `bookmark` String (optional) _macOS_ _mas_ - Base64 encoded string which contains the security scoped bookmark data for the saved file. `securityScopedBookmarks` must be enabled for this to be present. (For return values, see [table here](#bookmarks-array).) The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal. @@ -269,6 +269,7 @@ Shows a message box, it will block the process until the message box is closed. It returns the index of the clicked button. The `browserWindow` argument allows the dialog to attach itself to a parent window, making it modal. +If `browserWindow` is not shown dialog will not be attached to it. In such case It will be displayed as independed window. ### `dialog.showMessageBox([browserWindow, ]options)` @@ -350,6 +351,17 @@ On Windows the options are more limited, due to the Win32 APIs used: * The `browserWindow` argument is ignored since it is not possible to make this confirmation dialog modal. +## Bookmarks array + +`showOpenDialog`, `showOpenDialogSync`, `showSaveDialog`, and `showSaveDialogSync` will return a `bookmarks` array. + +| Build Type | securityScopedBookmarks boolean | Return Type | Return Value | +|------------|---------------------------------|:-----------:|--------------------------------| +| macOS mas | True | Success | `['LONGBOOKMARKSTRING']` | +| macOS mas | True | Error | `['']` (array of empty string) | +| macOS mas | False | NA | `[]` (empty array) | +| non mas | any | NA | `[]` (empty array) | + ## Sheets On macOS, dialogs are presented as sheets attached to a window if you provide diff --git a/docs/api/environment-variables.md b/docs/api/environment-variables.md index d31d769614f94..16ee4c197ea81 100644 --- a/docs/api/environment-variables.md +++ b/docs/api/environment-variables.md @@ -44,7 +44,12 @@ Unsupported options are: --use-openssl-ca ``` -`NODE_OPTIONS` are explicitly disallowed in packaged apps. +`NODE_OPTIONS` are explicitly disallowed in packaged apps, except for the following: + +```sh +--max-http-header-size +--http-parser +``` ### `GOOGLE_API_KEY` @@ -123,3 +128,14 @@ the one downloaded by `npm install`. Usage: ```sh export ELECTRON_OVERRIDE_DIST_PATH=/Users/username/projects/electron/out/Debug ``` + +## Set By Electron + +Electron sets some variables in your environment at runtime. + +### `ORIGINAL_XDG_CURRENT_DESKTOP` + +This variable is set to the value of `XDG_CURRENT_DESKTOP` that your application +originally launched with. Electron sometimes modifies the value of `XDG_CURRENT_DESKTOP` +to affect other logic within Chromium so if you want access to the _original_ value +you should look up this environment variable instead. diff --git a/docs/api/ipc-main.md b/docs/api/ipc-main.md index 595a9a37c9bfb..ea020c8866588 100644 --- a/docs/api/ipc-main.md +++ b/docs/api/ipc-main.md @@ -77,6 +77,7 @@ only the next time a message is sent to `channel`, after which it is removed. * `channel` String * `listener` Function + * `...args` any[] Removes the specified `listener` from the listener array for the specified `channel`. diff --git a/docs/api/native-image.md b/docs/api/native-image.md index a3524397b16f9..edd466f56fe01 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -329,9 +329,9 @@ can be called on empty images. [buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer -## Properties +### Instance Properties -### `nativeImage.isMacTemplateImage` _macOS_ +#### `nativeImage.isMacTemplateImage` _macOS_ A `Boolean` property that determines whether the image is considered a [template image](https://developer.apple.com/documentation/appkit/nsimage/1520017-template). diff --git a/docs/api/process.md b/docs/api/process.md index 92418975462cf..87e34cba02f4a 100644 --- a/docs/api/process.md +++ b/docs/api/process.md @@ -217,11 +217,15 @@ that all statistics are reported in Kilobytes. Returns `String` - The version of the host operating system. -Examples: - -* `macOS` -> `10.13.6` -* `Windows` -> `10.0.17763` -* `Linux` -> `4.15.0-45-generic` +Example: + +```js +const version = process.getSystemVersion() +console.log(version) +// On macOS -> '10.13.6' +// On Windows -> '10.0.17763' +// On Linux -> '4.15.0-45-generic' +``` **Note:** It returns the actual operating system version instead of kernel version on macOS unlike `os.release()`. diff --git a/docs/api/protocol.md b/docs/api/protocol.md index f33ab3c55dfd2..5da297c3c24ad 100644 --- a/docs/api/protocol.md +++ b/docs/api/protocol.md @@ -389,9 +389,7 @@ which sends a `Buffer` as a response. * `url` String * `method` String (optional) * `session` Session | null (optional) - * `uploadData` Object (optional) - * `contentType` String - MIME type of the content. - * `data` String - Content to be sent. + * `uploadData` [ProtocolResponseUploadData](structures/protocol-response-upload-data.md) (optional) * `completion` Function (optional) * `error` Error diff --git a/docs/api/session.md b/docs/api/session.md index 7eec76e226a52..134c2e67b5a14 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -105,6 +105,45 @@ Returns: Emitted when a render process requests preconnection to a URL, generally due to a [resource hint](https://w3c.github.io/resource-hints/). +#### Event: 'spellcheck-dictionary-initialized' + +Returns: + +* `event` Event +* `languageCode` String - The language code of the dictionary file + +Emitted when a hunspell dictionary file has been successfully initialized. This +occurs after the file has been downloaded. + +#### Event: 'spellcheck-dictionary-download-begin' + +Returns: + +* `event` Event +* `languageCode` String - The language code of the dictionary file + +Emitted when a hunspell dictionary file starts downloading + +#### Event: 'spellcheck-dictionary-download-success' + +Returns: + +* `event` Event +* `languageCode` String - The language code of the dictionary file + +Emitted when a hunspell dictionary file has been successfully downloaded + +#### Event: 'spellcheck-dictionary-download-failure' + +Returns: + +* `event` Event +* `languageCode` String - The language code of the dictionary file + +Emitted when a hunspell dictionary file download fails. For details +on the failure you should collect a netlog and inspect the download +request. + ### Instance Methods The following methods are available on instances of `Session`: @@ -456,10 +495,57 @@ this session just before normal `preload` scripts run. Returns `String[]` an array of paths to preload scripts that have been registered. +#### `ses.setSpellCheckerLanguages(languages)` + +* `languages` String[] - An array of language codes to enable the spellchecker for. + +The built in spellchecker does not automatically detect what language a user is typing in. In order for the +spell checker to correctly check their words you must call this API with an array of language codes. You can +get the list of supported language codes with the `ses.availableSpellCheckerLanguages` property. + +**Note:** On macOS the OS spellchecker is used and will detect your language automatically. This API is a no-op on macOS. + +#### `ses.getSpellCheckerLanguages()` + +Returns `String[]` - An array of language codes the spellchecker is enabled for. If this list is empty the spellchecker +will fallback to using `en-US`. By default on launch if this setting is an empty list Electron will try to populate this +setting with the current OS locale. This setting is persisted across restarts. + +**Note:** On macOS the OS spellchecker is used and has it's own list of languages. This API is a no-op on macOS. + +#### `ses.setSpellCheckerDictionaryDownloadURL(url)` + +* `url` String - A base URL for Electron to download hunspell dictionaries from. + +By default Electron will download hunspell dictionaries from the Chromium CDN. If you want to override this +behavior you can use this API to point the dictionary downloader at your own hosted version of the hunspell +dictionaries. We publish a `hunspell_dictionaries.zip` file with each release which contains the files you need +to host here, the file server must be **case insensitive** you must upload each file twice, once with the case it +has in the ZIP file and once with the filename as all lower case. + +If the files present in `hunspell_dictionaries.zip` are available at `https://example.com/dictionaries/language-code.bdic` +then you should call this api with `ses.setSpellCheckerDictionaryDownloadURL('https://example.com/dictionaries/')`. Please +note the trailing slash. The URL to the dictionaries is formed as `${url}${filename}`. + +**Note:** On macOS the OS spellchecker is used and therefore we do not download any dictionary files. This API is a no-op on macOS. + +#### `ses.addWordToSpellCheckerDictionary(word)` + +* `word` String - The word you want to add to the dictionary + +Returns `Boolean` - Whether the word was successfully written to the custom dictionary. + +**Note:** On macOS and Windows 10 this word will be written to the OS custom dictionary as well + ### Instance Properties The following properties are available on instances of `Session`: +#### `ses.availableSpellCheckerLanguages` _Readonly_ + +A `String[]` array which consists of all the known available spell checker languages. Providing a language +code to the `setSpellCheckerLanaguages` API that isn't in this array will result in an error. + #### `ses.cookies` _Readonly_ A [`Cookies`](cookies.md) object for this session. diff --git a/docs/api/structures/new-window-event.md b/docs/api/structures/new-window-event.md new file mode 100644 index 0000000000000..b3939a00785a1 --- /dev/null +++ b/docs/api/structures/new-window-event.md @@ -0,0 +1,4 @@ +# NewWindowEvent Object extends `Event` + +* `newGuest` BrowserWindow (optional) + diff --git a/docs/api/structures/protocol-response-upload-data.md b/docs/api/structures/protocol-response-upload-data.md index bcb70da071ed4..225d18bae0bbf 100644 --- a/docs/api/structures/protocol-response-upload-data.md +++ b/docs/api/structures/protocol-response-upload-data.md @@ -1,4 +1,4 @@ # ProtocolResponseUploadData Object * `contentType` String - MIME type of the content. -* `data` String - Content to be sent. +* `data` String | Buffer - Content to be sent. diff --git a/docs/api/system-preferences.md b/docs/api/system-preferences.md index 38672a4512818..9816acdadc100 100644 --- a/docs/api/system-preferences.md +++ b/docs/api/system-preferences.md @@ -369,14 +369,6 @@ Returns `String` - Can be `dark`, `light` or `unknown`. Gets the macOS appearance setting that is currently applied to your application, maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc) -Please note that until Electron is built targeting the 10.14 SDK, your application's -`effectiveAppearance` will default to 'light' and won't inherit the OS preference. In -the interim in order for your application to inherit the OS preference you must set the -`NSRequiresAquaSystemAppearance` key in your apps `Info.plist` to `false`. If you are -using `electron-packager` or `electron-forge` just set the `enableDarwinDarkMode` -packager option to `true`. See the [Electron Packager API](https://github.com/electron/electron-packager/blob/master/docs/api.md#darwindarkmodesupport) -for more details. - **[Deprecated](modernization/property-updates.md)** ### `systemPreferences.getAppLevelAppearance()` _macOS_ _Deprecated_ @@ -404,8 +396,6 @@ Returns `Boolean` - whether or not this device has the ability to use Touch ID. **NOTE:** This API will return `false` on macOS systems older than Sierra 10.12.2. -**[Deprecated](modernization/property-updates.md)** - ### `systemPreferences.promptTouchID(reason)` _macOS_ * `reason` String - The reason you are asking for Touch ID authentication @@ -434,11 +424,13 @@ Returns `Boolean` - `true` if the current process is a trusted accessibility cli ### `systemPreferences.getMediaAccessStatus(mediaType)` _macOS_ -* `mediaType` String - `microphone` or `camera`. +* `mediaType` String - Can be `microphone`, `camera` or `screen`. Returns `String` - Can be `not-determined`, `granted`, `denied`, `restricted` or `unknown`. -This user consent was not required until macOS 10.14 Mojave, so this method will always return `granted` if your system is running 10.13 High Sierra or lower. +This user consent was not required on macOS 10.13 High Sierra or lower so this method will always return `granted`. +macOS 10.14 Mojave or higher requires consent for `microphone` and `camera` access. +macOS 10.15 Catalina or higher requires consent for `screen` access. ### `systemPreferences.askForMediaAccess(mediaType)` _macOS_ @@ -478,11 +470,3 @@ A `String` property that can be `dark`, `light` or `unknown`. Returns the macOS appearance setting that is currently applied to your application, maps to [NSApplication.effectiveAppearance](https://developer.apple.com/documentation/appkit/nsapplication/2967171-effectiveappearance?language=objc) - -Please note that until Electron is built targeting the 10.14 SDK, your application's -`effectiveAppearance` will default to 'light' and won't inherit the OS preference. In -the interim in order for your application to inherit the OS preference you must set the -`NSRequiresAquaSystemAppearance` key in your apps `Info.plist` to `false`. If you are -using `electron-packager` or `electron-forge` just set the `enableDarwinDarkMode` -packager option to `true`. See the [Electron Packager API](https://github.com/electron/electron-packager/blob/master/docs/api.md#darwindarkmodesupport) -for more details. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index ec335b061f73c..d85b81e6e4607 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -138,7 +138,7 @@ Emitted when page receives favicon urls. Returns: -* `event` Event +* `event` NewWindowEvent * `url` String * `frameName` String * `disposition` String - Can be `default`, `foreground-tab`, `background-tab`, @@ -454,10 +454,8 @@ The usage is the same with [the `select-client-certificate` event of Returns: * `event` Event -* `request` Object - * `method` String +* `authenticationResponseDetails` Object * `url` URL - * `referrer` URL * `authInfo` Object * `isProxy` Boolean * `scheme` String @@ -465,8 +463,8 @@ Returns: * `port` Integer * `realm` String * `callback` Function - * `username` String - * `password` String + * `username` String (optional) + * `password` String (optional) Emitted when `webContents` wants to do basic auth. @@ -570,6 +568,9 @@ Returns: * `titleText` String - Title or alt text of the selection that the context was invoked on. * `misspelledWord` String - The misspelled word under the cursor, if any. + * `dictionarySuggestions` String[] - An array of suggested words to show the + user to replace the `misspelledWord`. Only available if there is a misspelled + word and spellchecker is enabled. * `frameCharset` String - The character encoding of the frame on which the menu was invoked. * `inputFieldType` String - If the context menu was invoked on an input @@ -1041,6 +1042,17 @@ contents.executeJavaScript('fetch("https://jsonplaceholder.typicode.com/users/1" }) ``` +#### `contents.executeJavaScriptInIsolatedWorld(worldId, scripts[, userGesture])` + +* `worldId` Integer - The ID of the world to run the javascript in, `0` is the default world, `999` is the world used by Electron's `contextIsolation` feature. You can provide any integer here. +* `scripts` [WebSource[]](structures/web-source.md) +* `userGesture` Boolean (optional) - Default is `false`. + +Returns `Promise` - A promise that resolves with the result of the executed code +or is rejected if the result of the code is a rejected promise. + +Works like `executeJavaScript` but evaluates `scripts` in an isolated context. + #### `contents.setIgnoreMenuShortcuts(ignore)` _Experimental_ * `ignore` Boolean @@ -1067,11 +1079,13 @@ Returns `Boolean` - Whether audio is currently playing. #### `contents.setZoomFactor(factor)` -* `factor` Number - Zoom factor. +* `factor` Double - Zoom factor; default is 1.0. Changes the zoom factor to the specified factor. Zoom factor is zoom percent divided by 100, so 300% = 3.0. +The factor must be greater than 0.0. + **[Deprecated](modernization/property-updates.md)** #### `contents.getZoomFactor()` @@ -1112,7 +1126,7 @@ Sets the maximum and minimum pinch-to-zoom level. > contents.setVisualZoomLevelLimits(1, 3) > ``` -#### `contents.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` +#### `contents.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` _Deprecated_ * `minimumLevel` Number * `maximumLevel` Number @@ -1121,6 +1135,8 @@ Returns `Promise` Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. +**Deprecated:** This API is no longer supported by Chromium. + #### `contents.undo()` Executes the editing command `undo` in web page. @@ -1233,11 +1249,34 @@ Returns `Promise` - Resolves with a [NativeImage](native-image.md) Captures a snapshot of the page within `rect`. Omitting `rect` will capture the whole visible page. +#### `contents.isBeingCaptured()` + +Returns `Boolean` - Whether this page is being captured. It returns true when the capturer count +is large then 0. + +#### `contents.incrementCapturerCount([size, stayHidden])` + +* `size` [Size](structures/size.md) (optional) - The perferred size for the capturer. +* `stayHidden` Boolean (optional) - Keep the page hidden instead of visible. + +Increase the capturer count by one. The page is considered visible when its browser window is +hidden and the capturer count is non-zero. If you would like the page to stay hidden, you should ensure that `stayHidden` is set to true. + +This also affects the Page Visibility API. + +#### `contents.decrementCapturerCount([stayHidden])` + +* `stayHidden` Boolean (optional) - Keep the page in hidden state instead of visible. + +Decrease the capturer count by one. The page will be set to hidden or occluded state when its +browser window is hidden or occluded and the capturer count reaches zero. If you want to +decrease the hidden capturer count instead you should set `stayHidden` to true. + #### `contents.getPrinters()` Get the system printer list. -Returns [`PrinterInfo[]`](structures/printer-info.md). +Returns [`PrinterInfo[]`](structures/printer-info.md) #### `contents.print([options], [callback])` @@ -1245,7 +1284,7 @@ Returns [`PrinterInfo[]`](structures/printer-info.md). * `silent` Boolean (optional) - Don't ask user for print settings. Default is `false`. * `printBackground` Boolean (optional) - Prints the background color and image of the web page. Default is `false`. - * `deviceName` String (optional) - Set the printer device name to use. Default is `''`. + * `deviceName` String (optional) - Set the printer device name to use. Must be the system-defined name and not the 'friendly' name, e.g 'Brother_QL_820NWB' and not 'Brother QL-820NWB'. * `color` Boolean (optional) - Set whether the printed web page will be in color or grayscale. Default is `true`. * `margins` Object (optional) * `marginType` String (optional) - Can be `default`, `none`, `printableArea`, or `custom`. If `custom` is chosen, you will also need to specify `top`, `bottom`, `left`, and `right`. @@ -1267,7 +1306,7 @@ Returns [`PrinterInfo[]`](structures/printer-info.md). * `footer` String (optional) - String to be printed as page footer. * `callback` Function (optional) * `success` Boolean - Indicates success of the print call. - * `failureReason` String - Called back if the print fails; can be `cancelled` or `failed`. + * `failureReason` String - Error description called back if the print fails. Prints window's web page. When `silent` is set to `true`, Electron will pick the system's default printer if `deviceName` is empty and the default settings for printing. @@ -1326,12 +1365,13 @@ win.loadURL('http://github.com') win.webContents.on('did-finish-load', () => { // Use default printing options - win.webContents.printToPDF({}, (error, data) => { - if (error) throw error + win.webContents.printToPDF({}).then(data => { fs.writeFile('/tmp/print.pdf', data, (error) => { if (error) throw error console.log('Write PDF successfully.') }) + }).catch(error => { + console.log(error) }) }) ``` @@ -1786,7 +1826,7 @@ A [`WebContents`](web-contents.md) instance that might own this `WebContents`. #### `contents.devToolsWebContents` _Readonly_ -A `WebContents` of DevTools for this `WebContents`. +A `WebContents | null` property that represents the of DevTools `WebContents` associated with a given `WebContents`. **Note:** Users should never store this object because it may become `null` when the DevTools has been closed. diff --git a/docs/api/web-frame.md b/docs/api/web-frame.md index 712aca43965f5..61c39d802cc49 100644 --- a/docs/api/web-frame.md +++ b/docs/api/web-frame.md @@ -22,11 +22,13 @@ The `WebFrame` class has the following instance methods: ### `webFrame.setZoomFactor(factor)` -* `factor` Number - Zoom factor. +* `factor` Double - Zoom factor; default is 1.0. Changes the zoom factor to the specified factor. Zoom factor is zoom percent divided by 100, so 300% = 3.0. +The factor must be greater than 0.0. + ### `webFrame.getZoomFactor()` Returns `Number` - The current zoom factor. @@ -56,13 +58,15 @@ Sets the maximum and minimum pinch-to-zoom level. > webFrame.setVisualZoomLevelLimits(1, 3) > ``` -### `webFrame.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` +### `webFrame.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` _Deprecated_ * `minimumLevel` Number * `maximumLevel` Number Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. +**Deprecated:** This API is no longer supported by Chromium. + ### `webFrame.setSpellCheckProvider(language, provider)` * `language` String @@ -74,6 +78,17 @@ Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. Sets a provider for spell checking in input fields and text areas. +If you want to use this method you must disable the builtin spellchecker when you +construct the window. + +```js +const mainWindow = new BrowserWindow({ + webPreferences: { + spellcheck: false + } +}) +``` + The `provider` must be an object that has a `spellCheck` method that accepts an array of individual words for spellchecking. The `spellCheck` function runs asynchronously and calls the `callback` function diff --git a/docs/api/web-request.md b/docs/api/web-request.md index 3fb98efb67ace..1370f0b47a5a6 100644 --- a/docs/api/web-request.md +++ b/docs/api/web-request.md @@ -146,7 +146,8 @@ response are visible by the time this listener is fired. * `timestamp` Double * `statusLine` String * `statusCode` Integer - * `responseHeaders` Record (optional) + * `requestHeaders` Record + * `responseHeaders` Record (optional) * `callback` Function * `headersReceivedResponse` Object * `cancel` Boolean (optional) @@ -175,7 +176,7 @@ The `callback` has to be called with a `response` object. * `resourceType` String * `referrer` String * `timestamp` Double - * `responseHeaders` Record (optional) + * `responseHeaders` Record (optional) * `fromCache` Boolean - Indicates whether the response was fetched from disk cache. * `statusCode` Integer @@ -205,7 +206,7 @@ and response headers are available. * `ip` String (optional) - The server IP address that the request was actually sent to. * `fromCache` Boolean - * `responseHeaders` Record (optional) + * `responseHeaders` Record (optional) The `listener` will be called with `listener(details)` when a server initiated redirect is about to occur. @@ -224,10 +225,11 @@ redirect is about to occur. * `resourceType` String * `referrer` String * `timestamp` Double - * `responseHeaders` Record (optional) + * `responseHeaders` Record (optional) * `fromCache` Boolean * `statusCode` Integer * `statusLine` String + * `error` String The `listener` will be called with `listener(details)` when a request is completed. diff --git a/docs/api/webview-tag.md b/docs/api/webview-tag.md index ed78287199aba..c0fe3bc33183b 100644 --- a/docs/api/webview-tag.md +++ b/docs/api/webview-tag.md @@ -635,7 +635,7 @@ Returns `Promise` Sets the maximum and minimum pinch-to-zoom level. -### `.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` +### `.setLayoutZoomLevelLimits(minimumLevel, maximumLevel)` _Deprecated_ * `minimumLevel` Number * `maximumLevel` Number @@ -644,11 +644,13 @@ Returns `Promise` Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. +**Deprecated:** This API is no longer supported by Chromium. + ### `.showDefinitionForSelection()` _macOS_ Shows pop-up dictionary that searches the selected word on the page. -### `.getWebContents()` +### `.getWebContents()` _Deprecated_ Returns [`WebContents`](web-contents.md) - The web contents associated with this `webview`. diff --git a/docs/tutorial/installation.md b/docs/tutorial/installation.md index 18b71d0564221..08498adf10176 100644 --- a/docs/tutorial/installation.md +++ b/docs/tutorial/installation.md @@ -38,11 +38,15 @@ npm install --platform=win32 electron ## Proxies -If you need to use an HTTP proxy you can [set these environment variables][proxy-env]. +If you need to use an HTTP proxy, you need to set the `ELECTRON_GET_USE_PROXY` variable to any +value, plus additional environment variables depending on your host system's Node version: + +* [Node 10 and above][proxy-env-10] +* [Before Node 10][proxy-env] ## Custom Mirrors and Caches During installation, the `electron` module will call out to -[`electron-download`][electron-download] to download prebuilt binaries of +[`@electron/get`][electron-get] to download prebuilt binaries of Electron for your platform. It will do so by contacting GitHub's release download page (`https://github.com/electron/electron/releases/tag/v$VERSION`, where `$VERSION` is the exact version of Electron). @@ -52,7 +56,7 @@ can do so by either providing a mirror or an existing cache directory. #### Mirror You can use environment variables to override the base URL, the path at which to -look for Electron binaries, and the binary filename. The url used by `electron-download` +look for Electron binaries, and the binary filename. The url used by `@electron/get` is composed as follows: ```plaintext @@ -62,11 +66,11 @@ url = ELECTRON_MIRROR + ELECTRON_CUSTOM_DIR + '/' + ELECTRON_CUSTOM_FILENAME For instance, to use the China mirror: ```plaintext -ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/" +ELECTRON_MIRROR="https://cdn.npm.taobao.org/dist/electron/" ``` #### Cache -Alternatively, you can override the local cache. `electron-download` will cache +Alternatively, you can override the local cache. `@electron/get` will cache downloaded binaries in a local directory to not stress your network. You can use that cache folder to provide custom builds of Electron or to avoid making contact with the network at all. @@ -85,16 +89,26 @@ The cache contains the version's official zip file as well as a checksum, stored a text file. A typical cache might look like this: ```sh -├── electron-v1.7.9-darwin-x64.zip -├── electron-v1.8.1-darwin-x64.zip -├── electron-v1.8.2-beta.1-darwin-x64.zip -├── electron-v1.8.2-beta.2-darwin-x64.zip -├── electron-v1.8.2-beta.3-darwin-x64.zip -├── SHASUMS256.txt-1.7.9 -├── SHASUMS256.txt-1.8.1 -├── SHASUMS256.txt-1.8.2-beta.1 -├── SHASUMS256.txt-1.8.2-beta.2 -├── SHASUMS256.txt-1.8.2-beta.3 +├── httpsgithub.comelectronelectronreleasesdownloadv1.7.9electron-v1.7.9-darwin-x64.zip +│ └── electron-v1.7.9-darwin-x64.zip +├── httpsgithub.comelectronelectronreleasesdownloadv1.7.9SHASUMS256.txt +│ └── SHASUMS256.txt +├── httpsgithub.comelectronelectronreleasesdownloadv1.8.1electron-v1.8.1-darwin-x64.zip +│ └── electron-v1.8.1-darwin-x64.zip +├── httpsgithub.comelectronelectronreleasesdownloadv1.8.1SHASUMS256.txt +│ └── SHASUMS256.txt +├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.1electron-v1.8.2-beta.1-darwin-x64.zip +│ └── electron-v1.8.2-beta.1-darwin-x64.zip +├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.1SHASUMS256.txt +│ └── SHASUMS256.txt +├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.2electron-v1.8.2-beta.2-darwin-x64.zip +│ └── electron-v1.8.2-beta.2-darwin-x64.zip +├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.2SHASUMS256.txt +│ └── SHASUMS256.txt +├── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.3electron-v1.8.2-beta.3-darwin-x64.zip +│ └── electron-v1.8.2-beta.3-darwin-x64.zip +└── httpsgithub.comelectronelectronreleasesdownloadv1.8.2-beta.3SHASUMS256.txt + └── SHASUMS256.txt ``` ## Skip binary download @@ -146,7 +160,8 @@ If you need to force a re-download of the asset and the SHASUM file set the [npm]: https://docs.npmjs.com [versioning]: ./electron-versioning.md [releases]: https://github.com/electron/electron/releases -[proxy-env]: https://github.com/request/request/tree/f0c4ec061141051988d1216c24936ad2e7d5c45d#controlling-proxy-behaviour-using-environment-variables -[electron-download]: https://github.com/electron-userland/electron-download +[proxy-env-10]: https://github.com/gajus/global-agent/blob/v2.1.5/README.md#environment-variables +[proxy-env]: https://github.com/np-maintain/global-tunnel/blob/v2.7.1/README.md#auto-config +[electron-get]: https://github.com/electron/get [npm-permissions]: https://docs.npmjs.com/getting-started/fixing-npm-permissions [unsafe-perm]: https://docs.npmjs.com/misc/config#unsafe-perm diff --git a/docs/tutorial/performance.md b/docs/tutorial/performance.md index 9fb1b3c538ba7..5ea1b24c88f68 100644 --- a/docs/tutorial/performance.md +++ b/docs/tutorial/performance.md @@ -54,13 +54,13 @@ at once, consider the [Chrome Tracing] tool. Chances are that your app could be a little leaner, faster, and generally less resource-hungry if you attempt these steps. -1) [Carelessly including modules](#1-carelessly-including-modules) -2) [Loading and running code too soon](#2-loading-and-running-code-too-soon) -3) [Blocking the main process](#3-blocking-the-main-process) -4) [Blocking the renderer process](#4-blocking-the-renderer-process) -5) [Unnecessary polyfills](#5-unnecessary-polyfills) -6) [Unnecessary or blocking network requests](#6-unnecessary-or-blocking-network-requests) -7) [Bundle your code](#7-bundle-your-code) +1. [Carelessly including modules](#1-carelessly-including-modules) +2. [Loading and running code too soon](#2-loading-and-running-code-too-soon) +3. [Blocking the main process](#3-blocking-the-main-process) +4. [Blocking the renderer process](#4-blocking-the-renderer-process) +5. [Unnecessary polyfills](#5-unnecessary-polyfills) +6. [Unnecessary or blocking network requests](#6-unnecessary-or-blocking-network-requests) +7. [Bundle your code](#7-bundle-your-code) ## 1) Carelessly including modules @@ -418,7 +418,6 @@ As of writing this article, the popular choices include [Webpack][webpack], [performance-cpu-prof]: ../images/performance-cpu-prof.png [performance-heap-prof]: ../images/performance-heap-prof.png [chrome-devtools-tutorial]: https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/ -[chrome-tracing-tutorial]: [worker-threads]: https://nodejs.org/api/worker_threads.html [web-workers]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers [request-idle-callback]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback diff --git a/docs/tutorial/snapcraft.md b/docs/tutorial/snapcraft.md index a6516b6ae2353..45d8297babc3a 100644 --- a/docs/tutorial/snapcraft.md +++ b/docs/tutorial/snapcraft.md @@ -61,7 +61,6 @@ The output should look roughly like this: ├── libgcrypt.so.11 ├── libnode.so ├── locales - ├── natives_blob.bin ├── resources ├── v8_context_snapshot.bin └── version diff --git a/docs/tutorial/windows-store-guide.md b/docs/tutorial/windows-store-guide.md index 1ef39eef19d51..d8708647e8eab 100644 --- a/docs/tutorial/windows-store-guide.md +++ b/docs/tutorial/windows-store-guide.md @@ -62,7 +62,6 @@ The output should look roughly like this: │   ├── am.pak │   ├── ar.pak │   ├── [...] -├── natives_blob.bin ├── node.dll ├── resources │   ├── app diff --git a/electron_strings.grdp b/electron_strings.grdp index ca59124720799..5b3f39afcfdf4 100644 --- a/electron_strings.grdp +++ b/electron_strings.grdp @@ -69,4 +69,10 @@ Previous track + + en-US + + + en-US,en + diff --git a/filenames.auto.gni b/filenames.auto.gni index cf87af39e1163..02976212a594a 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -96,6 +96,7 @@ auto_filenames = { "docs/api/structures/mime-typed-buffer.md", "docs/api/structures/mouse-input-event.md", "docs/api/structures/mouse-wheel-input-event.md", + "docs/api/structures/new-window-event.md", "docs/api/structures/notification-action.md", "docs/api/structures/point.md", "docs/api/structures/printer-info.md", @@ -135,12 +136,12 @@ auto_filenames = { "lib/common/api/module-list.ts", "lib/common/api/native-image.js", "lib/common/api/shell.js", - "lib/common/clipboard-utils.ts", "lib/common/crash-reporter.js", "lib/common/define-properties.ts", "lib/common/electron-binding-setup.ts", - "lib/common/remote/type-utils.ts", + "lib/common/type-utils.ts", "lib/common/web-view-methods.ts", + "lib/common/webpack-globals-provider.ts", "lib/renderer/api/context-bridge.ts", "lib/renderer/api/crash-reporter.js", "lib/renderer/api/desktop-capturer.ts", @@ -165,6 +166,7 @@ auto_filenames = { "lib/renderer/web-view/web-view-element.ts", "lib/renderer/web-view/web-view-impl.ts", "lib/renderer/web-view/web-view-init.ts", + "lib/renderer/window-setup.ts", "lib/sandboxed_renderer/api/exports/electron.ts", "lib/sandboxed_renderer/api/module-list.ts", "lib/sandboxed_renderer/init.js", @@ -176,11 +178,8 @@ auto_filenames = { isolated_bundle_deps = [ "lib/common/electron-binding-setup.ts", "lib/isolated_renderer/init.js", - "lib/renderer/ipc-renderer-internal-utils.ts", - "lib/renderer/ipc-renderer-internal.ts", "lib/renderer/web-view/web-view-constants.ts", "lib/renderer/web-view/web-view-element.ts", - "lib/renderer/window-setup.ts", "package.json", "tsconfig.electron.json", "tsconfig.json", @@ -188,7 +187,9 @@ auto_filenames = { content_script_bundle_deps = [ "lib/common/electron-binding-setup.ts", + "lib/common/webpack-globals-provider.ts", "lib/content_script/init.js", + "lib/renderer/api/context-bridge.ts", "lib/renderer/chrome-api.ts", "lib/renderer/extensions/event.ts", "lib/renderer/extensions/i18n.ts", @@ -266,15 +267,15 @@ auto_filenames = { "lib/common/api/module-list.ts", "lib/common/api/native-image.js", "lib/common/api/shell.js", - "lib/common/clipboard-utils.ts", "lib/common/crash-reporter.js", "lib/common/define-properties.ts", "lib/common/electron-binding-setup.ts", "lib/common/init.ts", "lib/common/parse-features-string.js", - "lib/common/remote/type-utils.ts", "lib/common/reset-search-paths.ts", + "lib/common/type-utils.ts", "lib/common/web-view-methods.ts", + "lib/common/webpack-globals-provider.ts", "lib/renderer/ipc-renderer-internal-utils.ts", "lib/renderer/ipc-renderer-internal.ts", "package.json", @@ -289,14 +290,14 @@ auto_filenames = { "lib/common/api/module-list.ts", "lib/common/api/native-image.js", "lib/common/api/shell.js", - "lib/common/clipboard-utils.ts", "lib/common/crash-reporter.js", "lib/common/define-properties.ts", "lib/common/electron-binding-setup.ts", "lib/common/init.ts", - "lib/common/remote/type-utils.ts", "lib/common/reset-search-paths.ts", + "lib/common/type-utils.ts", "lib/common/web-view-methods.ts", + "lib/common/webpack-globals-provider.ts", "lib/renderer/api/context-bridge.ts", "lib/renderer/api/crash-reporter.js", "lib/renderer/api/desktop-capturer.ts", @@ -338,13 +339,13 @@ auto_filenames = { "lib/common/api/module-list.ts", "lib/common/api/native-image.js", "lib/common/api/shell.js", - "lib/common/clipboard-utils.ts", "lib/common/crash-reporter.js", "lib/common/define-properties.ts", "lib/common/electron-binding-setup.ts", "lib/common/init.ts", - "lib/common/remote/type-utils.ts", "lib/common/reset-search-paths.ts", + "lib/common/type-utils.ts", + "lib/common/webpack-globals-provider.ts", "lib/renderer/api/context-bridge.ts", "lib/renderer/api/crash-reporter.js", "lib/renderer/api/desktop-capturer.ts", diff --git a/filenames.gni b/filenames.gni index 9b99a4e1f0ca9..b3f130be54a96 100644 --- a/filenames.gni +++ b/filenames.gni @@ -22,100 +22,99 @@ filenames = { ] lib_sources = [ - "shell/app/atom_content_client.cc", - "shell/app/atom_content_client.h", - "shell/app/atom_main_delegate.cc", - "shell/app/atom_main_delegate.h", - "shell/app/atom_main_delegate_mac.h", - "shell/app/atom_main_delegate_mac.mm", + "chromium_src/chrome/browser/process_singleton.h", + "chromium_src/chrome/browser/process_singleton_posix.cc", + "chromium_src/chrome/browser/process_singleton_win.cc", + "chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc", + "chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h", + "shell/app/electron_content_client.cc", + "shell/app/electron_content_client.h", + "shell/app/electron_main_delegate.cc", + "shell/app/electron_main_delegate.h", + "shell/app/electron_main_delegate_mac.h", + "shell/app/electron_main_delegate_mac.mm", "shell/app/command_line_args.cc", "shell/app/command_line_args.h", "shell/app/uv_task_runner.cc", "shell/app/uv_task_runner.h", - "shell/browser/api/atom_api_app_mac.mm", - "shell/browser/api/atom_api_app.cc", - "shell/browser/font_defaults.cc", - "shell/browser/font_defaults.h", - "shell/browser/feature_list.cc", - "shell/browser/feature_list.h", - "shell/browser/api/atom_api_app.h", - "shell/browser/api/atom_api_auto_updater.cc", - "shell/browser/api/atom_api_auto_updater.h", - "shell/browser/api/atom_api_browser_view.cc", - "shell/browser/api/atom_api_browser_view.h", - "shell/browser/api/atom_api_content_tracing.cc", - "shell/browser/api/atom_api_cookies.cc", - "shell/browser/api/atom_api_cookies.h", - "shell/browser/api/atom_api_data_pipe_holder.cc", - "shell/browser/api/atom_api_data_pipe_holder.h", - "shell/browser/api/atom_api_debugger.cc", - "shell/browser/api/atom_api_debugger.h", - "shell/browser/api/atom_api_dialog.cc", - "shell/browser/api/atom_api_download_item.cc", - "shell/browser/api/atom_api_download_item.h", - "shell/browser/api/atom_api_event.cc", - "shell/browser/api/atom_api_global_shortcut.cc", - "shell/browser/api/atom_api_global_shortcut.h", - "shell/browser/api/atom_api_in_app_purchase.cc", - "shell/browser/api/atom_api_in_app_purchase.h", - "shell/browser/api/atom_api_menu.cc", - "shell/browser/api/atom_api_menu.h", - "shell/browser/api/atom_api_menu_mac.h", - "shell/browser/api/atom_api_menu_mac.mm", - "shell/browser/api/atom_api_menu_views.cc", - "shell/browser/api/atom_api_menu_views.h", - "shell/browser/api/atom_api_native_theme.cc", - "shell/browser/api/atom_api_native_theme.h", - "shell/browser/api/atom_api_native_theme_mac.mm", - "shell/browser/api/atom_api_net.cc", - "shell/browser/api/atom_api_net.h", - "shell/browser/api/atom_api_net_log.cc", - "shell/browser/api/atom_api_net_log.h", - "shell/browser/api/atom_api_notification.cc", - "shell/browser/api/atom_api_notification.h", - "shell/browser/api/atom_api_power_monitor_mac.mm", - "shell/browser/api/atom_api_power_monitor_win.cc", - "shell/browser/api/atom_api_power_monitor.cc", - "shell/browser/api/atom_api_power_monitor.h", - "shell/browser/api/atom_api_power_save_blocker.cc", - "shell/browser/api/atom_api_power_save_blocker.h", - "shell/browser/api/atom_api_protocol_ns.cc", - "shell/browser/api/atom_api_protocol_ns.h", - "shell/browser/api/atom_api_screen.cc", - "shell/browser/api/atom_api_screen.h", - "shell/browser/api/atom_api_session.cc", - "shell/browser/api/atom_api_session.h", - "shell/browser/api/atom_api_system_preferences.cc", - "shell/browser/api/atom_api_system_preferences.h", - "shell/browser/api/atom_api_system_preferences_mac.mm", - "shell/browser/api/atom_api_system_preferences_win.cc", - "shell/browser/api/atom_api_top_level_window.cc", - "shell/browser/api/atom_api_top_level_window.h", - "shell/browser/api/atom_api_tray.cc", - "shell/browser/api/atom_api_tray.h", - "shell/browser/api/atom_api_url_request_ns.cc", - "shell/browser/api/atom_api_url_request_ns.h", - "shell/browser/api/atom_api_view.cc", - "shell/browser/api/atom_api_view.h", - "shell/browser/api/atom_api_web_contents.cc", - "shell/browser/api/atom_api_web_contents.h", - "shell/browser/api/atom_api_web_contents_impl.cc", - "shell/browser/api/atom_api_web_contents_mac.mm", - "shell/browser/api/atom_api_web_contents_view.cc", - "shell/browser/api/atom_api_web_contents_view.h", - "shell/browser/api/atom_api_web_request_ns.cc", - "shell/browser/api/atom_api_web_request_ns.h", - "shell/browser/api/atom_api_web_view_manager.cc", - "shell/browser/api/atom_api_browser_window.cc", - "shell/browser/api/atom_api_browser_window.h", - "shell/browser/api/atom_api_browser_window_mac.mm", - "shell/browser/api/atom_api_browser_window_views.cc", + "shell/browser/api/electron_api_app.cc", + "shell/browser/api/electron_api_app.h", + "shell/browser/api/electron_api_app_mac.mm", + "shell/browser/api/electron_api_auto_updater.cc", + "shell/browser/api/electron_api_auto_updater.h", + "shell/browser/api/electron_api_browser_view.cc", + "shell/browser/api/electron_api_browser_view.h", + "shell/browser/api/electron_api_browser_window.cc", + "shell/browser/api/electron_api_browser_window.h", + "shell/browser/api/electron_api_browser_window_mac.mm", + "shell/browser/api/electron_api_browser_window_views.cc", + "shell/browser/api/electron_api_content_tracing.cc", + "shell/browser/api/electron_api_cookies.cc", + "shell/browser/api/electron_api_cookies.h", + "shell/browser/api/electron_api_data_pipe_holder.cc", + "shell/browser/api/electron_api_data_pipe_holder.h", + "shell/browser/api/electron_api_debugger.cc", + "shell/browser/api/electron_api_debugger.h", + "shell/browser/api/electron_api_dialog.cc", + "shell/browser/api/electron_api_download_item.cc", + "shell/browser/api/electron_api_download_item.h", + "shell/browser/api/electron_api_event.cc", + "shell/browser/api/electron_api_global_shortcut.cc", + "shell/browser/api/electron_api_global_shortcut.h", + "shell/browser/api/electron_api_in_app_purchase.cc", + "shell/browser/api/electron_api_in_app_purchase.h", + "shell/browser/api/electron_api_menu.cc", + "shell/browser/api/electron_api_menu.h", + "shell/browser/api/electron_api_menu_mac.h", + "shell/browser/api/electron_api_menu_mac.mm", + "shell/browser/api/electron_api_menu_views.cc", + "shell/browser/api/electron_api_menu_views.h", + "shell/browser/api/electron_api_native_theme.cc", + "shell/browser/api/electron_api_native_theme.h", + "shell/browser/api/electron_api_native_theme_mac.mm", + "shell/browser/api/electron_api_net.cc", + "shell/browser/api/electron_api_net.h", + "shell/browser/api/electron_api_net_log.cc", + "shell/browser/api/electron_api_net_log.h", + "shell/browser/api/electron_api_notification.cc", + "shell/browser/api/electron_api_notification.h", + "shell/browser/api/electron_api_power_monitor.cc", + "shell/browser/api/electron_api_power_monitor.h", + "shell/browser/api/electron_api_power_monitor_mac.mm", + "shell/browser/api/electron_api_power_monitor_win.cc", + "shell/browser/api/electron_api_power_save_blocker.cc", + "shell/browser/api/electron_api_power_save_blocker.h", + "shell/browser/api/electron_api_protocol_ns.cc", + "shell/browser/api/electron_api_protocol_ns.h", + "shell/browser/api/electron_api_screen.cc", + "shell/browser/api/electron_api_screen.h", + "shell/browser/api/electron_api_session.cc", + "shell/browser/api/electron_api_session.h", + "shell/browser/api/electron_api_system_preferences.cc", + "shell/browser/api/electron_api_system_preferences.h", + "shell/browser/api/electron_api_system_preferences_mac.mm", + "shell/browser/api/electron_api_system_preferences_win.cc", + "shell/browser/api/electron_api_top_level_window.cc", + "shell/browser/api/electron_api_top_level_window.h", + "shell/browser/api/electron_api_tray.cc", + "shell/browser/api/electron_api_tray.h", + "shell/browser/api/electron_api_url_loader.cc", + "shell/browser/api/electron_api_url_loader.h", + "shell/browser/api/electron_api_view.cc", + "shell/browser/api/electron_api_view.h", + "shell/browser/api/electron_api_web_contents.cc", + "shell/browser/api/electron_api_web_contents.h", + "shell/browser/api/electron_api_web_contents_impl.cc", + "shell/browser/api/electron_api_web_contents_mac.mm", + "shell/browser/api/electron_api_web_contents_view.cc", + "shell/browser/api/electron_api_web_contents_view.h", + "shell/browser/api/electron_api_web_request_ns.cc", + "shell/browser/api/electron_api_web_request_ns.h", + "shell/browser/api/electron_api_web_view_manager.cc", "shell/browser/api/event.cc", "shell/browser/api/event.h", "shell/browser/api/event_emitter_deprecated.cc", "shell/browser/api/event_emitter_deprecated.h", - "shell/browser/api/trackable_object.cc", - "shell/browser/api/trackable_object.h", "shell/browser/api/frame_subscriber.cc", "shell/browser/api/frame_subscriber.h", "shell/browser/api/gpu_info_enumerator.cc", @@ -126,67 +125,75 @@ filenames = { "shell/browser/api/process_metric.h", "shell/browser/api/save_page_handler.cc", "shell/browser/api/save_page_handler.h", + "shell/browser/api/trackable_object.cc", + "shell/browser/api/trackable_object.h", + "shell/browser/electron_autofill_driver.cc", + "shell/browser/electron_autofill_driver.h", + "shell/browser/electron_autofill_driver_factory.cc", + "shell/browser/electron_autofill_driver_factory.h", + "shell/browser/electron_browser_client.cc", + "shell/browser/electron_browser_client.h", + "shell/browser/electron_browser_context.cc", + "shell/browser/electron_browser_context.h", + "shell/browser/electron_browser_main_parts.cc", + "shell/browser/electron_browser_main_parts.h", + "shell/browser/electron_browser_main_parts_mac.mm", + "shell/browser/electron_browser_main_parts_posix.cc", + "shell/browser/electron_download_manager_delegate.cc", + "shell/browser/electron_download_manager_delegate.h", + "shell/browser/electron_gpu_client.cc", + "shell/browser/electron_gpu_client.h", + "shell/browser/electron_javascript_dialog_manager.cc", + "shell/browser/electron_javascript_dialog_manager.h", + "shell/browser/electron_navigation_throttle.cc", + "shell/browser/electron_navigation_throttle.h", + "shell/browser/electron_paths.h", + "shell/browser/electron_permission_manager.cc", + "shell/browser/electron_permission_manager.h", + "shell/browser/electron_quota_permission_context.cc", + "shell/browser/electron_quota_permission_context.h", + "shell/browser/electron_speech_recognition_manager_delegate.cc", + "shell/browser/electron_speech_recognition_manager_delegate.h", + "shell/browser/electron_web_ui_controller_factory.cc", + "shell/browser/electron_web_ui_controller_factory.h", "shell/browser/auto_updater.cc", "shell/browser/auto_updater.h", "shell/browser/auto_updater_mac.mm", - "shell/browser/atom_autofill_driver_factory.cc", - "shell/browser/atom_autofill_driver_factory.h", - "shell/browser/atom_autofill_driver.cc", - "shell/browser/atom_autofill_driver.h", - "shell/browser/atom_browser_client.cc", - "shell/browser/atom_browser_client.h", - "shell/browser/atom_browser_context.cc", - "shell/browser/atom_browser_context.h", - "shell/browser/atom_download_manager_delegate.cc", - "shell/browser/atom_download_manager_delegate.h", - "shell/browser/atom_gpu_client.cc", - "shell/browser/atom_gpu_client.h", - "shell/browser/atom_browser_main_parts.cc", - "shell/browser/atom_browser_main_parts.h", - "shell/browser/atom_browser_main_parts_mac.mm", - "shell/browser/atom_browser_main_parts_posix.cc", - "shell/browser/atom_javascript_dialog_manager.cc", - "shell/browser/atom_javascript_dialog_manager.h", - "shell/browser/atom_navigation_throttle.h", - "shell/browser/atom_navigation_throttle.cc", - "shell/browser/atom_paths.h", - "shell/browser/atom_permission_manager.cc", - "shell/browser/atom_permission_manager.h", - "shell/browser/atom_quota_permission_context.cc", - "shell/browser/atom_quota_permission_context.h", - "shell/browser/atom_speech_recognition_manager_delegate.cc", - "shell/browser/atom_speech_recognition_manager_delegate.h", - "shell/browser/atom_web_ui_controller_factory.cc", - "shell/browser/atom_web_ui_controller_factory.h", "shell/browser/browser.cc", "shell/browser/browser.h", "shell/browser/browser_linux.cc", "shell/browser/browser_mac.mm", - "shell/browser/browser_win.cc", "shell/browser/browser_observer.h", "shell/browser/browser_process_impl.cc", "shell/browser/browser_process_impl.h", + "shell/browser/browser_win.cc", "shell/browser/child_web_contents_tracker.cc", "shell/browser/child_web_contents_tracker.h", - "shell/browser/common_web_contents_delegate_mac.mm", - "shell/browser/common_web_contents_delegate_views.cc", "shell/browser/common_web_contents_delegate.cc", "shell/browser/common_web_contents_delegate.h", + "shell/browser/common_web_contents_delegate_mac.mm", + "shell/browser/common_web_contents_delegate_views.cc", "shell/browser/cookie_change_notifier.cc", "shell/browser/cookie_change_notifier.h", + "shell/browser/feature_list.cc", + "shell/browser/feature_list.h", + "shell/browser/font_defaults.cc", + "shell/browser/font_defaults.h", "shell/browser/javascript_environment.cc", "shell/browser/javascript_environment.h", "shell/browser/lib/bluetooth_chooser.cc", "shell/browser/lib/bluetooth_chooser.h", "shell/browser/lib/power_observer.h", - "shell/browser/lib/power_observer_linux.h", "shell/browser/lib/power_observer_linux.cc", + "shell/browser/lib/power_observer_linux.h", + "shell/browser/linux/unity_service.cc", + "shell/browser/linux/unity_service.h", "shell/browser/login_handler.cc", "shell/browser/login_handler.h", - "shell/browser/mac/atom_application.h", - "shell/browser/mac/atom_application.mm", - "shell/browser/mac/atom_application_delegate.h", - "shell/browser/mac/atom_application_delegate.mm", + "shell/browser/mac/electron_application.h", + "shell/browser/mac/electron_application.mm", + "shell/browser/mac/electron_application_delegate.h", + "shell/browser/mac/electron_application_delegate.mm", "shell/browser/mac/dict_util.h", "shell/browser/mac/dict_util.mm", "shell/browser/mac/in_app_purchase.h", @@ -195,48 +202,55 @@ filenames = { "shell/browser/mac/in_app_purchase_observer.mm", "shell/browser/mac/in_app_purchase_product.h", "shell/browser/mac/in_app_purchase_product.mm", + "shell/browser/media/media_capture_devices_dispatcher.cc", + "shell/browser/media/media_capture_devices_dispatcher.h", + "shell/browser/media/media_device_id_salt.cc", + "shell/browser/media/media_device_id_salt.h", + "shell/browser/media/media_stream_devices_controller.cc", + "shell/browser/media/media_stream_devices_controller.h", "shell/browser/microtasks_runner.cc", "shell/browser/microtasks_runner.h", "shell/browser/native_browser_view.cc", "shell/browser/native_browser_view.h", "shell/browser/native_browser_view_mac.h", "shell/browser/native_browser_view_mac.mm", - "shell/browser/native_browser_view_views.h", "shell/browser/native_browser_view_views.cc", + "shell/browser/native_browser_view_views.h", "shell/browser/native_window.cc", "shell/browser/native_window.h", - "shell/browser/native_window_views_win.cc", - "shell/browser/native_window_views.cc", - "shell/browser/native_window_views.h", "shell/browser/native_window_mac.h", "shell/browser/native_window_mac.mm", "shell/browser/native_window_observer.h", - "shell/browser/media/media_capture_devices_dispatcher.cc", - "shell/browser/media/media_capture_devices_dispatcher.h", - "shell/browser/media/media_device_id_salt.cc", - "shell/browser/media/media_device_id_salt.h", - "shell/browser/media/media_stream_devices_controller.cc", - "shell/browser/media/media_stream_devices_controller.h", + "shell/browser/native_window_views.cc", + "shell/browser/native_window_views.h", + "shell/browser/native_window_views_win.cc", "shell/browser/net/asar/asar_url_loader.cc", "shell/browser/net/asar/asar_url_loader.h", - "shell/browser/net/atom_url_loader_factory.cc", - "shell/browser/net/atom_url_loader_factory.h", + "shell/browser/net/electron_url_loader_factory.cc", + "shell/browser/net/electron_url_loader_factory.h", "shell/browser/net/cert_verifier_client.cc", "shell/browser/net/cert_verifier_client.h", - "shell/browser/net/proxying_url_loader_factory.cc", - "shell/browser/net/proxying_url_loader_factory.h", - "shell/browser/net/network_context_service_factory.cc", - "shell/browser/net/network_context_service_factory.h", "shell/browser/net/network_context_service.cc", "shell/browser/net/network_context_service.h", + "shell/browser/net/network_context_service_factory.cc", + "shell/browser/net/network_context_service_factory.h", "shell/browser/net/node_stream_loader.cc", "shell/browser/net/node_stream_loader.h", + "shell/browser/net/proxying_url_loader_factory.cc", + "shell/browser/net/proxying_url_loader_factory.h", + "shell/browser/net/proxying_websocket.cc", + "shell/browser/net/proxying_websocket.h", "shell/browser/net/resolve_proxy_helper.cc", "shell/browser/net/resolve_proxy_helper.h", "shell/browser/net/system_network_context_manager.cc", "shell/browser/net/system_network_context_manager.h", "shell/browser/net/url_pipe_loader.cc", "shell/browser/net/url_pipe_loader.h", + "shell/browser/net/web_request_api_interface.h", + "shell/browser/network_hints_handler_impl.cc", + "shell/browser/network_hints_handler_impl.h", + "shell/browser/node_debugger.cc", + "shell/browser/node_debugger.h", "shell/browser/notifications/linux/libnotify_notification.cc", "shell/browser/notifications/linux/libnotify_notification.h", "shell/browser/notifications/linux/notification_presenter_linux.cc", @@ -269,67 +283,65 @@ filenames = { "shell/browser/notifications/win/win32_notification.h", "shell/browser/notifications/win/windows_toast_notification.cc", "shell/browser/notifications/win/windows_toast_notification.h", - "shell/browser/node_debugger.cc", - "shell/browser/node_debugger.h", "shell/browser/pref_store_delegate.cc", "shell/browser/pref_store_delegate.h", + "shell/browser/relauncher.cc", + "shell/browser/relauncher.h", "shell/browser/relauncher_linux.cc", "shell/browser/relauncher_mac.cc", "shell/browser/relauncher_win.cc", - "shell/browser/relauncher.cc", - "shell/browser/relauncher.h", - "shell/browser/renderer_host/electron_render_message_filter.cc", - "shell/browser/renderer_host/electron_render_message_filter.h", "shell/browser/session_preferences.cc", "shell/browser/session_preferences.h", "shell/browser/special_storage_policy.cc", "shell/browser/special_storage_policy.h", "shell/browser/ui/accelerator_util.cc", "shell/browser/ui/accelerator_util.h", - "shell/browser/ui/atom_menu_model.cc", - "shell/browser/ui/atom_menu_model.h", + "shell/browser/ui/electron_menu_model.cc", + "shell/browser/ui/electron_menu_model.h", "shell/browser/ui/autofill_popup.cc", "shell/browser/ui/autofill_popup.h", "shell/browser/ui/certificate_trust.h", "shell/browser/ui/certificate_trust_mac.mm", "shell/browser/ui/certificate_trust_win.cc", - "shell/browser/ui/cocoa/atom_bundle_mover.h", - "shell/browser/ui/cocoa/atom_bundle_mover.mm", - "shell/browser/ui/cocoa/atom_menu_controller.h", - "shell/browser/ui/cocoa/atom_menu_controller.mm", - "shell/browser/ui/cocoa/atom_native_widget_mac.h", - "shell/browser/ui/cocoa/atom_native_widget_mac.mm", - "shell/browser/ui/cocoa/atom_ns_window.h", - "shell/browser/ui/cocoa/atom_ns_window.mm", - "shell/browser/ui/cocoa/atom_ns_window_delegate.h", - "shell/browser/ui/cocoa/atom_ns_window_delegate.mm", - "shell/browser/ui/cocoa/atom_preview_item.h", - "shell/browser/ui/cocoa/atom_preview_item.mm", - "shell/browser/ui/cocoa/atom_touch_bar.h", - "shell/browser/ui/cocoa/atom_touch_bar.mm", + "shell/browser/ui/cocoa/NSColor+Hex.h", + "shell/browser/ui/cocoa/NSColor+Hex.mm", + "shell/browser/ui/cocoa/NSString+ANSI.h", + "shell/browser/ui/cocoa/NSString+ANSI.mm", + "shell/browser/ui/cocoa/electron_bundle_mover.h", + "shell/browser/ui/cocoa/electron_bundle_mover.mm", + "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h", + "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm", + "shell/browser/ui/cocoa/electron_menu_controller.h", + "shell/browser/ui/cocoa/electron_menu_controller.mm", + "shell/browser/ui/cocoa/electron_native_widget_mac.h", + "shell/browser/ui/cocoa/electron_native_widget_mac.mm", + "shell/browser/ui/cocoa/electron_ns_window.h", + "shell/browser/ui/cocoa/electron_ns_window.mm", + "shell/browser/ui/cocoa/electron_ns_window_delegate.h", + "shell/browser/ui/cocoa/electron_ns_window_delegate.mm", + "shell/browser/ui/cocoa/electron_preview_item.h", + "shell/browser/ui/cocoa/electron_preview_item.mm", + "shell/browser/ui/cocoa/electron_touch_bar.h", + "shell/browser/ui/cocoa/electron_touch_bar.mm", "shell/browser/ui/cocoa/delayed_native_view_host.cc", "shell/browser/ui/cocoa/delayed_native_view_host.h", - "shell/browser/ui/cocoa/views_delegate_mac.h", - "shell/browser/ui/cocoa/views_delegate_mac.mm", - "shell/browser/ui/cocoa/root_view_mac.mm", - "shell/browser/ui/cocoa/root_view_mac.h", - "shell/browser/ui/cocoa/atom_inspectable_web_contents_view.h", - "shell/browser/ui/cocoa/atom_inspectable_web_contents_view.mm", "shell/browser/ui/cocoa/event_dispatching_window.h", "shell/browser/ui/cocoa/event_dispatching_window.mm", + "shell/browser/ui/cocoa/root_view_mac.h", + "shell/browser/ui/cocoa/root_view_mac.mm", + "shell/browser/ui/cocoa/views_delegate_mac.h", + "shell/browser/ui/cocoa/views_delegate_mac.mm", "shell/browser/ui/devtools_manager_delegate.cc", "shell/browser/ui/devtools_manager_delegate.h", "shell/browser/ui/devtools_ui.cc", "shell/browser/ui/devtools_ui.h", + "shell/browser/ui/drag_util.h", "shell/browser/ui/drag_util_mac.mm", "shell/browser/ui/drag_util_views.cc", - "shell/browser/ui/drag_util.h", "shell/browser/ui/file_dialog.h", "shell/browser/ui/file_dialog_gtk.cc", "shell/browser/ui/file_dialog_mac.mm", "shell/browser/ui/file_dialog_win.cc", - "shell/browser/ui/util_gtk.cc", - "shell/browser/ui/util_gtk.h", "shell/browser/ui/inspectable_web_contents.cc", "shell/browser/ui/inspectable_web_contents.h", "shell/browser/ui/inspectable_web_contents_delegate.h", @@ -344,20 +356,17 @@ filenames = { "shell/browser/ui/message_box_gtk.cc", "shell/browser/ui/message_box_mac.mm", "shell/browser/ui/message_box_win.cc", - "shell/browser/ui/cocoa/NSColor+Hex.mm", - "shell/browser/ui/cocoa/NSColor+Hex.h", - "shell/browser/ui/cocoa/NSString+ANSI.mm", - "shell/browser/ui/cocoa/NSString+ANSI.h", "shell/browser/ui/tray_icon.cc", "shell/browser/ui/tray_icon.h", - "shell/browser/ui/tray_icon_gtk.cc", - "shell/browser/ui/tray_icon_gtk.h", "shell/browser/ui/tray_icon_cocoa.h", "shell/browser/ui/tray_icon_cocoa.mm", + "shell/browser/ui/tray_icon_gtk.cc", + "shell/browser/ui/tray_icon_gtk.h", "shell/browser/ui/tray_icon_observer.h", "shell/browser/ui/tray_icon_win.cc", - "shell/browser/ui/views/atom_views_delegate.cc", - "shell/browser/ui/views/atom_views_delegate.h", + "shell/browser/ui/views/electron_views_delegate.cc", + "shell/browser/ui/views/electron_views_delegate_win.cc", + "shell/browser/ui/views/electron_views_delegate.h", "shell/browser/ui/views/autofill_popup_view.cc", "shell/browser/ui/views/autofill_popup_view.h", "shell/browser/ui/views/frameless_view.cc", @@ -380,16 +389,18 @@ filenames = { "shell/browser/ui/views/submenu_button.h", "shell/browser/ui/views/win_frame_view.cc", "shell/browser/ui/views/win_frame_view.h", - "shell/browser/ui/win/atom_desktop_native_widget_aura.cc", - "shell/browser/ui/win/atom_desktop_native_widget_aura.h", - "shell/browser/ui/win/atom_desktop_window_tree_host_win.cc", - "shell/browser/ui/win/atom_desktop_window_tree_host_win.h", + "shell/browser/ui/win/dialog_thread.cc", + "shell/browser/ui/win/dialog_thread.h", + "shell/browser/ui/win/electron_desktop_native_widget_aura.cc", + "shell/browser/ui/win/electron_desktop_native_widget_aura.h", + "shell/browser/ui/win/electron_desktop_window_tree_host_win.cc", + "shell/browser/ui/win/electron_desktop_window_tree_host_win.h", "shell/browser/ui/win/jump_list.cc", "shell/browser/ui/win/jump_list.h", - "shell/browser/ui/win/notify_icon_host.cc", - "shell/browser/ui/win/notify_icon_host.h", "shell/browser/ui/win/notify_icon.cc", "shell/browser/ui/win/notify_icon.h", + "shell/browser/ui/win/notify_icon_host.cc", + "shell/browser/ui/win/notify_icon_host.h", "shell/browser/ui/win/taskbar_host.cc", "shell/browser/ui/win/taskbar_host.h", "shell/browser/ui/x/event_disabler.cc", @@ -400,8 +411,6 @@ filenames = { "shell/browser/ui/x/x_window_utils.h", "shell/browser/unresponsive_suppressor.cc", "shell/browser/unresponsive_suppressor.h", - "shell/browser/win/scoped_hstring.cc", - "shell/browser/win/scoped_hstring.h", "shell/browser/web_contents_permission_helper.cc", "shell/browser/web_contents_permission_helper.h", "shell/browser/web_contents_preferences.cc", @@ -414,46 +423,48 @@ filenames = { "shell/browser/web_view_guest_delegate.h", "shell/browser/web_view_manager.cc", "shell/browser/web_view_manager.h", + "shell/browser/win/scoped_hstring.cc", + "shell/browser/win/scoped_hstring.h", "shell/browser/window_list.cc", "shell/browser/window_list.h", "shell/browser/window_list_observer.h", "shell/browser/zoom_level_delegate.cc", "shell/browser/zoom_level_delegate.h", - "shell/common/api/atom_api_asar.cc", - "shell/common/api/atom_api_clipboard.cc", - "shell/common/api/atom_api_clipboard.h", - "shell/common/api/atom_api_clipboard_mac.mm", - "shell/common/api/atom_api_command_line.cc", - "shell/common/api/atom_api_crash_reporter.cc", - "shell/common/api/atom_api_key_weak_map.h", - "shell/common/api/atom_api_native_image.cc", - "shell/common/api/atom_api_native_image.h", - "shell/common/api/atom_api_native_image_mac.mm", - "shell/common/api/atom_api_shell.cc", - "shell/common/api/atom_api_v8_util.cc", + "shell/common/api/electron_api_asar.cc", + "shell/common/api/electron_api_clipboard.cc", + "shell/common/api/electron_api_clipboard.h", + "shell/common/api/electron_api_clipboard_mac.mm", + "shell/common/api/electron_api_command_line.cc", + "shell/common/api/electron_api_crash_reporter.cc", + "shell/common/api/electron_api_key_weak_map.h", + "shell/common/api/electron_api_native_image.cc", + "shell/common/api/electron_api_native_image.h", + "shell/common/api/electron_api_native_image_mac.mm", + "shell/common/api/electron_api_shell.cc", + "shell/common/api/electron_api_v8_util.cc", + "shell/common/api/constructor.h", "shell/common/api/electron_bindings.cc", "shell/common/api/electron_bindings.h", - "shell/common/api/constructor.h", "shell/common/api/event_emitter_caller_deprecated.cc", "shell/common/api/event_emitter_caller_deprecated.h", "shell/common/api/features.cc", "shell/common/api/locker.cc", "shell/common/api/locker.h", + "shell/common/application_info.cc", + "shell/common/application_info.h", + "shell/common/application_info_linux.cc", + "shell/common/application_info_mac.mm", + "shell/common/application_info_win.cc", "shell/common/asar/archive.cc", "shell/common/asar/archive.h", "shell/common/asar/asar_util.cc", "shell/common/asar/asar_util.h", "shell/common/asar/scoped_temporary_file.cc", "shell/common/asar/scoped_temporary_file.h", - "shell/common/application_info_linux.cc", - "shell/common/application_info_mac.mm", - "shell/common/application_info_win.cc", - "shell/common/application_info.cc", - "shell/common/application_info.h", - "shell/common/atom_command_line.cc", - "shell/common/atom_command_line.h", - "shell/common/atom_constants.cc", - "shell/common/atom_constants.h", + "shell/common/electron_command_line.cc", + "shell/common/electron_command_line.h", + "shell/common/electron_constants.cc", + "shell/common/electron_constants.h", "shell/common/color_util.cc", "shell/common/color_util.h", "shell/common/crash_reporter/crash_reporter.cc", @@ -462,12 +473,15 @@ filenames = { "shell/common/crash_reporter/crash_reporter_linux.h", "shell/common/crash_reporter/crash_reporter_mac.h", "shell/common/crash_reporter/crash_reporter_mac.mm", - "shell/common/crash_reporter/crash_reporter_win.h", "shell/common/crash_reporter/crash_reporter_win.cc", + "shell/common/crash_reporter/crash_reporter_win.h", "shell/common/crash_reporter/linux/crash_dump_handler.cc", "shell/common/crash_reporter/linux/crash_dump_handler.h", "shell/common/crash_reporter/win/crash_service_main.cc", "shell/common/crash_reporter/win/crash_service_main.h", + "shell/common/deprecate_util.cc", + "shell/common/deprecate_util.h", + "shell/common/gin_converters/blink_converter_gin_adapter.h", "shell/common/gin_converters/callback_converter.h", "shell/common/gin_converters/file_dialog_converter.cc", "shell/common/gin_converters/file_dialog_converter.h", @@ -483,7 +497,6 @@ filenames = { "shell/common/gin_converters/net_converter.cc", "shell/common/gin_converters/net_converter.h", "shell/common/gin_converters/std_converter.h", - "shell/common/gin_converters/blink_converter_gin_adapter.h", "shell/common/gin_converters/value_converter_gin_adapter.h", "shell/common/gin_helper/arguments.cc", "shell/common/gin_helper/arguments.h", @@ -494,10 +507,10 @@ filenames = { "shell/common/gin_helper/dictionary.h", "shell/common/gin_helper/error_thrower.cc", "shell/common/gin_helper/error_thrower.h", - "shell/common/gin_helper/event_emitter_caller.cc", - "shell/common/gin_helper/event_emitter_caller.h", "shell/common/gin_helper/event_emitter.cc", "shell/common/gin_helper/event_emitter.h", + "shell/common/gin_helper/event_emitter_caller.cc", + "shell/common/gin_helper/event_emitter_caller.h", "shell/common/gin_helper/function_template.cc", "shell/common/gin_helper/function_template.h", "shell/common/gin_helper/object_template_builder.cc", @@ -507,12 +520,14 @@ filenames = { "shell/common/key_weak_map.h", "shell/common/keyboard_util.cc", "shell/common/keyboard_util.h", - "shell/common/deprecate_util.cc", - "shell/common/deprecate_util.h", - "shell/common/mouse_util.cc", - "shell/common/mouse_util.h", + "shell/common/language_util.h", + "shell/common/language_util_linux.cc", + "shell/common/language_util_mac.mm", + "shell/common/language_util_win.cc", "shell/common/mac/main_application_bundle.h", "shell/common/mac/main_application_bundle.mm", + "shell/common/mouse_util.cc", + "shell/common/mouse_util.h", "shell/common/native_mate_converters/accelerator_converter.cc", "shell/common/native_mate_converters/accelerator_converter.h", "shell/common/native_mate_converters/blink_converter.cc", @@ -545,51 +560,48 @@ filenames = { "shell/common/node_bindings_win.cc", "shell/common/node_bindings_win.h", "shell/common/node_includes.h", - "shell/common/node_util.h", "shell/common/node_util.cc", + "shell/common/node_util.h", "shell/common/options_switches.cc", "shell/common/options_switches.h", "shell/common/platform_util.h", "shell/common/platform_util_linux.cc", "shell/common/platform_util_mac.mm", "shell/common/platform_util_win.cc", - "shell/common/promise_util.h", "shell/common/promise_util.cc", - "shell/common/skia_util.h", + "shell/common/promise_util.h", "shell/common/skia_util.cc", - "shell/renderer/api/context_bridge/render_frame_context_bridge_store.cc", - "shell/renderer/api/context_bridge/render_frame_context_bridge_store.h", - "shell/renderer/api/atom_api_context_bridge.cc", - "shell/renderer/api/atom_api_context_bridge.h", - "shell/renderer/api/atom_api_renderer_ipc.cc", - "shell/renderer/api/atom_api_spell_check_client.cc", - "shell/renderer/api/atom_api_spell_check_client.h", - "shell/renderer/api/atom_api_web_frame.cc", - "shell/renderer/atom_autofill_agent.cc", - "shell/renderer/atom_autofill_agent.h", - "shell/renderer/atom_render_frame_observer.cc", - "shell/renderer/atom_render_frame_observer.h", - "shell/renderer/atom_renderer_client.cc", - "shell/renderer/atom_renderer_client.h", + "shell/common/skia_util.h", + "shell/renderer/api/context_bridge/object_cache.cc", + "shell/renderer/api/context_bridge/object_cache.h", + "shell/renderer/api/electron_api_context_bridge.cc", + "shell/renderer/api/electron_api_context_bridge.h", + "shell/renderer/api/electron_api_renderer_ipc.cc", + "shell/renderer/api/electron_api_spell_check_client.cc", + "shell/renderer/api/electron_api_spell_check_client.h", + "shell/renderer/api/electron_api_web_frame.cc", + "shell/renderer/electron_autofill_agent.cc", + "shell/renderer/electron_autofill_agent.h", + "shell/renderer/electron_render_frame_observer.cc", + "shell/renderer/electron_render_frame_observer.h", + "shell/renderer/electron_renderer_client.cc", + "shell/renderer/electron_renderer_client.h", + "shell/renderer/electron_sandboxed_renderer_client.cc", + "shell/renderer/electron_sandboxed_renderer_client.h", + "shell/renderer/browser_exposed_renderer_interfaces.cc", + "shell/renderer/browser_exposed_renderer_interfaces.h", "shell/renderer/content_settings_observer.cc", "shell/renderer/content_settings_observer.h", "shell/renderer/electron_api_service_impl.cc", "shell/renderer/electron_api_service_impl.h", - "shell/renderer/atom_sandboxed_renderer_client.cc", - "shell/renderer/atom_sandboxed_renderer_client.h", "shell/renderer/guest_view_container.cc", "shell/renderer/guest_view_container.h", "shell/renderer/renderer_client_base.cc", "shell/renderer/renderer_client_base.h", "shell/renderer/web_worker_observer.cc", "shell/renderer/web_worker_observer.h", - "shell/utility/atom_content_utility_client.cc", - "shell/utility/atom_content_utility_client.h", - "chromium_src/chrome/browser/process_singleton_posix.cc", - "chromium_src/chrome/browser/process_singleton_win.cc", - "chromium_src/chrome/browser/process_singleton.h", - "chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc", - "chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h", + "shell/utility/electron_content_utility_client.cc", + "shell/utility/electron_content_utility_client.h", ] lib_sources_nss = [ @@ -598,43 +610,43 @@ filenames = { ] lib_sources_extensions = [ - "shell/browser/extensions/api/runtime/atom_runtime_api_delegate.cc", - "shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h", - "shell/browser/extensions/atom_extensions_browser_client.cc", - "shell/browser/extensions/atom_extensions_browser_client.h", - "shell/browser/extensions/atom_browser_context_keyed_service_factories.cc", - "shell/browser/extensions/atom_browser_context_keyed_service_factories.h", - "shell/browser/extensions/atom_display_info_provider.cc", - "shell/browser/extensions/atom_display_info_provider.h", - "shell/browser/extensions/atom_extension_host_delegate.cc", - "shell/browser/extensions/atom_extension_host_delegate.h", - "shell/browser/extensions/atom_extension_loader.cc", - "shell/browser/extensions/atom_extension_loader.h", - "shell/browser/extensions/atom_extension_system.cc", - "shell/browser/extensions/atom_extension_system.h", - "shell/browser/extensions/atom_extension_system_factory.cc", - "shell/browser/extensions/atom_extension_system_factory.h", - "shell/browser/extensions/atom_extension_web_contents_observer.cc", - "shell/browser/extensions/atom_extension_web_contents_observer.h", - "shell/browser/extensions/atom_navigation_ui_data.cc", - "shell/browser/extensions/atom_navigation_ui_data.h", - "shell/common/extensions/atom_extensions_api_provider.cc", - "shell/common/extensions/atom_extensions_api_provider.h", - "shell/common/extensions/atom_extensions_client.cc", - "shell/common/extensions/atom_extensions_client.h", - "shell/renderer/extensions/atom_extensions_renderer_client.cc", - "shell/renderer/extensions/atom_extensions_renderer_client.h", + "shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc", + "shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h", + "shell/browser/extensions/electron_extensions_browser_client.cc", + "shell/browser/extensions/electron_extensions_browser_client.h", + "shell/browser/extensions/electron_browser_context_keyed_service_factories.cc", + "shell/browser/extensions/electron_browser_context_keyed_service_factories.h", + "shell/browser/extensions/electron_display_info_provider.cc", + "shell/browser/extensions/electron_display_info_provider.h", + "shell/browser/extensions/electron_extension_host_delegate.cc", + "shell/browser/extensions/electron_extension_host_delegate.h", + "shell/browser/extensions/electron_extension_loader.cc", + "shell/browser/extensions/electron_extension_loader.h", + "shell/browser/extensions/electron_extension_system.cc", + "shell/browser/extensions/electron_extension_system.h", + "shell/browser/extensions/electron_extension_system_factory.cc", + "shell/browser/extensions/electron_extension_system_factory.h", + "shell/browser/extensions/electron_extension_web_contents_observer.cc", + "shell/browser/extensions/electron_extension_web_contents_observer.h", + "shell/browser/extensions/electron_navigation_ui_data.cc", + "shell/browser/extensions/electron_navigation_ui_data.h", + "shell/common/extensions/electron_extensions_api_provider.cc", + "shell/common/extensions/electron_extensions_api_provider.h", + "shell/common/extensions/electron_extensions_client.cc", + "shell/common/extensions/electron_extensions_client.h", + "shell/renderer/extensions/electron_extensions_renderer_client.cc", + "shell/renderer/extensions/electron_extensions_renderer_client.h", ] app_sources = [ - "shell/app/atom_main.cc", - "shell/app/atom_main.h", + "shell/app/electron_main.cc", + "shell/app/electron_main.h", ] framework_sources = [ - "shell/app/atom_library_main.h", - "shell/app/atom_library_main.mm", + "shell/app/electron_library_main.h", + "shell/app/electron_library_main.mm", ] - login_helper_sources = [ "shell/app/atom_login_helper.mm" ] + login_helper_sources = [ "shell/app/electron_login_helper.mm" ] } diff --git a/filenames.hunspell.gni b/filenames.hunspell.gni new file mode 100644 index 0000000000000..866240876019e --- /dev/null +++ b/filenames.hunspell.gni @@ -0,0 +1,60 @@ +hunspell_dictionaries = [ + "//third_party/hunspell_dictionaries/af-ZA-3-0.bdic", + "//third_party/hunspell_dictionaries/bg-BG-3-0.bdic", + "//third_party/hunspell_dictionaries/ca-ES-3-0.bdic", + "//third_party/hunspell_dictionaries/cs-CZ-3-0.bdic", + "//third_party/hunspell_dictionaries/cy-GB-1-0.bdic", + "//third_party/hunspell_dictionaries/da-DK-3-0.bdic", + "//third_party/hunspell_dictionaries/de-DE-3-0.bdic", + "//third_party/hunspell_dictionaries/el-GR-3-0.bdic", + "//third_party/hunspell_dictionaries/en-AU-8-0.bdic", + "//third_party/hunspell_dictionaries/en-CA-8-0.bdic", + "//third_party/hunspell_dictionaries/en-GB-8-0.bdic", + "//third_party/hunspell_dictionaries/en-US-8-0.bdic", + "//third_party/hunspell_dictionaries/es-ES-3-0.bdic", + "//third_party/hunspell_dictionaries/et-EE-3-0.bdic", + "//third_party/hunspell_dictionaries/fa-IR-8-0.bdic", + "//third_party/hunspell_dictionaries/fo-FO-3-0.bdic", + "//third_party/hunspell_dictionaries/fr-FR-3-0.bdic", + "//third_party/hunspell_dictionaries/he-IL-3-0.bdic", + "//third_party/hunspell_dictionaries/hi-IN-3-0.bdic", + "//third_party/hunspell_dictionaries/hr-HR-3-0.bdic", + "//third_party/hunspell_dictionaries/hu-HU-3-0.bdic", + "//third_party/hunspell_dictionaries/hy-1-0.bdic", + "//third_party/hunspell_dictionaries/id-ID-3-0.bdic", + "//third_party/hunspell_dictionaries/it-IT-3-0.bdic", + "//third_party/hunspell_dictionaries/ko-3-0.bdic", + "//third_party/hunspell_dictionaries/lt-LT-3-0.bdic", + "//third_party/hunspell_dictionaries/lv-LV-3-0.bdic", + "//third_party/hunspell_dictionaries/nb-NO-3-0.bdic", + "//third_party/hunspell_dictionaries/nl-NL-3-0.bdic", + "//third_party/hunspell_dictionaries/pl-PL-3-0.bdic", + "//third_party/hunspell_dictionaries/pt-BR-3-0.bdic", + "//third_party/hunspell_dictionaries/pt-PT-3-0.bdic", + "//third_party/hunspell_dictionaries/ro-RO-3-0.bdic", + "//third_party/hunspell_dictionaries/ru-RU-3-0.bdic", + "//third_party/hunspell_dictionaries/sh-3-0.bdic", + "//third_party/hunspell_dictionaries/sh-4-0.bdic", + "//third_party/hunspell_dictionaries/sk-SK-3-0.bdic", + "//third_party/hunspell_dictionaries/sl-SI-3-0.bdic", + "//third_party/hunspell_dictionaries/sq-3-0.bdic", + "//third_party/hunspell_dictionaries/sr-3-0.bdic", + "//third_party/hunspell_dictionaries/sr-4-0.bdic", + "//third_party/hunspell_dictionaries/sv-SE-3-0.bdic", + "//third_party/hunspell_dictionaries/ta-IN-3-0.bdic", + "//third_party/hunspell_dictionaries/tg-TG-5-0.bdic", + "//third_party/hunspell_dictionaries/tr-TR-4-0.bdic", + "//third_party/hunspell_dictionaries/uk-UA-3-0.bdic", + "//third_party/hunspell_dictionaries/vi-VN-3-0.bdic", + "//third_party/hunspell_dictionaries/xx-XX-3-0.bdic", +] + +hunspell_licenses = [ + "//third_party/hunspell_dictionaries/COPYING", + "//third_party/hunspell_dictionaries/COPYING.Apache", + "//third_party/hunspell_dictionaries/COPYING.LESSER", + "//third_party/hunspell_dictionaries/COPYING.LGPL", + "//third_party/hunspell_dictionaries/COPYING.MIT", + "//third_party/hunspell_dictionaries/COPYING.MPL", + "//third_party/hunspell_dictionaries/LICENSE", +] diff --git a/lib/browser/api/app.ts b/lib/browser/api/app.ts index da9fa4cdd4544..e44e7dba39cac 100644 --- a/lib/browser/api/app.ts +++ b/lib/browser/api/app.ts @@ -1,21 +1,21 @@ -import * as fs from 'fs' -import * as path from 'path' +import * as fs from 'fs'; +import * as path from 'path'; -import { deprecate, Menu } from 'electron' -import { EventEmitter } from 'events' +import { deprecate, Menu } from 'electron'; +import { EventEmitter } from 'events'; -const bindings = process.electronBinding('app') -const commandLine = process.electronBinding('command_line') -const { app, App } = bindings +const bindings = process.electronBinding('app'); +const commandLine = process.electronBinding('command_line'); +const { app, App } = bindings; // Only one app object permitted. -export default app +export default app; -let dockMenu: Electron.Menu | null = null +let dockMenu: Electron.Menu | null = null; // App is an EventEmitter. -Object.setPrototypeOf(App.prototype, EventEmitter.prototype) -EventEmitter.call(app as any) +Object.setPrototypeOf(App.prototype, EventEmitter.prototype); +EventEmitter.call(app as any); Object.assign(app, { commandLine: { @@ -24,99 +24,99 @@ Object.assign(app, { appendSwitch: (theSwitch: string, value?: string) => commandLine.appendSwitch(String(theSwitch), typeof value === 'undefined' ? value : String(value)), appendArgument: (arg: string) => commandLine.appendArgument(String(arg)) } as Electron.CommandLine -}) +}); // we define this here because it'd be overly complicated to // do in native land Object.defineProperty(app, 'applicationMenu', { get () { - return Menu.getApplicationMenu() + return Menu.getApplicationMenu(); }, set (menu: Electron.Menu | null) { - return Menu.setApplicationMenu(menu) + return Menu.setApplicationMenu(menu); } -}) +}); App.prototype.isPackaged = (() => { - const execFile = path.basename(process.execPath).toLowerCase() + const execFile = path.basename(process.execPath).toLowerCase(); if (process.platform === 'win32') { - return execFile !== 'electron.exe' + return execFile !== 'electron.exe'; } - return execFile !== 'electron' -})() + return execFile !== 'electron'; +})(); app._setDefaultAppPaths = (packagePath) => { // Set the user path according to application's name. - app.setPath('userData', path.join(app.getPath('appData'), app.name!)) - app.setPath('userCache', path.join(app.getPath('cache'), app.name!)) - app.setAppPath(packagePath) + app.setPath('userData', path.join(app.getPath('appData'), app.name!)); + app.setPath('userCache', path.join(app.getPath('cache'), app.name!)); + app.setAppPath(packagePath); // Add support for --user-data-dir= if (app.commandLine.hasSwitch('user-data-dir')) { - const userDataDir = app.commandLine.getSwitchValue('user-data-dir') - if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir) + const userDataDir = app.commandLine.getSwitchValue('user-data-dir'); + if (path.isAbsolute(userDataDir)) app.setPath('userData', userDataDir); } -} +}; if (process.platform === 'darwin') { - const setDockMenu = app.dock!.setMenu + const setDockMenu = app.dock!.setMenu; app.dock!.setMenu = (menu) => { - dockMenu = menu - setDockMenu(menu) - } - app.dock!.getMenu = () => dockMenu + dockMenu = menu; + setDockMenu(menu); + }; + app.dock!.getMenu = () => dockMenu; } if (process.platform === 'linux') { - const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m - const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m + const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m; + const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m; const getStatus = (pid: number) => { try { - return fs.readFileSync(`/proc/${pid}/status`, 'utf8') + return fs.readFileSync(`/proc/${pid}/status`, 'utf8'); } catch { - return '' + return ''; } - } + }; const getEntry = (file: string, pattern: RegExp) => { - const match = file.match(pattern) - return match ? parseInt(match[1], 10) : 0 - } + const match = file.match(pattern); + return match ? parseInt(match[1], 10) : 0; + }; const getProcessMemoryInfo = (pid: number) => { - const file = getStatus(pid) + const file = getStatus(pid); return { workingSetSize: getEntry(file, patternVmRSS), peakWorkingSetSize: getEntry(file, patternVmHWM) - } - } + }; + }; - const nativeFn = app.getAppMetrics + const nativeFn = app.getAppMetrics; app.getAppMetrics = () => { - const metrics = nativeFn.call(app) + const metrics = nativeFn.call(app); for (const metric of metrics) { - metric.memory = getProcessMemoryInfo(metric.pid) + metric.memory = getProcessMemoryInfo(metric.pid); } - return metrics - } + return metrics; + }; } // Routes the events to webContents. -const events = ['login', 'certificate-error', 'select-client-certificate'] +const events = ['certificate-error', 'select-client-certificate']; for (const name of events) { - app.on(name as 'login', (event, webContents, ...args: any[]) => { - webContents.emit(name, event, ...args) - }) + app.on(name as 'certificate-error', (event, webContents, ...args: any[]) => { + webContents.emit(name, event, ...args); + }); } // Property Deprecations -deprecate.fnToProperty(App.prototype, 'accessibilitySupportEnabled', '_isAccessibilitySupportEnabled', '_setAccessibilitySupportEnabled') -deprecate.fnToProperty(App.prototype, 'badgeCount', '_getBadgeCount', '_setBadgeCount') -deprecate.fnToProperty(App.prototype, 'name', '_getName', '_setName') +deprecate.fnToProperty(App.prototype, 'accessibilitySupportEnabled', '_isAccessibilitySupportEnabled', '_setAccessibilitySupportEnabled'); +deprecate.fnToProperty(App.prototype, 'badgeCount', '_getBadgeCount', '_setBadgeCount'); +deprecate.fnToProperty(App.prototype, 'name', '_getName', '_setName'); // Wrappers for native classes. -const { DownloadItem } = process.electronBinding('download_item') -Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype) +const { DownloadItem } = process.electronBinding('download_item'); +Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype); diff --git a/lib/browser/api/auto-updater.js b/lib/browser/api/auto-updater.js index db3e8da158c3d..3435247a68ccc 100644 --- a/lib/browser/api/auto-updater.js +++ b/lib/browser/api/auto-updater.js @@ -1,7 +1,7 @@ -'use strict' +'use strict'; if (process.platform === 'win32') { - module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win') + module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-win'); } else { - module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native') + module.exports = require('@electron/internal/browser/api/auto-updater/auto-updater-native'); } diff --git a/lib/browser/api/auto-updater/auto-updater-native.js b/lib/browser/api/auto-updater/auto-updater-native.js index 666cd758c8871..ded4a36681c3f 100644 --- a/lib/browser/api/auto-updater/auto-updater-native.js +++ b/lib/browser/api/auto-updater/auto-updater-native.js @@ -1,10 +1,10 @@ -'use strict' +'use strict'; -const EventEmitter = require('events').EventEmitter -const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater') +const EventEmitter = require('events').EventEmitter; +const { autoUpdater, AutoUpdater } = process.electronBinding('auto_updater'); // AutoUpdater is an EventEmitter. -Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype) -EventEmitter.call(autoUpdater) +Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype); +EventEmitter.call(autoUpdater); -module.exports = autoUpdater +module.exports = autoUpdater; diff --git a/lib/browser/api/auto-updater/auto-updater-win.js b/lib/browser/api/auto-updater/auto-updater-win.js index cb8e6a0db51c8..93e7a94ac58e7 100644 --- a/lib/browser/api/auto-updater/auto-updater-win.js +++ b/lib/browser/api/auto-updater/auto-updater-win.js @@ -1,74 +1,74 @@ -'use strict' +'use strict'; -const { app } = require('electron') -const { EventEmitter } = require('events') -const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win') +const { app } = require('electron'); +const { EventEmitter } = require('events'); +const squirrelUpdate = require('@electron/internal/browser/api/auto-updater/squirrel-update-win'); class AutoUpdater extends EventEmitter { quitAndInstall () { if (!this.updateAvailable) { - return this.emitError('No update available, can\'t quit and install') + return this.emitError('No update available, can\'t quit and install'); } - squirrelUpdate.processStart() - app.quit() + squirrelUpdate.processStart(); + app.quit(); } getFeedURL () { - return this.updateURL + return this.updateURL; } setFeedURL (options) { - let updateURL + let updateURL; if (typeof options === 'object') { if (typeof options.url === 'string') { - updateURL = options.url + updateURL = options.url; } else { - throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call') + throw new Error('Expected options object to contain a \'url\' string property in setFeedUrl call'); } } else if (typeof options === 'string') { - updateURL = options + updateURL = options; } else { - throw new Error('Expected an options object with a \'url\' property to be provided') + throw new Error('Expected an options object with a \'url\' property to be provided'); } - this.updateURL = updateURL + this.updateURL = updateURL; } checkForUpdates () { if (!this.updateURL) { - return this.emitError('Update URL is not set') + return this.emitError('Update URL is not set'); } if (!squirrelUpdate.supported()) { - return this.emitError('Can not find Squirrel') + return this.emitError('Can not find Squirrel'); } - this.emit('checking-for-update') + this.emit('checking-for-update'); squirrelUpdate.checkForUpdate(this.updateURL, (error, update) => { if (error != null) { - return this.emitError(error) + return this.emitError(error); } if (update == null) { - return this.emit('update-not-available') + return this.emit('update-not-available'); } - this.updateAvailable = true - this.emit('update-available') + this.updateAvailable = true; + this.emit('update-available'); squirrelUpdate.update(this.updateURL, (error) => { if (error != null) { - return this.emitError(error) + return this.emitError(error); } - const { releaseNotes, version } = update + const { releaseNotes, version } = update; // Date is not available on Windows, so fake it. - const date = new Date() + const date = new Date(); this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => { - this.quitAndInstall() - }) - }) - }) + this.quitAndInstall(); + }); + }); + }); } // Private: Emit both error object and message, this is to keep compatibility // with Old APIs. emitError (message) { - this.emit('error', new Error(message), message) + this.emit('error', new Error(message), message); } } -module.exports = new AutoUpdater() +module.exports = new AutoUpdater(); diff --git a/lib/browser/api/auto-updater/squirrel-update-win.js b/lib/browser/api/auto-updater/squirrel-update-win.js index ddb29f8df6f8a..8ee6489da8f7e 100644 --- a/lib/browser/api/auto-updater/squirrel-update-win.js +++ b/lib/browser/api/auto-updater/squirrel-update-win.js @@ -1,24 +1,24 @@ -'use strict' +'use strict'; -const fs = require('fs') -const path = require('path') -const spawn = require('child_process').spawn +const fs = require('fs'); +const path = require('path'); +const spawn = require('child_process').spawn; // i.e. my-app/app-0.1.13/ -const appFolder = path.dirname(process.execPath) +const appFolder = path.dirname(process.execPath); // i.e. my-app/Update.exe -const updateExe = path.resolve(appFolder, '..', 'Update.exe') -const exeName = path.basename(process.execPath) -let spawnedArgs = [] -let spawnedProcess +const updateExe = path.resolve(appFolder, '..', 'Update.exe'); +const exeName = path.basename(process.execPath); +let spawnedArgs = []; +let spawnedProcess; -const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i]) +const isSameArgs = (args) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i]); // Spawn a command and invoke the callback when it completes with an error // and the output from standard out. const spawnUpdate = function (args, detached, callback) { - let error, errorEmitted, stderr, stdout + let error, errorEmitted, stderr, stdout; try { // Ensure we don't spawn multiple squirrel processes @@ -28,92 +28,92 @@ const spawnUpdate = function (args, detached, callback) { if (spawnedProcess && !isSameArgs(args)) { // Disabled for backwards compatibility: // eslint-disable-next-line standard/no-callback-literal - return callback(`AutoUpdater process with arguments ${args} is already running`) + return callback(`AutoUpdater process with arguments ${args} is already running`); } else if (!spawnedProcess) { spawnedProcess = spawn(updateExe, args, { detached: detached, windowsHide: true - }) - spawnedArgs = args || [] + }); + spawnedArgs = args || []; } } catch (error1) { - error = error1 + error = error1; // Shouldn't happen, but still guard it. process.nextTick(function () { - return callback(error) - }) - return + return callback(error); + }); + return; } - stdout = '' - stderr = '' + stdout = ''; + stderr = ''; - spawnedProcess.stdout.on('data', (data) => { stdout += data }) - spawnedProcess.stderr.on('data', (data) => { stderr += data }) + spawnedProcess.stdout.on('data', (data) => { stdout += data; }); + spawnedProcess.stderr.on('data', (data) => { stderr += data; }); - errorEmitted = false + errorEmitted = false; spawnedProcess.on('error', (error) => { - errorEmitted = true - callback(error) - }) + errorEmitted = true; + callback(error); + }); return spawnedProcess.on('exit', function (code, signal) { - spawnedProcess = undefined - spawnedArgs = [] + spawnedProcess = undefined; + spawnedArgs = []; // We may have already emitted an error. if (errorEmitted) { - return + return; } // Process terminated with error. if (code !== 0) { // Disabled for backwards compatibility: // eslint-disable-next-line standard/no-callback-literal - return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`) + return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`); } // Success. - callback(null, stdout) - }) -} + callback(null, stdout); + }); +}; // Start an instance of the installed app. exports.processStart = function () { - return spawnUpdate(['--processStartAndWait', exeName], true, function () {}) -} + return spawnUpdate(['--processStartAndWait', exeName], true, function () {}); +}; // Download the releases specified by the URL and write new results to stdout. exports.checkForUpdate = function (updateURL, callback) { return spawnUpdate(['--checkForUpdate', updateURL], false, function (error, stdout) { - let ref, ref1, update + let ref, ref1, update; if (error != null) { - return callback(error) + return callback(error); } try { // Last line of output is the JSON details about the releases - const json = stdout.trim().split('\n').pop() - update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : void 0 : void 0 : void 0 + const json = stdout.trim().split('\n').pop(); + update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : void 0 : void 0 : void 0; } catch { // Disabled for backwards compatibility: // eslint-disable-next-line standard/no-callback-literal - return callback(`Invalid result:\n${stdout}`) + return callback(`Invalid result:\n${stdout}`); } - return callback(null, update) - }) -} + return callback(null, update); + }); +}; // Update the application to the latest remote version specified by URL. exports.update = function (updateURL, callback) { - return spawnUpdate(['--update', updateURL], false, callback) -} + return spawnUpdate(['--update', updateURL], false, callback); +}; // Is the Update.exe installed with the current application? exports.supported = function () { try { - fs.accessSync(updateExe, fs.R_OK) - return true + fs.accessSync(updateExe, fs.R_OK); + return true; } catch { - return false + return false; } -} +}; diff --git a/lib/browser/api/browser-view.js b/lib/browser/api/browser-view.js index 5471b6359313e..2a57f5709d902 100644 --- a/lib/browser/api/browser-view.js +++ b/lib/browser/api/browser-view.js @@ -1,16 +1,16 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') -const { BrowserView } = process.electronBinding('browser_view') +const { EventEmitter } = require('events'); +const { BrowserView } = process.electronBinding('browser_view'); -Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype) +Object.setPrototypeOf(BrowserView.prototype, EventEmitter.prototype); BrowserView.fromWebContents = (webContents) => { for (const view of BrowserView.getAllViews()) { - if (view.webContents.equal(webContents)) return view + if (view.webContents.equal(webContents)) return view; } - return null -} + return null; +}; -module.exports = BrowserView +module.exports = BrowserView; diff --git a/lib/browser/api/browser-window.js b/lib/browser/api/browser-window.js index 521414fd01bab..107d6fa6b615d 100644 --- a/lib/browser/api/browser-window.js +++ b/lib/browser/api/browser-window.js @@ -1,49 +1,49 @@ -'use strict' +'use strict'; -const electron = require('electron') -const { WebContentsView, TopLevelWindow, deprecate } = electron -const { BrowserWindow } = process.electronBinding('window') +const electron = require('electron'); +const { WebContentsView, TopLevelWindow, deprecate } = electron; +const { BrowserWindow } = process.electronBinding('window'); -Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype) +Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype); BrowserWindow.prototype._init = function () { // Call parent class's _init. - TopLevelWindow.prototype._init.call(this) + TopLevelWindow.prototype._init.call(this); // Avoid recursive require. - const { app } = electron + const { app } = electron; // Create WebContentsView. - this.setContentView(new WebContentsView(this.webContents)) + this.setContentView(new WebContentsView(this.webContents)); - const nativeSetBounds = this.setBounds + const nativeSetBounds = this.setBounds; this.setBounds = (bounds, ...opts) => { bounds = { ...this.getBounds(), ...bounds - } - nativeSetBounds.call(this, bounds, ...opts) - } + }; + nativeSetBounds.call(this, bounds, ...opts); + }; // window.resizeTo(...) // window.moveTo(...) this.webContents.on('move', (event, size) => { - this.setBounds(size) - }) + this.setBounds(size); + }); // Hide the auto-hide menu when webContents is focused. this.webContents.on('activate', () => { - if (process.platform !== 'darwin' && this.isMenuBarAutoHide() && this.isMenuBarVisible()) { - this.setMenuBarVisibility(false) + if (process.platform !== 'darwin' && this.autoHideMenuBar && this.isMenuBarVisible()) { + this.setMenuBarVisibility(false); } - }) + }); // Change window title to page title. this.webContents.on('page-title-updated', (event, title, ...args) => { // Route the event to BrowserWindow. - this.emit('page-title-updated', event, title, ...args) - if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title) - }) + this.emit('page-title-updated', event, title, ...args); + if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title); + }); // Sometimes the webContents doesn't get focus when window is shown, so we // have to force focusing on webContents in this case. The safest way is to @@ -54,144 +54,144 @@ BrowserWindow.prototype._init = function () { // Finder, we still do it on all platforms in case of other bugs we don't // know. this.webContents.once('load-url', function () { - this.focus() - }) + this.focus(); + }); // Redirect focus/blur event to app instance too. this.on('blur', (event) => { - app.emit('browser-window-blur', event, this) - }) + app.emit('browser-window-blur', event, this); + }); this.on('focus', (event) => { - app.emit('browser-window-focus', event, this) - }) + app.emit('browser-window-focus', event, this); + }); // Subscribe to visibilityState changes and pass to renderer process. - let isVisible = this.isVisible() && !this.isMinimized() + let isVisible = this.isVisible() && !this.isMinimized(); const visibilityChanged = () => { - const newState = this.isVisible() && !this.isMinimized() + const newState = this.isVisible() && !this.isMinimized(); if (isVisible !== newState) { - isVisible = newState - const visibilityState = isVisible ? 'visible' : 'hidden' - this.webContents.emit('-window-visibility-change', visibilityState) + isVisible = newState; + const visibilityState = isVisible ? 'visible' : 'hidden'; + this.webContents.emit('-window-visibility-change', visibilityState); } - } + }; - const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore'] + const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore']; for (const event of visibilityEvents) { - this.on(event, visibilityChanged) + this.on(event, visibilityChanged); } // Notify the creation of the window. - const event = process.electronBinding('event').createEmpty() - app.emit('browser-window-created', event, this) + const event = process.electronBinding('event').createEmpty(); + app.emit('browser-window-created', event, this); Object.defineProperty(this, 'devToolsWebContents', { enumerable: true, configurable: false, get () { - return this.webContents.devToolsWebContents + return this.webContents.devToolsWebContents; } - }) -} + }); +}; const isBrowserWindow = (win) => { - return win && win.constructor.name === 'BrowserWindow' -} + return win && win.constructor.name === 'BrowserWindow'; +}; BrowserWindow.fromId = (id) => { - const win = TopLevelWindow.fromId(id) - return isBrowserWindow(win) ? win : null -} + const win = TopLevelWindow.fromId(id); + return isBrowserWindow(win) ? win : null; +}; BrowserWindow.getAllWindows = () => { - return TopLevelWindow.getAllWindows().filter(isBrowserWindow) -} + return TopLevelWindow.getAllWindows().filter(isBrowserWindow); +}; BrowserWindow.getFocusedWindow = () => { for (const window of BrowserWindow.getAllWindows()) { - if (window.isFocused() || window.isDevToolsFocused()) return window + if (window.isFocused() || window.isDevToolsFocused()) return window; } - return null -} + return null; +}; BrowserWindow.fromWebContents = (webContents) => { for (const window of BrowserWindow.getAllWindows()) { - if (window.webContents.equal(webContents)) return window + if (window.webContents && window.webContents.equal(webContents)) return window; } - return null -} + return null; +}; BrowserWindow.fromBrowserView = (browserView) => { for (const window of BrowserWindow.getAllWindows()) { - if (window.getBrowserView() === browserView) return window + if (window.getBrowserView() === browserView) return window; } - return null -} + return null; +}; // Helpers. Object.assign(BrowserWindow.prototype, { loadURL (...args) { - return this.webContents.loadURL(...args) + return this.webContents.loadURL(...args); }, getURL (...args) { - return this.webContents.getURL() + return this.webContents.getURL(); }, loadFile (...args) { - return this.webContents.loadFile(...args) + return this.webContents.loadFile(...args); }, reload (...args) { - return this.webContents.reload(...args) + return this.webContents.reload(...args); }, send (...args) { - return this.webContents.send(...args) + return this.webContents.send(...args); }, openDevTools (...args) { - return this.webContents.openDevTools(...args) + return this.webContents.openDevTools(...args); }, closeDevTools () { - return this.webContents.closeDevTools() + return this.webContents.closeDevTools(); }, isDevToolsOpened () { - return this.webContents.isDevToolsOpened() + return this.webContents.isDevToolsOpened(); }, isDevToolsFocused () { - return this.webContents.isDevToolsFocused() + return this.webContents.isDevToolsFocused(); }, toggleDevTools () { - return this.webContents.toggleDevTools() + return this.webContents.toggleDevTools(); }, inspectElement (...args) { - return this.webContents.inspectElement(...args) + return this.webContents.inspectElement(...args); }, inspectSharedWorker () { - return this.webContents.inspectSharedWorker() + return this.webContents.inspectSharedWorker(); }, inspectServiceWorker () { - return this.webContents.inspectServiceWorker() + return this.webContents.inspectServiceWorker(); }, showDefinitionForSelection () { - return this.webContents.showDefinitionForSelection() + return this.webContents.showDefinitionForSelection(); }, capturePage (...args) { - return this.webContents.capturePage(...args) + return this.webContents.capturePage(...args); }, setTouchBar (touchBar) { - electron.TouchBar._setOnWindow(touchBar, this) + electron.TouchBar._setOnWindow(touchBar, this); }, setBackgroundThrottling (allowed) { - this.webContents.setBackgroundThrottling(allowed) + this.webContents.setBackgroundThrottling(allowed); } -}) +}); // Deprecations -deprecate.fnToProperty(BrowserWindow.prototype, 'autoHideMenuBar', '_isMenuBarAutoHide', '_setAutoHideMenuBar') -deprecate.fnToProperty(BrowserWindow.prototype, 'minimizable', '_isMinimizable', '_setMinimizable') -deprecate.fnToProperty(BrowserWindow.prototype, 'maximizable', '_isMaximizable', '_setMaximizable') -deprecate.fnToProperty(BrowserWindow.prototype, 'resizable', '_isResizable', '_setResizable') -deprecate.fnToProperty(BrowserWindow.prototype, 'fullScreenable', '_isFullScreenable', '_setFullScreenable') -deprecate.fnToProperty(BrowserWindow.prototype, 'closable', '_isClosable', '_setClosable') -deprecate.fnToProperty(BrowserWindow.prototype, 'movable', '_isMovable', '_setMovable') - -module.exports = BrowserWindow +deprecate.fnToProperty(BrowserWindow.prototype, 'autoHideMenuBar', '_isMenuBarAutoHide', '_setAutoHideMenuBar'); +deprecate.fnToProperty(BrowserWindow.prototype, 'minimizable', '_isMinimizable', '_setMinimizable'); +deprecate.fnToProperty(BrowserWindow.prototype, 'maximizable', '_isMaximizable', '_setMaximizable'); +deprecate.fnToProperty(BrowserWindow.prototype, 'resizable', '_isResizable', '_setResizable'); +deprecate.fnToProperty(BrowserWindow.prototype, 'fullScreenable', '_isFullScreenable', '_setFullScreenable'); +deprecate.fnToProperty(BrowserWindow.prototype, 'closable', '_isClosable', '_setClosable'); +deprecate.fnToProperty(BrowserWindow.prototype, 'movable', '_isMovable', '_setMovable'); + +module.exports = BrowserWindow; diff --git a/lib/browser/api/content-tracing.js b/lib/browser/api/content-tracing.js index 64565f72db3ce..9aa9792954baf 100644 --- a/lib/browser/api/content-tracing.js +++ b/lib/browser/api/content-tracing.js @@ -1,2 +1,2 @@ -'use strict' -module.exports = process.electronBinding('content_tracing') +'use strict'; +module.exports = process.electronBinding('content_tracing'); diff --git a/lib/browser/api/crash-reporter.js b/lib/browser/api/crash-reporter.js index d5f8f8480dd81..cb27917661631 100644 --- a/lib/browser/api/crash-reporter.js +++ b/lib/browser/api/crash-reporter.js @@ -1,12 +1,12 @@ -'use strict' +'use strict'; -const CrashReporter = require('@electron/internal/common/crash-reporter') -const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init') +const CrashReporter = require('@electron/internal/common/crash-reporter'); +const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init'); class CrashReporterMain extends CrashReporter { init (options) { - return crashReporterInit(options) + return crashReporterInit(options); } } -module.exports = new CrashReporterMain() +module.exports = new CrashReporterMain(); diff --git a/lib/browser/api/dialog.js b/lib/browser/api/dialog.js index fbe864528b894..24285704843af 100644 --- a/lib/browser/api/dialog.js +++ b/lib/browser/api/dialog.js @@ -1,13 +1,13 @@ -'use strict' +'use strict'; -const { app, BrowserWindow, deprecate } = require('electron') -const binding = process.electronBinding('dialog') -const v8Util = process.electronBinding('v8_util') +const { app, BrowserWindow, deprecate } = require('electron'); +const binding = process.electronBinding('dialog'); +const v8Util = process.electronBinding('v8_util'); const DialogType = { OPEN: 'OPEN', SAVE: 'SAVE' -} +}; const saveFileDialogProperties = { createDirectory: 1 << 0, @@ -15,7 +15,7 @@ const saveFileDialogProperties = { treatPackageAsDirectory: 1 << 2, showOverwriteConfirmation: 1 << 3, dontAddToRecent: 1 << 4 -} +}; const openFileDialogProperties = { openFile: 1 << 0, @@ -27,15 +27,15 @@ const openFileDialogProperties = { noResolveAliases: 1 << 6, // macOS treatPackageAsDirectory: 1 << 7, // macOS dontAddToRecent: 1 << 8 // Windows -} +}; const normalizeAccessKey = (text) => { - if (typeof text !== 'string') return text + if (typeof text !== 'string') return text; // macOS does not have access keys so remove single ampersands // and replace double ampersands with a single ampersand if (process.platform === 'darwin') { - return text.replace(/&(&?)/g, '$1') + return text.replace(/&(&?)/g, '$1'); } // Linux uses a single underscore as an access key prefix so escape @@ -44,41 +44,41 @@ const normalizeAccessKey = (text) => { // a single underscore if (process.platform === 'linux') { return text.replace(/_/g, '__').replace(/&(.?)/g, (match, after) => { - if (after === '&') return after - return `_${after}` - }) + if (after === '&') return after; + return `_${after}`; + }); } - return text -} + return text; +}; const checkAppInitialized = function () { if (!app.isReady()) { - throw new Error('dialog module can only be used after app is ready') + throw new Error('dialog module can only be used after app is ready'); } -} +}; const setupDialogProperties = (type, properties) => { - const dialogPropertiesTypes = (type === DialogType.OPEN) ? openFileDialogProperties : saveFileDialogProperties - let dialogProperties = 0 + const dialogPropertiesTypes = (type === DialogType.OPEN) ? openFileDialogProperties : saveFileDialogProperties; + let dialogProperties = 0; for (const prop in dialogPropertiesTypes) { if (properties.includes(prop)) { - dialogProperties |= dialogPropertiesTypes[prop] + dialogProperties |= dialogPropertiesTypes[prop]; } } - return dialogProperties -} + return dialogProperties; +}; const saveDialog = (sync, window, options) => { - checkAppInitialized() + checkAppInitialized(); if (window && window.constructor !== BrowserWindow) { - options = window - window = null + options = window; + window = null; } - if (options == null) options = { title: 'Save' } + if (options == null) options = { title: 'Save' }; const { buttonLabel = '', @@ -90,33 +90,33 @@ const saveDialog = (sync, window, options) => { securityScopedBookmarks = false, nameFieldLabel = '', showsTagField = true - } = options + } = options; - if (typeof title !== 'string') throw new TypeError('Title must be a string') - if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string') - if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string') - if (typeof message !== 'string') throw new TypeError('Message must be a string') - if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string') + if (typeof title !== 'string') throw new TypeError('Title must be a string'); + if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string'); + if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string'); + if (typeof message !== 'string') throw new TypeError('Message must be a string'); + if (typeof nameFieldLabel !== 'string') throw new TypeError('Name field label must be a string'); - const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window } - settings.properties = setupDialogProperties(DialogType.SAVE, properties) + const settings = { buttonLabel, defaultPath, filters, title, message, securityScopedBookmarks, nameFieldLabel, showsTagField, window }; + settings.properties = setupDialogProperties(DialogType.SAVE, properties); - return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings) -} + return (sync) ? binding.showSaveDialogSync(settings) : binding.showSaveDialog(settings); +}; const openDialog = (sync, window, options) => { - checkAppInitialized() + checkAppInitialized(); if (window && window.constructor !== BrowserWindow) { - options = window - window = null + options = window; + window = null; } if (options == null) { options = { title: 'Open', properties: ['openFile'] - } + }; } const { @@ -127,33 +127,33 @@ const openDialog = (sync, window, options) => { title = '', message = '', securityScopedBookmarks = false - } = options + } = options; - if (!Array.isArray(properties)) throw new TypeError('Properties must be an array') + if (!Array.isArray(properties)) throw new TypeError('Properties must be an array'); - if (typeof title !== 'string') throw new TypeError('Title must be a string') - if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string') - if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string') - if (typeof message !== 'string') throw new TypeError('Message must be a string') + if (typeof title !== 'string') throw new TypeError('Title must be a string'); + if (typeof buttonLabel !== 'string') throw new TypeError('Button label must be a string'); + if (typeof defaultPath !== 'string') throw new TypeError('Default path must be a string'); + if (typeof message !== 'string') throw new TypeError('Message must be a string'); - const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window } - settings.properties = setupDialogProperties(DialogType.OPEN, properties) + const settings = { title, buttonLabel, defaultPath, filters, message, securityScopedBookmarks, window }; + settings.properties = setupDialogProperties(DialogType.OPEN, properties); - return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings) -} + return (sync) ? binding.showOpenDialogSync(settings) : binding.showOpenDialog(settings); +}; const messageBox = (sync, window, options) => { - checkAppInitialized() + checkAppInitialized(); if (window && window.constructor !== BrowserWindow) { - options = window - window = null + options = window; + window = null; } - if (options == null) options = { type: 'none' } + if (options == null) options = { type: 'none' }; - const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question'] - const messageBoxOptions = { noLink: 1 << 0 } + const messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']; + const messageBoxOptions = { noLink: 1 << 0 }; let { buttons = [], @@ -163,101 +163,104 @@ const messageBox = (sync, window, options) => { defaultId = -1, detail = '', icon = null, + noLink = false, message = '', title = '', type = 'none' - } = options - - const messageBoxType = messageBoxTypes.indexOf(type) - if (messageBoxType === -1) throw new TypeError('Invalid message box type') - if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array') - if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey) - if (typeof title !== 'string') throw new TypeError('Title must be a string') - if (typeof message !== 'string') throw new TypeError('Message must be a string') - if (typeof detail !== 'string') throw new TypeError('Detail must be a string') - if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string') - - checkboxChecked = !!checkboxChecked + } = options; + + const messageBoxType = messageBoxTypes.indexOf(type); + if (messageBoxType === -1) throw new TypeError('Invalid message box type'); + if (!Array.isArray(buttons)) throw new TypeError('Buttons must be an array'); + if (options.normalizeAccessKeys) buttons = buttons.map(normalizeAccessKey); + if (typeof title !== 'string') throw new TypeError('Title must be a string'); + if (typeof noLink !== 'boolean') throw new TypeError('noLink must be a boolean'); + if (typeof message !== 'string') throw new TypeError('Message must be a string'); + if (typeof detail !== 'string') throw new TypeError('Detail must be a string'); + if (typeof checkboxLabel !== 'string') throw new TypeError('checkboxLabel must be a string'); + + checkboxChecked = !!checkboxChecked; + if (checkboxChecked && !checkboxLabel) { + throw new Error('checkboxChecked requires that checkboxLabel also be passed'); + } // Choose a default button to get selected when dialog is cancelled. if (cancelId == null) { // If the defaultId is set to 0, ensure the cancel button is a different index (1) - cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0 + cancelId = (defaultId === 0 && buttons.length > 1) ? 1 : 0; for (let i = 0; i < buttons.length; i++) { - const text = buttons[i].toLowerCase() + const text = buttons[i].toLowerCase(); if (text === 'cancel' || text === 'no') { - cancelId = i - break + cancelId = i; + break; } } } - const flags = options.noLink ? messageBoxOptions.noLink : 0 - const settings = { window, messageBoxType, buttons, defaultId, cancelId, - flags, + noLink, title, message, detail, checkboxLabel, checkboxChecked, icon - } + }; if (sync) { - return binding.showMessageBoxSync(settings) + return binding.showMessageBoxSync(settings); } else { - return binding.showMessageBox(settings) + return binding.showMessageBox(settings); } -} +}; module.exports = { showOpenDialog: function (window, options) { - return openDialog(false, window, options) + return openDialog(false, window, options); }, showOpenDialogSync: function (window, options) { - return openDialog(true, window, options) + return openDialog(true, window, options); }, showSaveDialog: function (window, options) { - return saveDialog(false, window, options) + return saveDialog(false, window, options); }, showSaveDialogSync: function (window, options) { - return saveDialog(true, window, options) + return saveDialog(true, window, options); }, showMessageBox: function (window, options) { - return messageBox(false, window, options) + return messageBox(false, window, options); }, showMessageBoxSync: function (window, options) { - return messageBox(true, window, options) + return messageBox(true, window, options); }, showErrorBox: function (...args) { - return binding.showErrorBox(...args) + return binding.showErrorBox(...args); }, showCertificateTrustDialog: function (window, options) { - if (window && window.constructor !== BrowserWindow) options = window + if (window && window.constructor !== BrowserWindow) options = window; if (options == null || typeof options !== 'object') { - throw new TypeError('options must be an object') + throw new TypeError('options must be an object'); } - const { certificate, message = '' } = options + const { certificate, message = '' } = options; if (certificate == null || typeof certificate !== 'object') { - throw new TypeError('certificate must be an object') + throw new TypeError('certificate must be an object'); } - if (typeof message !== 'string') throw new TypeError('message must be a string') + if (typeof message !== 'string') throw new TypeError('message must be a string'); - return binding.showCertificateTrustDialog(window, certificate, message) + return binding.showCertificateTrustDialog(window, certificate, message); } -} +}; diff --git a/lib/browser/api/exports/electron.ts b/lib/browser/api/exports/electron.ts index 49d8309f0a5fe..904de54f69836 100644 --- a/lib/browser/api/exports/electron.ts +++ b/lib/browser/api/exports/electron.ts @@ -1,6 +1,8 @@ -import { defineProperties } from '@electron/internal/common/define-properties' -import { commonModuleList } from '@electron/internal/common/api/module-list' -import { browserModuleList } from '@electron/internal/browser/api/module-list' +import { defineProperties } from '@electron/internal/common/define-properties'; +import { commonModuleList } from '@electron/internal/common/api/module-list'; +import { browserModuleList } from '@electron/internal/browser/api/module-list'; -defineProperties(exports, commonModuleList) -defineProperties(exports, browserModuleList) +module.exports = {}; + +defineProperties(module.exports, commonModuleList); +defineProperties(module.exports, browserModuleList); diff --git a/lib/browser/api/global-shortcut.js b/lib/browser/api/global-shortcut.js index f1c6e21a34023..b43431a0775fc 100644 --- a/lib/browser/api/global-shortcut.js +++ b/lib/browser/api/global-shortcut.js @@ -1,3 +1,3 @@ -'use strict' +'use strict'; -module.exports = process.electronBinding('global_shortcut').globalShortcut +module.exports = process.electronBinding('global_shortcut').globalShortcut; diff --git a/lib/browser/api/in-app-purchase.js b/lib/browser/api/in-app-purchase.js index 3efde208738dc..77243f1a7f2b6 100644 --- a/lib/browser/api/in-app-purchase.js +++ b/lib/browser/api/in-app-purchase.js @@ -1,22 +1,22 @@ -'use strict' +'use strict'; -const { deprecate } = require('electron') +const { deprecate } = require('electron'); if (process.platform === 'darwin') { - const { EventEmitter } = require('events') - const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase') + const { EventEmitter } = require('events'); + const { inAppPurchase, InAppPurchase } = process.electronBinding('in_app_purchase'); // inAppPurchase is an EventEmitter. - Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype) - EventEmitter.call(inAppPurchase) + Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype); + EventEmitter.call(inAppPurchase); - module.exports = inAppPurchase + module.exports = inAppPurchase; } else { module.exports = { purchaseProduct: (productID, quantity, callback) => { - throw new Error('The inAppPurchase module can only be used on macOS') + throw new Error('The inAppPurchase module can only be used on macOS'); }, canMakePayments: () => false, getReceiptURL: () => '' - } + }; } diff --git a/lib/browser/api/ipc-main.ts b/lib/browser/api/ipc-main.ts index c71660d45388f..20c7fb9dc5cf6 100644 --- a/lib/browser/api/ipc-main.ts +++ b/lib/browser/api/ipc-main.ts @@ -1,8 +1,8 @@ -import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl' +import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl'; -const ipcMain = new IpcMainImpl() +const ipcMain = new IpcMainImpl(); // Do not throw exception when channel name is "error". -ipcMain.on('error', () => {}) +ipcMain.on('error', () => {}); -export default ipcMain +export default ipcMain; diff --git a/lib/browser/api/menu-item-roles.js b/lib/browser/api/menu-item-roles.js index 53bb332e64ba7..efc9fe7c9a981 100644 --- a/lib/browser/api/menu-item-roles.js +++ b/lib/browser/api/menu-item-roles.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const { app } = require('electron') +const { app } = require('electron'); -const isMac = process.platform === 'darwin' -const isWindows = process.platform === 'win32' -const isLinux = process.platform === 'linux' +const isMac = process.platform === 'darwin'; +const isWindows = process.platform === 'win32'; +const isLinux = process.platform === 'linux'; const roles = { about: { get label () { - return isLinux ? 'About' : `About ${app.name}` + return isLinux ? 'About' : `About ${app.name}`; } }, close: { @@ -38,7 +38,7 @@ const roles = { accelerator: 'Shift+CmdOrCtrl+R', nonNativeMacOSRole: true, windowMethod: (window) => { - window.webContents.reloadIgnoringCache() + window.webContents.reloadIgnoringCache(); } }, front: { @@ -49,7 +49,7 @@ const roles = { }, hide: { get label () { - return `Hide ${app.name}` + return `Hide ${app.name}`; }, accelerator: 'Command+H' }, @@ -77,9 +77,9 @@ const roles = { quit: { get label () { switch (process.platform) { - case 'darwin': return `Quit ${app.name}` - case 'win32': return 'Exit' - default: return 'Quit' + case 'darwin': return `Quit ${app.name}`; + case 'win32': return 'Exit'; + default: return 'Quit'; } }, accelerator: isWindows ? undefined : 'CommandOrControl+Q', @@ -101,7 +101,7 @@ const roles = { accelerator: 'CommandOrControl+0', nonNativeMacOSRole: true, webContentsMethod: (webContents) => { - webContents.zoomLevel = 0 + webContents.zoomLevel = 0; } }, selectall: { @@ -134,7 +134,7 @@ const roles = { label: 'Toggle Full Screen', accelerator: isMac ? 'Control+Command+F' : 'F11', windowMethod: (window) => { - window.setFullScreen(!window.isFullScreen()) + window.setFullScreen(!window.isFullScreen()); } }, undo: { @@ -156,7 +156,7 @@ const roles = { accelerator: 'CommandOrControl+Plus', nonNativeMacOSRole: true, webContentsMethod: (webContents) => { - webContents.zoomLevel += 0.5 + webContents.zoomLevel += 0.5; } }, zoomout: { @@ -164,13 +164,13 @@ const roles = { accelerator: 'CommandOrControl+-', nonNativeMacOSRole: true, webContentsMethod: (webContents) => { - webContents.zoomLevel -= 0.5 + webContents.zoomLevel -= 0.5; } }, // App submenu should be used for Mac only appmenu: { get label () { - return app.name + return app.name; }, submenu: [ { role: 'about' }, @@ -249,71 +249,71 @@ const roles = { ]) ] } -} +}; -exports.roleList = roles +exports.roleList = roles; const canExecuteRole = (role) => { - if (!roles.hasOwnProperty(role)) return false - if (!isMac) return true + if (!roles.hasOwnProperty(role)) return false; + if (!isMac) return true; // macOS handles all roles natively except for a few - return roles[role].nonNativeMacOSRole -} + return roles[role].nonNativeMacOSRole; +}; exports.getDefaultLabel = (role) => { - return roles.hasOwnProperty(role) ? roles[role].label : '' -} + return roles.hasOwnProperty(role) ? roles[role].label : ''; +}; exports.getDefaultAccelerator = (role) => { - if (roles.hasOwnProperty(role)) return roles[role].accelerator -} + if (roles.hasOwnProperty(role)) return roles[role].accelerator; +}; exports.shouldRegisterAccelerator = (role) => { - const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined - return hasRoleRegister ? roles[role].registerAccelerator : true -} + const hasRoleRegister = roles.hasOwnProperty(role) && roles[role].registerAccelerator !== undefined; + return hasRoleRegister ? roles[role].registerAccelerator : true; +}; exports.getDefaultSubmenu = (role) => { - if (!roles.hasOwnProperty(role)) return + if (!roles.hasOwnProperty(role)) return; - let { submenu } = roles[role] + let { submenu } = roles[role]; // remove null items from within the submenu if (Array.isArray(submenu)) { - submenu = submenu.filter((item) => item != null) + submenu = submenu.filter((item) => item != null); } - return submenu -} + return submenu; +}; exports.execute = (role, focusedWindow, focusedWebContents) => { - if (!canExecuteRole(role)) return false + if (!canExecuteRole(role)) return false; - const { appMethod, webContentsMethod, windowMethod } = roles[role] + const { appMethod, webContentsMethod, windowMethod } = roles[role]; if (appMethod) { - app[appMethod]() - return true + app[appMethod](); + return true; } if (windowMethod && focusedWindow != null) { if (typeof windowMethod === 'function') { - windowMethod(focusedWindow) + windowMethod(focusedWindow); } else { - focusedWindow[windowMethod]() + focusedWindow[windowMethod](); } - return true + return true; } if (webContentsMethod && focusedWebContents != null) { if (typeof webContentsMethod === 'function') { - webContentsMethod(focusedWebContents) + webContentsMethod(focusedWebContents); } else { - focusedWebContents[webContentsMethod]() + focusedWebContents[webContentsMethod](); } - return true + return true; } - return false -} + return false; +}; diff --git a/lib/browser/api/menu-item.js b/lib/browser/api/menu-item.js index 211879ec673f4..171f2ead068e8 100644 --- a/lib/browser/api/menu-item.js +++ b/lib/browser/api/menu-item.js @@ -1,87 +1,87 @@ -'use strict' +'use strict'; -const roles = require('@electron/internal/browser/api/menu-item-roles') +const roles = require('@electron/internal/browser/api/menu-item-roles'); -let nextCommandId = 0 +let nextCommandId = 0; const MenuItem = function (options) { - const { Menu } = require('electron') + const { Menu } = require('electron'); // Preserve extra fields specified by user for (const key in options) { - if (!(key in this)) this[key] = options[key] + if (!(key in this)) this[key] = options[key]; } if (typeof this.role === 'string' || this.role instanceof String) { - this.role = this.role.toLowerCase() + this.role = this.role.toLowerCase(); } - this.submenu = this.submenu || roles.getDefaultSubmenu(this.role) + this.submenu = this.submenu || roles.getDefaultSubmenu(this.role); if (this.submenu != null && this.submenu.constructor !== Menu) { - this.submenu = Menu.buildFromTemplate(this.submenu) + this.submenu = Menu.buildFromTemplate(this.submenu); } if (this.type == null && this.submenu != null) { - this.type = 'submenu' + this.type = 'submenu'; } if (this.type === 'submenu' && (this.submenu == null || this.submenu.constructor !== Menu)) { - throw new Error('Invalid submenu') + throw new Error('Invalid submenu'); } - this.overrideReadOnlyProperty('type', 'normal') - this.overrideReadOnlyProperty('role') - this.overrideReadOnlyProperty('accelerator') - this.overrideReadOnlyProperty('icon') - this.overrideReadOnlyProperty('submenu') + this.overrideReadOnlyProperty('type', 'normal'); + this.overrideReadOnlyProperty('role'); + this.overrideReadOnlyProperty('accelerator'); + this.overrideReadOnlyProperty('icon'); + this.overrideReadOnlyProperty('submenu'); - this.overrideProperty('label', roles.getDefaultLabel(this.role)) - this.overrideProperty('sublabel', '') - this.overrideProperty('toolTip', '') - this.overrideProperty('enabled', true) - this.overrideProperty('visible', true) - this.overrideProperty('checked', false) - this.overrideProperty('acceleratorWorksWhenHidden', true) - this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role)) + this.overrideProperty('label', roles.getDefaultLabel(this.role)); + this.overrideProperty('sublabel', ''); + this.overrideProperty('toolTip', ''); + this.overrideProperty('enabled', true); + this.overrideProperty('visible', true); + this.overrideProperty('checked', false); + this.overrideProperty('acceleratorWorksWhenHidden', true); + this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role)); if (!MenuItem.types.includes(this.type)) { - throw new Error(`Unknown menu item type: ${this.type}`) + throw new Error(`Unknown menu item type: ${this.type}`); } - this.overrideReadOnlyProperty('commandId', ++nextCommandId) + this.overrideReadOnlyProperty('commandId', ++nextCommandId); - const click = options.click + const click = options.click; this.click = (event, focusedWindow, focusedWebContents) => { // Manually flip the checked flags when clicked. if (this.type === 'checkbox' || this.type === 'radio') { - this.checked = !this.checked + this.checked = !this.checked; } if (!roles.execute(this.role, focusedWindow, focusedWebContents)) { if (typeof click === 'function') { - click(this, focusedWindow, event) + click(this, focusedWindow, event); } else if (typeof this.selector === 'string' && process.platform === 'darwin') { - Menu.sendActionToFirstResponder(this.selector) + Menu.sendActionToFirstResponder(this.selector); } } - } -} + }; +}; -MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'] +MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']; MenuItem.prototype.getDefaultRoleAccelerator = function () { - return roles.getDefaultAccelerator(this.role) -} + return roles.getDefaultAccelerator(this.role); +}; MenuItem.prototype.overrideProperty = function (name, defaultValue = null) { if (this[name] == null) { - this[name] = defaultValue + this[name] = defaultValue; } -} +}; MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) { - this.overrideProperty(name, defaultValue) + this.overrideProperty(name, defaultValue); Object.defineProperty(this, name, { enumerable: true, writable: false, value: this[name] - }) -} + }); +}; -module.exports = MenuItem +module.exports = MenuItem; diff --git a/lib/browser/api/menu-utils.js b/lib/browser/api/menu-utils.js index 42636bf7da504..493f04c19c7b0 100644 --- a/lib/browser/api/menu-utils.js +++ b/lib/browser/api/menu-utils.js @@ -1,41 +1,41 @@ -'use strict' +'use strict'; function splitArray (arr, predicate) { const result = arr.reduce((multi, item) => { - const current = multi[multi.length - 1] + const current = multi[multi.length - 1]; if (predicate(item)) { - if (current.length > 0) multi.push([]) + if (current.length > 0) multi.push([]); } else { - current.push(item) + current.push(item); } - return multi - }, [[]]) + return multi; + }, [[]]); if (result[result.length - 1].length === 0) { - return result.slice(0, result.length - 1) + return result.slice(0, result.length - 1); } - return result + return result; } function joinArrays (arrays, joinIDs) { return arrays.reduce((joined, arr, i) => { if (i > 0 && arr.length) { if (joinIDs.length > 0) { - joined.push(joinIDs[0]) - joinIDs.splice(0, 1) + joined.push(joinIDs[0]); + joinIDs.splice(0, 1); } else { - joined.push({ type: 'separator' }) + joined.push({ type: 'separator' }); } } - return joined.concat(arr) - }, []) + return joined.concat(arr); + }, []); } function pushOntoMultiMap (map, key, value) { if (!map.has(key)) { - map.set(key, []) + map.set(key, []); } - map.get(key).push(value) + map.get(key).push(value); } function indexOfGroupContainingID (groups, id, ignoreGroup) { @@ -45,102 +45,102 @@ function indexOfGroupContainingID (groups, id, ignoreGroup) { candidateGroup.some( candidateItem => candidateItem.id === id ) - ) + ); } // Sort nodes topologically using a depth-first approach. Encountered cycles // are broken. function sortTopologically (originalOrder, edgesById) { - const sorted = [] - const marked = new Set() + const sorted = []; + const marked = new Set(); const visit = (mark) => { - if (marked.has(mark)) return - marked.add(mark) - const edges = edgesById.get(mark) + if (marked.has(mark)) return; + marked.add(mark); + const edges = edgesById.get(mark); if (edges != null) { - edges.forEach(visit) + edges.forEach(visit); } - sorted.push(mark) - } + sorted.push(mark); + }; - originalOrder.forEach(visit) - return sorted + originalOrder.forEach(visit); + return sorted; } function attemptToMergeAGroup (groups) { for (let i = 0; i < groups.length; i++) { - const group = groups[i] + const group = groups[i]; for (const item of group) { - const toIDs = [...(item.before || []), ...(item.after || [])] + const toIDs = [...(item.before || []), ...(item.after || [])]; for (const id of toIDs) { - const index = indexOfGroupContainingID(groups, id, group) - if (index === -1) continue - const mergeTarget = groups[index] + const index = indexOfGroupContainingID(groups, id, group); + if (index === -1) continue; + const mergeTarget = groups[index]; - mergeTarget.push(...group) - groups.splice(i, 1) - return true + mergeTarget.push(...group); + groups.splice(i, 1); + return true; } } } - return false + return false; } function mergeGroups (groups) { - let merged = true + let merged = true; while (merged) { - merged = attemptToMergeAGroup(groups) + merged = attemptToMergeAGroup(groups); } - return groups + return groups; } function sortItemsInGroup (group) { - const originalOrder = group.map((node, i) => i) - const edges = new Map() - const idToIndex = new Map(group.map((item, i) => [item.id, i])) + const originalOrder = group.map((node, i) => i); + const edges = new Map(); + const idToIndex = new Map(group.map((item, i) => [item.id, i])); group.forEach((item, i) => { if (item.before) { item.before.forEach(toID => { - const to = idToIndex.get(toID) + const to = idToIndex.get(toID); if (to != null) { - pushOntoMultiMap(edges, to, i) + pushOntoMultiMap(edges, to, i); } - }) + }); } if (item.after) { item.after.forEach(toID => { - const to = idToIndex.get(toID) + const to = idToIndex.get(toID); if (to != null) { - pushOntoMultiMap(edges, i, to) + pushOntoMultiMap(edges, i, to); } - }) + }); } - }) + }); - const sortedNodes = sortTopologically(originalOrder, edges) - return sortedNodes.map(i => group[i]) + const sortedNodes = sortTopologically(originalOrder, edges); + return sortedNodes.map(i => group[i]); } function findEdgesInGroup (groups, i, edges) { - const group = groups[i] + const group = groups[i]; for (const item of group) { if (item.beforeGroupContaining) { for (const id of item.beforeGroupContaining) { - const to = indexOfGroupContainingID(groups, id, group) + const to = indexOfGroupContainingID(groups, id, group); if (to !== -1) { - pushOntoMultiMap(edges, to, i) - return + pushOntoMultiMap(edges, to, i); + return; } } } if (item.afterGroupContaining) { for (const id of item.afterGroupContaining) { - const to = indexOfGroupContainingID(groups, id, group) + const to = indexOfGroupContainingID(groups, id, group); if (to !== -1) { - pushOntoMultiMap(edges, i, to) - return + pushOntoMultiMap(edges, i, to); + return; } } } @@ -148,29 +148,29 @@ function findEdgesInGroup (groups, i, edges) { } function sortGroups (groups) { - const originalOrder = groups.map((item, i) => i) - const edges = new Map() + const originalOrder = groups.map((item, i) => i); + const edges = new Map(); for (let i = 0; i < groups.length; i++) { - findEdgesInGroup(groups, i, edges) + findEdgesInGroup(groups, i, edges); } - const sortedGroupIndexes = sortTopologically(originalOrder, edges) - return sortedGroupIndexes.map(i => groups[i]) + const sortedGroupIndexes = sortTopologically(originalOrder, edges); + return sortedGroupIndexes.map(i => groups[i]); } function sortMenuItems (menuItems) { - const isSeparator = (item) => item.type === 'separator' - const separators = menuItems.filter(i => i.type === 'separator') + const isSeparator = (item) => item.type === 'separator'; + const separators = menuItems.filter(i => i.type === 'separator'); // Split the items into their implicit groups based upon separators. - const groups = splitArray(menuItems, isSeparator) - const mergedGroups = mergeGroups(groups) - const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup) - const sortedGroups = sortGroups(mergedGroupsWithSortedItems) + const groups = splitArray(menuItems, isSeparator); + const mergedGroups = mergeGroups(groups); + const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup); + const sortedGroups = sortGroups(mergedGroupsWithSortedItems); - const joined = joinArrays(sortedGroups, separators) - return joined + const joined = joinArrays(sortedGroups, separators); + return joined; } -module.exports = { sortMenuItems } +module.exports = { sortMenuItems }; diff --git a/lib/browser/api/menu.js b/lib/browser/api/menu.js index 1587c8ce9fa69..68f21856b1527 100644 --- a/lib/browser/api/menu.js +++ b/lib/browser/api/menu.js @@ -1,16 +1,16 @@ -'use strict' +'use strict'; -const { TopLevelWindow, MenuItem, webContents } = require('electron') -const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils') -const EventEmitter = require('events').EventEmitter -const v8Util = process.electronBinding('v8_util') -const bindings = process.electronBinding('menu') +const { TopLevelWindow, MenuItem, webContents } = require('electron'); +const { sortMenuItems } = require('@electron/internal/browser/api/menu-utils'); +const EventEmitter = require('events').EventEmitter; +const v8Util = process.electronBinding('v8_util'); +const bindings = process.electronBinding('menu'); -const { Menu } = bindings -let applicationMenu = null -let groupIdIndex = 0 +const { Menu } = bindings; +let applicationMenu = null; +let groupIdIndex = 0; -Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype) +Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype); // Menu Delegate. // This object should hold no reference to |Menu| to avoid cyclic reference. @@ -20,172 +20,172 @@ const delegate = { shouldCommandIdWorkWhenHidden: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].acceleratorWorksWhenHidden : undefined, isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined, getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => { - const command = menu.commandsMap[id] - if (!command) return - if (command.accelerator != null) return command.accelerator - if (useDefaultAccelerator) return command.getDefaultRoleAccelerator() + const command = menu.commandsMap[id]; + if (!command) return; + if (command.accelerator != null) return command.accelerator; + if (useDefaultAccelerator) return command.getDefaultRoleAccelerator(); }, shouldRegisterAcceleratorForCommandId: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].registerAccelerator : undefined, executeCommand: (menu, event, id) => { - const command = menu.commandsMap[id] - if (!command) return - command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents()) + const command = menu.commandsMap[id]; + if (!command) return; + command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents()); }, menuWillShow: (menu) => { // Ensure radio groups have at least one menu item selected - for (const id in menu.groupsMap) { - const found = menu.groupsMap[id].find(item => item.checked) || null - if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true) + for (const id of Object.keys(menu.groupsMap)) { + const found = menu.groupsMap[id].find(item => item.checked) || null; + if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true); } } -} +}; /* Instance Methods */ Menu.prototype._init = function () { - this.commandsMap = {} - this.groupsMap = {} - this.items = [] - this.delegate = delegate -} + this.commandsMap = {}; + this.groupsMap = {}; + this.items = []; + this.delegate = delegate; +}; Menu.prototype.popup = function (options = {}) { if (options == null || typeof options !== 'object') { - throw new TypeError('Options must be an object') + throw new TypeError('Options must be an object'); } - let { window, x, y, positioningItem, callback } = options + let { window, x, y, positioningItem, callback } = options; // no callback passed - if (!callback || typeof callback !== 'function') callback = () => {} + if (!callback || typeof callback !== 'function') callback = () => {}; // set defaults - if (typeof x !== 'number') x = -1 - if (typeof y !== 'number') y = -1 - if (typeof positioningItem !== 'number') positioningItem = -1 + if (typeof x !== 'number') x = -1; + if (typeof y !== 'number') y = -1; + if (typeof positioningItem !== 'number') positioningItem = -1; // find which window to use - const wins = TopLevelWindow.getAllWindows() + const wins = TopLevelWindow.getAllWindows(); if (!wins || wins.indexOf(window) === -1) { - window = TopLevelWindow.getFocusedWindow() + window = TopLevelWindow.getFocusedWindow(); if (!window && wins && wins.length > 0) { - window = wins[0] + window = wins[0]; } if (!window) { - throw new Error(`Cannot open Menu without a TopLevelWindow present`) + throw new Error(`Cannot open Menu without a TopLevelWindow present`); } } - this.popupAt(window, x, y, positioningItem, callback) - return { browserWindow: window, x, y, position: positioningItem } -} + this.popupAt(window, x, y, positioningItem, callback); + return { browserWindow: window, x, y, position: positioningItem }; +}; Menu.prototype.closePopup = function (window) { if (window instanceof TopLevelWindow) { - this.closePopupAt(window.id) + this.closePopupAt(window.id); } else { // Passing -1 (invalid) would make closePopupAt close the all menu runners // belong to this menu. - this.closePopupAt(-1) + this.closePopupAt(-1); } -} +}; Menu.prototype.getMenuItemById = function (id) { - const items = this.items + const items = this.items; - let found = items.find(item => item.id === id) || null + let found = items.find(item => item.id === id) || null; for (let i = 0; !found && i < items.length; i++) { if (items[i].submenu) { - found = items[i].submenu.getMenuItemById(id) + found = items[i].submenu.getMenuItemById(id); } } - return found -} + return found; +}; Menu.prototype.append = function (item) { - return this.insert(this.getItemCount(), item) -} + return this.insert(this.getItemCount(), item); +}; Menu.prototype.insert = function (pos, item) { if ((item ? item.constructor : void 0) !== MenuItem) { - throw new TypeError('Invalid item') + throw new TypeError('Invalid item'); } if (pos < 0) { - throw new RangeError(`Position ${pos} cannot be less than 0`) + throw new RangeError(`Position ${pos} cannot be less than 0`); } else if (pos > this.getItemCount()) { - throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`) + throw new RangeError(`Position ${pos} cannot be greater than the total MenuItem count`); } // insert item depending on its type - insertItemByType.call(this, item, pos) + insertItemByType.call(this, item, pos); // set item properties - if (item.sublabel) this.setSublabel(pos, item.sublabel) - if (item.toolTip) this.setToolTip(pos, item.toolTip) - if (item.icon) this.setIcon(pos, item.icon) - if (item.role) this.setRole(pos, item.role) + if (item.sublabel) this.setSublabel(pos, item.sublabel); + if (item.toolTip) this.setToolTip(pos, item.toolTip); + if (item.icon) this.setIcon(pos, item.icon); + if (item.role) this.setRole(pos, item.role); // Make menu accessable to items. - item.overrideReadOnlyProperty('menu', this) + item.overrideReadOnlyProperty('menu', this); // Remember the items. - this.items.splice(pos, 0, item) - this.commandsMap[item.commandId] = item -} + this.items.splice(pos, 0, item); + this.commandsMap[item.commandId] = item; +}; Menu.prototype._callMenuWillShow = function () { - if (this.delegate) this.delegate.menuWillShow(this) + if (this.delegate) this.delegate.menuWillShow(this); this.items.forEach(item => { - if (item.submenu) item.submenu._callMenuWillShow() - }) -} + if (item.submenu) item.submenu._callMenuWillShow(); + }); +}; /* Static Methods */ -Menu.getApplicationMenu = () => applicationMenu +Menu.getApplicationMenu = () => applicationMenu; -Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder +Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder; // set application menu with a preexisting menu Menu.setApplicationMenu = function (menu) { if (menu && menu.constructor !== Menu) { - throw new TypeError('Invalid menu') + throw new TypeError('Invalid menu'); } - applicationMenu = menu - v8Util.setHiddenValue(global, 'applicationMenuSet', true) + applicationMenu = menu; + v8Util.setHiddenValue(global, 'applicationMenuSet', true); if (process.platform === 'darwin') { - if (!menu) return - menu._callMenuWillShow() - bindings.setApplicationMenu(menu) + if (!menu) return; + menu._callMenuWillShow(); + bindings.setApplicationMenu(menu); } else { - const windows = TopLevelWindow.getAllWindows() - return windows.map(w => w.setMenu(menu)) + const windows = TopLevelWindow.getAllWindows(); + return windows.map(w => w.setMenu(menu)); } -} +}; Menu.buildFromTemplate = function (template) { if (!Array.isArray(template)) { - throw new TypeError('Invalid template for Menu: Menu template must be an array') + throw new TypeError('Invalid template for Menu: Menu template must be an array'); } if (!areValidTemplateItems(template)) { - throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type') + throw new TypeError('Invalid template for MenuItem: must have at least one of label, role or type'); } - const filtered = removeExtraSeparators(template) - const sorted = sortTemplate(filtered) + const filtered = removeExtraSeparators(template); + const sorted = sortTemplate(filtered); - const menu = new Menu() + const menu = new Menu(); sorted.forEach(item => { if (item instanceof MenuItem) { - menu.append(item) + menu.append(item); } else { - menu.append(new MenuItem(item)) + menu.append(new MenuItem(item)); } - }) + }); - return menu -} + return menu; +}; /* Helper Functions */ @@ -196,51 +196,50 @@ function areValidTemplateItems (template) { typeof item === 'object' && (item.hasOwnProperty('label') || item.hasOwnProperty('role') || - item.type === 'separator')) + item.type === 'separator')); } function sortTemplate (template) { - const sorted = sortMenuItems(template) - for (const id in sorted) { - const item = sorted[id] + const sorted = sortMenuItems(template); + for (const item of sorted) { if (Array.isArray(item.submenu)) { - item.submenu = sortTemplate(item.submenu) + item.submenu = sortTemplate(item.submenu); } } - return sorted + return sorted; } // Search between separators to find a radio menu item and return its group id function generateGroupId (items, pos) { if (pos > 0) { for (let idx = pos - 1; idx >= 0; idx--) { - if (items[idx].type === 'radio') return items[idx].groupId - if (items[idx].type === 'separator') break + if (items[idx].type === 'radio') return items[idx].groupId; + if (items[idx].type === 'separator') break; } } else if (pos < items.length) { for (let idx = pos; idx <= items.length - 1; idx++) { - if (items[idx].type === 'radio') return items[idx].groupId - if (items[idx].type === 'separator') break + if (items[idx].type === 'radio') return items[idx].groupId; + if (items[idx].type === 'separator') break; } } - groupIdIndex += 1 - return groupIdIndex + groupIdIndex += 1; + return groupIdIndex; } function removeExtraSeparators (items) { // fold adjacent separators together let ret = items.filter((e, idx, arr) => { - if (e.visible === false) return true - return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator' - }) + if (e.visible === false) return true; + return e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator'; + }); // remove edge separators ret = ret.filter((e, idx, arr) => { - if (e.visible === false) return true - return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1) - }) + if (e.visible === false) return true; + return e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1); + }); - return ret + return ret; } function insertItemByType (item, pos) { @@ -251,28 +250,28 @@ function insertItemByType (item, pos) { submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu), radio: () => { // Grouping radio menu items - item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos)) + item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos)); if (this.groupsMap[item.groupId] == null) { - this.groupsMap[item.groupId] = [] + this.groupsMap[item.groupId] = []; } - this.groupsMap[item.groupId].push(item) + this.groupsMap[item.groupId].push(item); // Setting a radio menu item should flip other items in the group. - v8Util.setHiddenValue(item, 'checked', item.checked) + v8Util.setHiddenValue(item, 'checked', item.checked); Object.defineProperty(item, 'checked', { enumerable: true, get: () => v8Util.getHiddenValue(item, 'checked'), set: () => { this.groupsMap[item.groupId].forEach(other => { - if (other !== item) v8Util.setHiddenValue(other, 'checked', false) - }) - v8Util.setHiddenValue(item, 'checked', true) + if (other !== item) v8Util.setHiddenValue(other, 'checked', false); + }); + v8Util.setHiddenValue(item, 'checked', true); } - }) - this.insertRadioItem(pos, item.commandId, item.label, item.groupId) + }); + this.insertRadioItem(pos, item.commandId, item.label, item.groupId); } - } - types[item.type]() + }; + types[item.type](); } -module.exports = Menu +module.exports = Menu; diff --git a/lib/browser/api/module-keys.js b/lib/browser/api/module-keys.js index fca9e65b5fdd2..1b6a46eeb8a69 100644 --- a/lib/browser/api/module-keys.js +++ b/lib/browser/api/module-keys.js @@ -1,11 +1,11 @@ -'use strict' +'use strict'; // TODO: Figure out a way to not duplicate this information between here and module-list // It is currently duplicated as module-list "require"s all the browser API file and the // remote module in the renderer process depends on that file. As a result webpack // includes all the browser API files in the renderer process as well and we want to avoid that -const features = process.electronBinding('features') +const features = process.electronBinding('features'); // Browser side modules, please sort alphabetically. module.exports = [ @@ -21,6 +21,7 @@ module.exports = [ { name: 'inAppPurchase' }, { name: 'Menu' }, { name: 'MenuItem' }, + { name: 'nativeTheme' }, { name: 'net' }, { name: 'netLog' }, { name: 'Notification' }, @@ -36,7 +37,7 @@ module.exports = [ { name: 'View' }, { name: 'webContents' }, { name: 'WebContentsView' } -] +]; if (features.isViewApiEnabled()) { module.exports.push( @@ -47,5 +48,5 @@ if (features.isViewApiEnabled()) { { name: 'MdTextButton' }, { name: 'ResizeArea' }, { name: 'TextField' } - ) + ); } diff --git a/lib/browser/api/module-list.ts b/lib/browser/api/module-list.ts index dbcba8a9f0bee..597a35232da8e 100644 --- a/lib/browser/api/module-list.ts +++ b/lib/browser/api/module-list.ts @@ -1,6 +1,6 @@ // TODO: Updating this file also required updating the module-keys file -const features = process.electronBinding('features') +const features = process.electronBinding('features'); // Browser side modules, please sort alphabetically. export const browserModuleList: ElectronInternal.ModuleEntry[] = [ @@ -32,7 +32,7 @@ export const browserModuleList: ElectronInternal.ModuleEntry[] = [ { name: 'View', loader: () => require('./view') }, { name: 'webContents', loader: () => require('./web-contents') }, { name: 'WebContentsView', loader: () => require('./web-contents-view') } -] +]; if (features.isViewApiEnabled()) { browserModuleList.push( @@ -43,5 +43,5 @@ if (features.isViewApiEnabled()) { { name: 'MdTextButton', loader: () => require('./views/md-text-button') }, { name: 'ResizeArea', loader: () => require('./views/resize-area') }, { name: 'TextField', loader: () => require('./views/text-field') } - ) + ); } diff --git a/lib/browser/api/native-theme.ts b/lib/browser/api/native-theme.ts index 78d7e9049bf01..f39a0dd8ee1e7 100644 --- a/lib/browser/api/native-theme.ts +++ b/lib/browser/api/native-theme.ts @@ -1,8 +1,8 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'events'; -const { NativeTheme, nativeTheme } = process.electronBinding('native_theme') +const { NativeTheme, nativeTheme } = process.electronBinding('native_theme'); -Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype) -EventEmitter.call(nativeTheme as any) +Object.setPrototypeOf(NativeTheme.prototype, EventEmitter.prototype); +EventEmitter.call(nativeTheme as any); -module.exports = nativeTheme +module.exports = nativeTheme; diff --git a/lib/browser/api/net-log.js b/lib/browser/api/net-log.js index 0807f571834e8..981fa9bde9b21 100644 --- a/lib/browser/api/net-log.js +++ b/lib/browser/api/net-log.js @@ -1,32 +1,32 @@ -'use strict' +'use strict'; // TODO(deepak1556): Deprecate and remove standalone netLog module, // it is now a property of session module. -const { app, session } = require('electron') +const { app, session } = require('electron'); // Fallback to default session. Object.setPrototypeOf(module.exports, new Proxy({}, { get (target, property) { - if (!app.isReady()) return + if (!app.isReady()) return; - const netLog = session.defaultSession.netLog + const netLog = session.defaultSession.netLog; - if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return + if (!Object.getPrototypeOf(netLog).hasOwnProperty(property)) return; // check for properties on the prototype chain that aren't functions - if (typeof netLog[property] !== 'function') return netLog[property] + if (typeof netLog[property] !== 'function') return netLog[property]; // Returning a native function directly would throw error. - return (...args) => netLog[property](...args) + return (...args) => netLog[property](...args); }, ownKeys () { - if (!app.isReady()) return [] + if (!app.isReady()) return []; - return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog)) + return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.netLog)); }, getOwnPropertyDescriptor (target) { - return { configurable: true, enumerable: true } + return { configurable: true, enumerable: true }; } -})) +})); diff --git a/lib/browser/api/net.js b/lib/browser/api/net.js index 379245b4c9bfc..49af4cd661d42 100644 --- a/lib/browser/api/net.js +++ b/lib/browser/api/net.js @@ -1,20 +1,16 @@ -'use strict' +'use strict'; -const url = require('url') -const { EventEmitter } = require('events') -const { Readable } = require('stream') -const { app } = require('electron') -const { Session } = process.electronBinding('session') -const { net, Net } = process.electronBinding('net') -const { URLRequest } = net +const url = require('url'); +const { EventEmitter } = require('events'); +const { Readable, Writable } = require('stream'); +const { app } = require('electron'); +const { Session } = process.electronBinding('session'); +const { net, Net, _isValidHeaderName, _isValidHeaderValue } = process.electronBinding('net'); +const { URLLoader } = net; -// Net is an EventEmitter. -Object.setPrototypeOf(Net.prototype, EventEmitter.prototype) -EventEmitter.call(net) +Object.setPrototypeOf(URLLoader.prototype, EventEmitter.prototype); -Object.setPrototypeOf(URLRequest.prototype, EventEmitter.prototype) - -const kSupportedProtocols = new Set(['http:', 'https:']) +const kSupportedProtocols = new Set(['http:', 'https:']); // set of headers that Node.js discards duplicates for // see https://nodejs.org/api/http.html#http_message_headers @@ -37,375 +33,446 @@ const discardableDuplicateHeaders = new Set([ 'server', 'age', 'expires' -]) +]); class IncomingMessage extends Readable { - constructor (urlRequest) { - super() - this.urlRequest = urlRequest - this.shouldPush = false - this.data = [] - this.urlRequest.on('data', (event, chunk) => { - this._storeInternalData(chunk) - this._pushInternalData() - }) - this.urlRequest.on('end', () => { - this._storeInternalData(null) - this._pushInternalData() - }) + constructor (responseHead) { + super(); + this._shouldPush = false; + this._data = []; + this._responseHead = responseHead; } get statusCode () { - return this.urlRequest.statusCode + return this._responseHead.statusCode; } get statusMessage () { - return this.urlRequest.statusMessage + return this._responseHead.statusMessage; } get headers () { - const filteredHeaders = {} - const rawHeaders = this.urlRequest.rawResponseHeaders - Object.keys(rawHeaders).forEach(header => { - if (header in filteredHeaders && discardableDuplicateHeaders.has(header)) { + const filteredHeaders = {}; + const { rawHeaders } = this._responseHead; + rawHeaders.forEach(header => { + if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key) && + discardableDuplicateHeaders.has(header.key)) { // do nothing with discardable duplicate headers } else { - if (header === 'set-cookie') { + if (header.key === 'set-cookie') { // keep set-cookie as an array per Node.js rules // see https://nodejs.org/api/http.html#http_message_headers - filteredHeaders[header] = rawHeaders[header] + if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) { + filteredHeaders[header.key].push(header.value); + } else { + filteredHeaders[header.key] = [header.value]; + } } else { // for non-cookie headers, the values are joined together with ', ' - filteredHeaders[header] = rawHeaders[header].join(', ') + if (Object.prototype.hasOwnProperty.call(filteredHeaders, header.key)) { + filteredHeaders[header.key] += `, ${header.value}`; + } else { + filteredHeaders[header.key] = header.value; + } } } - }) - return filteredHeaders + }); + return filteredHeaders; } get httpVersion () { - return `${this.httpVersionMajor}.${this.httpVersionMinor}` + return `${this.httpVersionMajor}.${this.httpVersionMinor}`; } get httpVersionMajor () { - return this.urlRequest.httpVersionMajor + return this._responseHead.httpVersion.major; } get httpVersionMinor () { - return this.urlRequest.httpVersionMinor + return this._responseHead.httpVersion.minor; } get rawTrailers () { - throw new Error('HTTP trailers are not supported') + throw new Error('HTTP trailers are not supported'); } get trailers () { - throw new Error('HTTP trailers are not supported') + throw new Error('HTTP trailers are not supported'); } _storeInternalData (chunk) { - this.data.push(chunk) + this._data.push(chunk); + this._pushInternalData(); } _pushInternalData () { - while (this.shouldPush && this.data.length > 0) { - const chunk = this.data.shift() - this.shouldPush = this.push(chunk) + while (this._shouldPush && this._data.length > 0) { + const chunk = this._data.shift(); + this._shouldPush = this.push(chunk); } } _read () { - this.shouldPush = true - this._pushInternalData() + this._shouldPush = true; + this._pushInternalData(); } } -URLRequest.prototype._emitRequestEvent = function (isAsync, ...rest) { - if (isAsync) { - process.nextTick(() => { - this.clientRequest.emit(...rest) - }) - } else { - this.clientRequest.emit(...rest) +/** Writable stream that buffers up everything written to it. */ +class SlurpStream extends Writable { + constructor () { + super(); + this._data = Buffer.alloc(0); + } + _write (chunk, encoding, callback) { + this._data = Buffer.concat([this._data, chunk]); + callback(); } + data () { return this._data; } } -URLRequest.prototype._emitResponseEvent = function (isAsync, ...rest) { - if (isAsync) { - process.nextTick(() => { - this._response.emit(...rest) - }) - } else { - this._response.emit(...rest) +class ChunkedBodyStream extends Writable { + constructor (clientRequest) { + super(); + this._clientRequest = clientRequest; } -} -class ClientRequest extends EventEmitter { - constructor (options, callback) { - super() + _write (chunk, encoding, callback) { + if (this._downstream) { + this._downstream.write(chunk).then(callback, callback); + } else { + // the contract of _write is that we won't be called again until we call + // the callback, so we're good to just save a single chunk. + this._pendingChunk = chunk; + this._pendingCallback = callback; - if (!app.isReady()) { - throw new Error('net module can only be used after app is ready') + // The first write to a chunked body stream begins the request. + this._clientRequest._startRequest(); } + } - if (typeof options === 'string') { - options = url.parse(options) - } else { - options = Object.assign({}, options) + _final (callback) { + this._downstream.done(); + callback(); + } + + startReading (pipe) { + if (this._downstream) { + throw new Error('two startReading calls???'); + } + this._downstream = pipe; + if (this._pendingChunk) { + const doneWriting = (maybeError) => { + const cb = this._pendingCallback; + delete this._pendingCallback; + delete this._pendingChunk; + cb(maybeError); + }; + this._downstream.write(this._pendingChunk).then(doneWriting, doneWriting); } + } +} - const method = (options.method || 'GET').toUpperCase() - let urlStr = options.url +function parseOptions (options) { + if (typeof options === 'string') { + options = url.parse(options); + } else { + options = { ...options }; + } - if (!urlStr) { - const urlObj = {} - const protocol = options.protocol || 'http:' - if (!kSupportedProtocols.has(protocol)) { - throw new Error('Protocol "' + protocol + '" not supported') - } - urlObj.protocol = protocol + const method = (options.method || 'GET').toUpperCase(); + let urlStr = options.url; - if (options.host) { - urlObj.host = options.host - } else { - if (options.hostname) { - urlObj.hostname = options.hostname - } else { - urlObj.hostname = 'localhost' - } + if (!urlStr) { + const urlObj = {}; + const protocol = options.protocol || 'http:'; + if (!kSupportedProtocols.has(protocol)) { + throw new Error('Protocol "' + protocol + '" not supported'); + } + urlObj.protocol = protocol; - if (options.port) { - urlObj.port = options.port - } + if (options.host) { + urlObj.host = options.host; + } else { + if (options.hostname) { + urlObj.hostname = options.hostname; + } else { + urlObj.hostname = 'localhost'; } - if (options.path && / /.test(options.path)) { - // The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/ - // with an additional rule for ignoring percentage-escaped characters - // but that's a) hard to capture in a regular expression that performs - // well, and b) possibly too restrictive for real-world usage. That's - // why it only scans for spaces because those are guaranteed to create - // an invalid request. - throw new TypeError('Request path contains unescaped characters') + if (options.port) { + urlObj.port = options.port; } - const pathObj = url.parse(options.path || '/') - urlObj.pathname = pathObj.pathname - urlObj.search = pathObj.search - urlObj.hash = pathObj.hash - urlStr = url.format(urlObj) } - const redirectPolicy = options.redirect || 'follow' - if (!['follow', 'error', 'manual'].includes(redirectPolicy)) { - throw new Error('redirect mode should be one of follow, error or manual') + if (options.path && / /.test(options.path)) { + // The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/ + // with an additional rule for ignoring percentage-escaped characters + // but that's a) hard to capture in a regular expression that performs + // well, and b) possibly too restrictive for real-world usage. That's + // why it only scans for spaces because those are guaranteed to create + // an invalid request. + throw new TypeError('Request path contains unescaped characters'); } + const pathObj = url.parse(options.path || '/'); + urlObj.pathname = pathObj.pathname; + urlObj.search = pathObj.search; + urlObj.hash = pathObj.hash; + urlStr = url.format(urlObj); + } - const urlRequestOptions = { - method: method, - url: urlStr, - redirect: redirectPolicy - } - if (options.session) { - if (options.session instanceof Session) { - urlRequestOptions.session = options.session - } else { - throw new TypeError('`session` should be an instance of the Session class') - } - } else if (options.partition) { - if (typeof options.partition === 'string') { - urlRequestOptions.partition = options.partition - } else { - throw new TypeError('`partition` should be a string') - } - } + const redirectPolicy = options.redirect || 'follow'; + if (!['follow', 'error', 'manual'].includes(redirectPolicy)) { + throw new Error('redirect mode should be one of follow, error or manual'); + } - const urlRequest = new URLRequest(urlRequestOptions) + if (options.headers != null && typeof options.headers !== 'object') { + throw new TypeError('headers must be an object'); + } - // Set back and forward links. - this.urlRequest = urlRequest - urlRequest.clientRequest = this + const urlLoaderOptions = { + method: method, + url: urlStr, + redirectPolicy, + extraHeaders: options.headers || {}, + useSessionCookies: options.useSessionCookies || false + }; + for (const [name, value] of Object.entries(urlLoaderOptions.extraHeaders)) { + if (!_isValidHeaderName(name)) { + throw new Error(`Invalid header name: '${name}'`); + } + if (!_isValidHeaderValue(value.toString())) { + throw new Error(`Invalid value for header '${name}': '${value}'`); + } + } + if (options.session) { + if (options.session instanceof Session) { + urlLoaderOptions.session = options.session; + } else { + throw new TypeError('`session` should be an instance of the Session class'); + } + } else if (options.partition) { + if (typeof options.partition === 'string') { + urlLoaderOptions.partition = options.partition; + } else { + throw new TypeError('`partition` should be a string'); + } + } + return urlLoaderOptions; +} - // This is a copy of the extra headers structure held by the native - // net::URLRequest. The main reason is to keep the getHeader API synchronous - // after the request starts. - this.extraHeaders = {} +class ClientRequest extends Writable { + constructor (options, callback) { + super({ autoDestroy: true }); - if (options.headers) { - for (const key in options.headers) { - this.setHeader(key, options.headers[key]) - } + if (!app.isReady()) { + throw new Error('net module can only be used after app is ready'); } - // Set when the request uses chunked encoding. Can be switched - // to true only once and never set back to false. - this.chunkedEncodingEnabled = false - - urlRequest.on('response', () => { - const response = new IncomingMessage(urlRequest) - urlRequest._response = response - this.emit('response', response) - }) - - urlRequest.on('login', (event, authInfo, callback) => { - this.emit('login', authInfo, (username, password) => { - // If null or undefined username/password, force to empty string. - if (username === null || username === undefined) { - username = '' - } - if (typeof username !== 'string') { - throw new Error('username must be a string') - } - if (password === null || password === undefined) { - password = '' - } - if (typeof password !== 'string') { - throw new Error('password must be a string') - } - callback(username, password) - }) - }) - if (callback) { - this.once('response', callback) + this.once('response', callback); } - } - get chunkedEncoding () { - return this.chunkedEncodingEnabled + const { redirectPolicy, ...urlLoaderOptions } = parseOptions(options); + this._urlLoaderOptions = urlLoaderOptions; + this._redirectPolicy = redirectPolicy; + this._started = false; } set chunkedEncoding (value) { - if (!this.urlRequest.notStarted) { - throw new Error('Can\'t set the transfer encoding, headers have been sent') + if (this._started) { + throw new Error('chunkedEncoding can only be set before the request is started'); + } + if (typeof this._chunkedEncoding !== 'undefined') { + throw new Error('chunkedEncoding can only be set once'); + } + this._chunkedEncoding = !!value; + if (this._chunkedEncoding) { + this._body = new ChunkedBodyStream(this); + this._urlLoaderOptions.body = (pipe) => { + this._body.startReading(pipe); + }; } - this.chunkedEncodingEnabled = value } setHeader (name, value) { if (typeof name !== 'string') { - throw new TypeError('`name` should be a string in setHeader(name, value)') + throw new TypeError('`name` should be a string in setHeader(name, value)'); } if (value == null) { - throw new Error('`value` required in setHeader("' + name + '", value)') + throw new Error('`value` required in setHeader("' + name + '", value)'); + } + if (this._started || this._firstWrite) { + throw new Error('Can\'t set headers after they are sent'); } - if (!this.urlRequest.notStarted) { - throw new Error('Can\'t set headers after they are sent') + if (!_isValidHeaderName(name)) { + throw new Error(`Invalid header name: '${name}'`); + } + if (!_isValidHeaderValue(value.toString())) { + throw new Error(`Invalid value for header '${name}': '${value}'`); } - const key = name.toLowerCase() - this.extraHeaders[key] = value - this.urlRequest.setExtraHeader(name, value.toString()) + const key = name.toLowerCase(); + this._urlLoaderOptions.extraHeaders[key] = value; } getHeader (name) { if (name == null) { - throw new Error('`name` is required for getHeader(name)') - } - - if (!this.extraHeaders) { - return + throw new Error('`name` is required for getHeader(name)'); } - const key = name.toLowerCase() - return this.extraHeaders[key] + const key = name.toLowerCase(); + return this._urlLoaderOptions.extraHeaders[key]; } removeHeader (name) { if (name == null) { - throw new Error('`name` is required for removeHeader(name)') + throw new Error('`name` is required for removeHeader(name)'); } - if (!this.urlRequest.notStarted) { - throw new Error('Can\'t remove headers after they are sent') + if (this._started || this._firstWrite) { + throw new Error('Can\'t remove headers after they are sent'); } - const key = name.toLowerCase() - delete this.extraHeaders[key] - this.urlRequest.removeExtraHeader(name) + const key = name.toLowerCase(); + delete this._urlLoaderOptions.extraHeaders[key]; } - _write (chunk, encoding, callback, isLast) { - const chunkIsString = typeof chunk === 'string' - const chunkIsBuffer = chunk instanceof Buffer - if (!chunkIsString && !chunkIsBuffer) { - throw new TypeError('First argument must be a string or Buffer') - } - - if (chunkIsString) { - // We convert all strings into binary buffers. - chunk = Buffer.from(chunk, encoding) - } - - // Since writing to the network is asynchronous, we conservatively - // assume that request headers are written after delivering the first - // buffer to the network IO thread. - if (this.urlRequest.notStarted) { - this.urlRequest.setChunkedUpload(this.chunkedEncoding) - } - - // Headers are assumed to be sent on first call to _writeBuffer, - // i.e. after the first call to write or end. - const result = this.urlRequest.write(chunk, isLast) - - // The write callback is fired asynchronously to mimic Node.js. - if (callback) { - process.nextTick(callback) + _write (chunk, encoding, callback) { + this._firstWrite = true; + if (!this._body) { + this._body = new SlurpStream(); + this._body.on('finish', () => { + this._urlLoaderOptions.body = this._body.data(); + this._startRequest(); + }); } - - return result + // TODO: is this the right way to forward to another stream? + this._body.write(chunk, encoding, callback); } - write (data, encoding, callback) { - if (this.urlRequest.finished) { - const error = new Error('Write after end') - process.nextTick(writeAfterEndNT, this, error, callback) - return true + _final (callback) { + if (this._body) { + // TODO: is this the right way to forward to another stream? + this._body.end(callback); + } else { + // end() called without a body, go ahead and start the request + this._startRequest(); + callback(); } - - return this._write(data, encoding, callback, false) } - end (data, encoding, callback) { - if (this.urlRequest.finished) { - return false - } - - if (typeof data === 'function') { - callback = data - encoding = null - data = null - } else if (typeof encoding === 'function') { - callback = encoding - encoding = null - } + _startRequest () { + this._started = true; + const stringifyValues = (obj) => { + const ret = {}; + for (const k of Object.keys(obj)) { + ret[k] = obj[k].toString(); + } + return ret; + }; + this._urlLoaderOptions.referrer = this._urlLoaderOptions.extraHeaders.referer || ''; + const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) }; + this._urlLoader = new URLLoader(opts); + this._urlLoader.on('response-started', (event, finalUrl, responseHead) => { + const response = this._response = new IncomingMessage(responseHead); + this.emit('response', response); + }); + this._urlLoader.on('data', (event, data) => { + this._response._storeInternalData(Buffer.from(data)); + }); + this._urlLoader.on('complete', () => { + if (this._response) { this._response._storeInternalData(null); } + }); + this._urlLoader.on('error', (event, netErrorString) => { + const error = new Error(netErrorString); + if (this._response) this._response.destroy(error); + this._die(error); + }); + + this._urlLoader.on('login', (event, authInfo, callback) => { + const handled = this.emit('login', authInfo, callback); + if (!handled) { + // If there were no listeners, cancel the authentication request. + callback(); + } + }); + + this._urlLoader.on('redirect', (event, redirectInfo, headers) => { + const { statusCode, newMethod, newUrl } = redirectInfo; + if (this._redirectPolicy === 'error') { + this._die(new Error(`Attempted to redirect, but redirect policy was 'error'`)); + } else if (this._redirectPolicy === 'manual') { + let _followRedirect = false; + this._followRedirectCb = () => { _followRedirect = true; }; + try { + this.emit('redirect', statusCode, newMethod, newUrl, headers); + } finally { + this._followRedirectCb = null; + if (!_followRedirect && !this._aborted) { + this._die(new Error('Redirect was cancelled')); + } + } + } else if (this._redirectPolicy === 'follow') { + // Calling followRedirect() when the redirect policy is 'follow' is + // allowed but does nothing. (Perhaps it should throw an error + // though...? Since the redirect will happen regardless.) + try { + this._followRedirectCb = () => {}; + this.emit('redirect', statusCode, newMethod, newUrl, headers); + } finally { + this._followRedirectCb = null; + } + } else { + this._die(new Error(`Unexpected redirect policy '${this._redirectPolicy}'`)); + } + }); - data = data || '' + this._urlLoader.on('upload-progress', (event, position, total) => { + this._uploadProgress = { active: true, started: true, current: position, total }; + this.emit('upload-progress', position, total); // Undocumented, for now + }); - return this._write(data, encoding, callback, true) + this._urlLoader.on('download-progress', (event, current) => { + if (this._response) { + this._response.emit('download-progress', current); // Undocumented, for now + } + }); } followRedirect () { - this.urlRequest.followRedirect() + if (this._followRedirectCb) { + this._followRedirectCb(); + } else { + throw new Error('followRedirect() called, but was not waiting for a redirect'); + } } abort () { - this.urlRequest.cancel() + if (!this._aborted) { + process.nextTick(() => { this.emit('abort'); }); + } + this._aborted = true; + this._die(); } - getUploadProgress () { - return this.urlRequest.getUploadProgress() + _die (err) { + this.destroy(err); + if (this._urlLoader) { + this._urlLoader.cancel(); + if (this._response) this._response.destroy(err); + } } -} -function writeAfterEndNT (self, error, callback) { - self.emit('error', error) - if (callback) callback(error) + getUploadProgress () { + return this._uploadProgress ? { ...this._uploadProgress } : { active: false }; + } } Net.prototype.request = function (options, callback) { - return new ClientRequest(options, callback) -} + return new ClientRequest(options, callback); +}; -net.ClientRequest = ClientRequest +net.ClientRequest = ClientRequest; -module.exports = net +module.exports = net; diff --git a/lib/browser/api/notification.js b/lib/browser/api/notification.js index b835278b0bfa7..862f2646471a3 100644 --- a/lib/browser/api/notification.js +++ b/lib/browser/api/notification.js @@ -1,10 +1,10 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') -const { Notification, isSupported } = process.electronBinding('notification') +const { EventEmitter } = require('events'); +const { Notification, isSupported } = process.electronBinding('notification'); -Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype) +Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype); -Notification.isSupported = isSupported +Notification.isSupported = isSupported; -module.exports = Notification +module.exports = Notification; diff --git a/lib/browser/api/power-monitor.ts b/lib/browser/api/power-monitor.ts index 795236334d656..89c958d404bdb 100644 --- a/lib/browser/api/power-monitor.ts +++ b/lib/browser/api/power-monitor.ts @@ -1,28 +1,38 @@ -'use strict' +'use strict'; -import { createLazyInstance } from '../utils' +import { createLazyInstance } from '../utils'; -const { EventEmitter } = require('events') -const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor') +const { EventEmitter } = require('events'); +const { createPowerMonitor, PowerMonitor } = process.electronBinding('power_monitor'); // PowerMonitor is an EventEmitter. -Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype) +Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype); -const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true) +const powerMonitor = createLazyInstance(createPowerMonitor, PowerMonitor, true); -// On Linux we need to call blockShutdown() to subscribe to shutdown event. if (process.platform === 'linux') { - powerMonitor.on('newListener', (event:string) => { - if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { - powerMonitor.blockShutdown() - } - }) - - powerMonitor.on('removeListener', (event: string) => { - if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { - powerMonitor.unblockShutdown() - } - }) + // In order to delay system shutdown when e.preventDefault() is invoked + // on a powerMonitor 'shutdown' event, we need an org.freedesktop.login1 + // shutdown delay lock. For more details see the "Taking Delay Locks" + // section of https://www.freedesktop.org/wiki/Software/systemd/inhibit/ + // + // So here we watch for 'shutdown' listeners to be added or removed and + // set or unset our shutdown delay lock accordingly. + const { app } = require('electron'); + app.whenReady().then(() => { + powerMonitor.on('newListener', (event: string) => { + // whenever the listener count is incremented to one... + if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { + powerMonitor.blockShutdown(); + } + }); + powerMonitor.on('removeListener', (event: string) => { + // whenever the listener count is decremented to zero... + if (event === 'shutdown' && powerMonitor.listenerCount('shutdown') === 0) { + powerMonitor.unblockShutdown(); + } + }); + }); } -module.exports = powerMonitor +module.exports = powerMonitor; diff --git a/lib/browser/api/power-save-blocker.js b/lib/browser/api/power-save-blocker.js index 010b934c11010..928a8605cdcfe 100644 --- a/lib/browser/api/power-save-blocker.js +++ b/lib/browser/api/power-save-blocker.js @@ -1,3 +1,3 @@ -'use strict' +'use strict'; -module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker +module.exports = process.electronBinding('power_save_blocker').powerSaveBlocker; diff --git a/lib/browser/api/protocol.ts b/lib/browser/api/protocol.ts index 18387d172f478..45ec2e5efbaca 100644 --- a/lib/browser/api/protocol.ts +++ b/lib/browser/api/protocol.ts @@ -1,29 +1,29 @@ -import { app, session } from 'electron' +import { app, session } from 'electron'; // Global protocol APIs. -const protocol = process.electronBinding('protocol') +const protocol = process.electronBinding('protocol'); // Fallback protocol APIs of default session. Object.setPrototypeOf(protocol, new Proxy({}, { get (_target, property) { - if (!app.isReady()) return + if (!app.isReady()) return; - const protocol = session.defaultSession!.protocol - if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return + const protocol = session.defaultSession!.protocol; + if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return; // Returning a native function directly would throw error. - return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args) + return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args); }, ownKeys () { - if (!app.isReady()) return [] + if (!app.isReady()) return []; - return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol)) + return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession!.protocol)); }, getOwnPropertyDescriptor () { - return { configurable: true, enumerable: true } + return { configurable: true, enumerable: true }; } -})) +})); -export default protocol +export default protocol; diff --git a/lib/browser/api/screen.ts b/lib/browser/api/screen.ts index c0c1ea9587fd7..a07e54aff9259 100644 --- a/lib/browser/api/screen.ts +++ b/lib/browser/api/screen.ts @@ -1,10 +1,10 @@ -'use strict' +'use strict'; -import { createLazyInstance } from '../utils' -const { EventEmitter } = require('events') -const { Screen, createScreen } = process.electronBinding('screen') +import { createLazyInstance } from '../utils'; +const { EventEmitter } = require('events'); +const { Screen, createScreen } = process.electronBinding('screen'); // Screen is an EventEmitter. -Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype) +Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype); -module.exports = createLazyInstance(createScreen, Screen, true) +module.exports = createLazyInstance(createScreen, Screen, true); diff --git a/lib/browser/api/session.js b/lib/browser/api/session.js index 9c05886406029..35ded9d84fa61 100644 --- a/lib/browser/api/session.js +++ b/lib/browser/api/session.js @@ -1,52 +1,53 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') -const { app, deprecate } = require('electron') -const { fromPartition, Session, Cookies, NetLog, Protocol } = process.electronBinding('session') +const { EventEmitter } = require('events'); +const { app, deprecate } = require('electron'); +const { fromPartition, Session, Cookies, NetLog, Protocol } = process.electronBinding('session'); // Public API. Object.defineProperties(exports, { defaultSession: { enumerable: true, - get () { return fromPartition('') } + get () { return fromPartition(''); } }, fromPartition: { enumerable: true, value: fromPartition } -}) +}); -Object.setPrototypeOf(Session.prototype, EventEmitter.prototype) -Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype) +Object.setPrototypeOf(Session.prototype, EventEmitter.prototype); +Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype); Session.prototype._init = function () { - app.emit('session-created', this) -} + app.emit('session-created', this); +}; -const _originalStartLogging = NetLog.prototype.startLogging +const _originalStartLogging = NetLog.prototype.startLogging; NetLog.prototype.startLogging = function (path, ...args) { - this._currentlyLoggingPath = path + this._currentlyLoggingPath = path; try { - return _originalStartLogging.call(this, path, ...args) + return _originalStartLogging.call(this, path, ...args); } catch (e) { - this._currentlyLoggingPath = null - throw e + this._currentlyLoggingPath = null; + throw e; } -} +}; -const _originalStopLogging = NetLog.prototype.stopLogging +const _originalStopLogging = NetLog.prototype.stopLogging; NetLog.prototype.stopLogging = function () { - this._currentlyLoggingPath = null - return _originalStopLogging.call(this) -} + const logPath = this._currentlyLoggingPath; + this._currentlyLoggingPath = null; + return _originalStopLogging.call(this).then(() => logPath); +}; -const currentlyLoggingPathDeprecated = deprecate.warnOnce('currentlyLoggingPath') +const currentlyLoggingPathDeprecated = deprecate.warnOnce('currentlyLoggingPath'); Object.defineProperties(NetLog.prototype, { currentlyLoggingPath: { enumerable: true, get () { - currentlyLoggingPathDeprecated() - return this._currentlyLoggingPath == null ? '' : this._currentlyLoggingPath + currentlyLoggingPathDeprecated(); + return this._currentlyLoggingPath == null ? '' : this._currentlyLoggingPath; } } -}) +}); diff --git a/lib/browser/api/system-preferences.ts b/lib/browser/api/system-preferences.ts index 3de4732de05ce..857676c0370a3 100644 --- a/lib/browser/api/system-preferences.ts +++ b/lib/browser/api/system-preferences.ts @@ -1,10 +1,10 @@ -import { EventEmitter } from 'events' -import { deprecate } from 'electron' -const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences') +import { EventEmitter } from 'events'; +import { deprecate } from 'electron'; +const { systemPreferences, SystemPreferences } = process.electronBinding('system_preferences'); // SystemPreferences is an EventEmitter. -Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype) -EventEmitter.call(systemPreferences) +Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype); +EventEmitter.call(systemPreferences); if ('appLevelAppearance' in systemPreferences) { deprecate.fnToProperty( @@ -12,7 +12,7 @@ if ('appLevelAppearance' in systemPreferences) { 'appLevelAppearance', '_getAppLevelAppearance', '_setAppLevelAppearance' - ) + ); } if ('effectiveAppearance' in systemPreferences) { @@ -20,23 +20,23 @@ if ('effectiveAppearance' in systemPreferences) { SystemPreferences.prototype, 'effectiveAppearance', '_getEffectiveAppearance' - ) + ); } SystemPreferences.prototype.isDarkMode = deprecate.moveAPI( SystemPreferences.prototype.isDarkMode, 'systemPreferences.isDarkMode()', 'nativeTheme.shouldUseDarkColors' -) +); SystemPreferences.prototype.isInvertedColorScheme = deprecate.moveAPI( SystemPreferences.prototype.isInvertedColorScheme, 'systemPreferences.isInvertedColorScheme()', 'nativeTheme.shouldUseInvertedColorScheme' -) +); SystemPreferences.prototype.isHighContrastColorScheme = deprecate.moveAPI( SystemPreferences.prototype.isHighContrastColorScheme, 'systemPreferences.isHighContrastColorScheme()', 'nativeTheme.shouldUseHighContrastColors' -) +); -module.exports = systemPreferences +module.exports = systemPreferences; diff --git a/lib/browser/api/top-level-window.js b/lib/browser/api/top-level-window.js index b74ebf37a6099..570a57350899f 100644 --- a/lib/browser/api/top-level-window.js +++ b/lib/browser/api/top-level-window.js @@ -1,24 +1,24 @@ -'use strict' +'use strict'; -const electron = require('electron') -const { EventEmitter } = require('events') -const { TopLevelWindow } = process.electronBinding('top_level_window') +const electron = require('electron'); +const { EventEmitter } = require('events'); +const { TopLevelWindow } = process.electronBinding('top_level_window'); -Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype) +Object.setPrototypeOf(TopLevelWindow.prototype, EventEmitter.prototype); TopLevelWindow.prototype._init = function () { // Avoid recursive require. - const { app } = electron + const { app } = electron; // Simulate the application menu on platforms other than macOS. if (process.platform !== 'darwin') { - const menu = app.applicationMenu - if (menu) this.setMenu(menu) + const menu = app.applicationMenu; + if (menu) this.setMenu(menu); } -} +}; TopLevelWindow.getFocusedWindow = () => { - return TopLevelWindow.getAllWindows().find((win) => win.isFocused()) -} + return TopLevelWindow.getAllWindows().find((win) => win.isFocused()); +}; -module.exports = TopLevelWindow +module.exports = TopLevelWindow; diff --git a/lib/browser/api/touch-bar.js b/lib/browser/api/touch-bar.js index ca1c6388706dd..543f1def34fd4 100644 --- a/lib/browser/api/touch-bar.js +++ b/lib/browser/api/touch-bar.js @@ -1,336 +1,348 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') +const { EventEmitter } = require('events'); -let nextItemID = 1 +let nextItemID = 1; class TouchBar extends EventEmitter { // Bind a touch bar to a window static _setOnWindow (touchBar, window) { if (window._touchBar != null) { - window._touchBar._removeFromWindow(window) + window._touchBar._removeFromWindow(window); } if (touchBar == null) { - window._setTouchBarItems([]) - return + window._setTouchBarItems([]); + return; } if (Array.isArray(touchBar)) { - touchBar = new TouchBar(touchBar) + touchBar = new TouchBar(touchBar); } - touchBar._addToWindow(window) + touchBar._addToWindow(window); } constructor (options) { - super() + super(); if (options == null) { - throw new Error('Must specify options object as first argument') + throw new Error('Must specify options object as first argument'); } - let { items, escapeItem } = options + let { items, escapeItem } = options; if (!Array.isArray(items)) { - items = [] + items = []; } this.changeListener = (item) => { - this.emit('change', item.id, item.type) - } + this.emit('change', item.id, item.type); + }; - this.windowListeners = {} - this.items = {} - this.ordereredItems = [] - this.escapeItem = escapeItem + this.windowListeners = {}; + this.items = {}; + this.ordereredItems = []; + this.escapeItem = escapeItem; const registerItem = (item) => { - this.items[item.id] = item - item.on('change', this.changeListener) + this.items[item.id] = item; + item.on('change', this.changeListener); if (item.child instanceof TouchBar) { - item.child.ordereredItems.forEach(registerItem) + item.child.ordereredItems.forEach(registerItem); } - } + }; + + const idSet = new Set(); items.forEach((item) => { if (!(item instanceof TouchBarItem)) { - throw new Error('Each item must be an instance of TouchBarItem') + throw new Error('Each item must be an instance of TouchBarItem'); } - this.ordereredItems.push(item) - registerItem(item) - }) + + if (!idSet.has(item.id)) { + idSet.add(item.id); + } else { + throw new Error('Cannot add a single instance of TouchBarItem multiple times in a TouchBar'); + } + }); + + // register in separate loop after all items are validated + for (const item of items) { + this.ordereredItems.push(item); + registerItem(item); + } } set escapeItem (item) { if (item != null && !(item instanceof TouchBarItem)) { - throw new Error('Escape item must be an instance of TouchBarItem') + throw new Error('Escape item must be an instance of TouchBarItem'); } if (this.escapeItem != null) { - this.escapeItem.removeListener('change', this.changeListener) + this.escapeItem.removeListener('change', this.changeListener); } - this._escapeItem = item + this._escapeItem = item; if (this.escapeItem != null) { - this.escapeItem.on('change', this.changeListener) + this.escapeItem.on('change', this.changeListener); } - this.emit('escape-item-change', item) + this.emit('escape-item-change', item); } get escapeItem () { - return this._escapeItem + return this._escapeItem; } _addToWindow (window) { - const { id } = window + const { id } = window; // Already added to window - if (this.windowListeners.hasOwnProperty(id)) return + if (this.windowListeners.hasOwnProperty(id)) return; - window._touchBar = this + window._touchBar = this; const changeListener = (itemID) => { - window._refreshTouchBarItem(itemID) - } - this.on('change', changeListener) + window._refreshTouchBarItem(itemID); + }; + this.on('change', changeListener); const escapeItemListener = (item) => { - window._setEscapeTouchBarItem(item != null ? item : {}) - } - this.on('escape-item-change', escapeItemListener) + window._setEscapeTouchBarItem(item != null ? item : {}); + }; + this.on('escape-item-change', escapeItemListener); const interactionListener = (event, itemID, details) => { - let item = this.items[itemID] + let item = this.items[itemID]; if (item == null && this.escapeItem != null && this.escapeItem.id === itemID) { - item = this.escapeItem + item = this.escapeItem; } if (item != null && item.onInteraction != null) { - item.onInteraction(details) + item.onInteraction(details); } - } - window.on('-touch-bar-interaction', interactionListener) + }; + window.on('-touch-bar-interaction', interactionListener); const removeListeners = () => { - this.removeListener('change', changeListener) - this.removeListener('escape-item-change', escapeItemListener) - window.removeListener('-touch-bar-interaction', interactionListener) - window.removeListener('closed', removeListeners) - window._touchBar = null - delete this.windowListeners[id] + this.removeListener('change', changeListener); + this.removeListener('escape-item-change', escapeItemListener); + window.removeListener('-touch-bar-interaction', interactionListener); + window.removeListener('closed', removeListeners); + window._touchBar = null; + delete this.windowListeners[id]; const unregisterItems = (items) => { for (const item of items) { - item.removeListener('change', this.changeListener) + item.removeListener('change', this.changeListener); if (item.child instanceof TouchBar) { - unregisterItems(item.child.ordereredItems) + unregisterItems(item.child.ordereredItems); } } - } - unregisterItems(this.ordereredItems) + }; + unregisterItems(this.ordereredItems); if (this.escapeItem) { - this.escapeItem.removeListener('change', this.changeListener) + this.escapeItem.removeListener('change', this.changeListener); } - } - window.once('closed', removeListeners) - this.windowListeners[id] = removeListeners + }; + window.once('closed', removeListeners); + this.windowListeners[id] = removeListeners; - window._setTouchBarItems(this.ordereredItems) - escapeItemListener(this.escapeItem) + window._setTouchBarItems(this.ordereredItems); + escapeItemListener(this.escapeItem); } _removeFromWindow (window) { - const removeListeners = this.windowListeners[window.id] - if (removeListeners != null) removeListeners() + const removeListeners = this.windowListeners[window.id]; + if (removeListeners != null) removeListeners(); } } class TouchBarItem extends EventEmitter { constructor () { - super() - this._addImmutableProperty('id', `${nextItemID++}`) - this._parents = [] + super(); + this._addImmutableProperty('id', `${nextItemID++}`); + this._parents = []; } _addImmutableProperty (name, value) { Object.defineProperty(this, name, { get: function () { - return value + return value; }, set: function () { - throw new Error(`Cannot override property ${name}`) + throw new Error(`Cannot override property ${name}`); }, enumerable: true, configurable: false - }) + }); } _addLiveProperty (name, initialValue) { - const privateName = `_${name}` - this[privateName] = initialValue + const privateName = `_${name}`; + this[privateName] = initialValue; Object.defineProperty(this, name, { get: function () { - return this[privateName] + return this[privateName]; }, set: function (value) { - this[privateName] = value - this.emit('change', this) + this[privateName] = value; + this.emit('change', this); }, enumerable: true - }) + }); } _addParent (item) { - const existing = this._parents.some(test => test.id === item.id) + const existing = this._parents.some(test => test.id === item.id); if (!existing) { this._parents.push({ id: item.id, type: item.type - }) + }); } } } TouchBar.TouchBarButton = class TouchBarButton extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'button') - this._addLiveProperty('label', config.label) - this._addLiveProperty('accessibilityLabel', config.accessibilityLabel) - this._addLiveProperty('backgroundColor', config.backgroundColor) - this._addLiveProperty('icon', config.icon) - this._addLiveProperty('iconPosition', config.iconPosition) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'button'); + this._addLiveProperty('label', config.label); + this._addLiveProperty('accessibilityLabel', config.accessibilityLabel); + this._addLiveProperty('backgroundColor', config.backgroundColor); + this._addLiveProperty('icon', config.icon); + this._addLiveProperty('iconPosition', config.iconPosition); if (typeof config.click === 'function') { this._addImmutableProperty('onInteraction', () => { - config.click() - }) + config.click(); + }); } } -} +}; TouchBar.TouchBarColorPicker = class TouchBarColorPicker extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'colorpicker') - this._addLiveProperty('availableColors', config.availableColors) - this._addLiveProperty('selectedColor', config.selectedColor) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'colorpicker'); + this._addLiveProperty('availableColors', config.availableColors); + this._addLiveProperty('selectedColor', config.selectedColor); if (typeof config.change === 'function') { this._addImmutableProperty('onInteraction', (details) => { - this._selectedColor = details.color - config.change(details.color) - }) + this._selectedColor = details.color; + config.change(details.color); + }); } } -} +}; TouchBar.TouchBarGroup = class TouchBarGroup extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'group') - const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items) - this._addLiveProperty('child', defaultChild) - this.child.ordereredItems.forEach((item) => item._addParent(this)) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'group'); + const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items); + this._addLiveProperty('child', defaultChild); + this.child.ordereredItems.forEach((item) => item._addParent(this)); } -} +}; TouchBar.TouchBarLabel = class TouchBarLabel extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'label') - this._addLiveProperty('label', config.label) - this._addLiveProperty('accessibilityLabel', config.accessibilityLabel) - this._addLiveProperty('textColor', config.textColor) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'label'); + this._addLiveProperty('label', config.label); + this._addLiveProperty('accessibilityLabel', config.accessibilityLabel); + this._addLiveProperty('textColor', config.textColor); } -} +}; TouchBar.TouchBarPopover = class TouchBarPopover extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'popover') - this._addLiveProperty('label', config.label) - this._addLiveProperty('icon', config.icon) - this._addLiveProperty('showCloseButton', config.showCloseButton) - const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items) - this._addLiveProperty('child', defaultChild) - this.child.ordereredItems.forEach((item) => item._addParent(this)) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'popover'); + this._addLiveProperty('label', config.label); + this._addLiveProperty('icon', config.icon); + this._addLiveProperty('showCloseButton', config.showCloseButton); + const defaultChild = (config.items instanceof TouchBar) ? config.items : new TouchBar(config.items); + this._addLiveProperty('child', defaultChild); + this.child.ordereredItems.forEach((item) => item._addParent(this)); } -} +}; TouchBar.TouchBarSlider = class TouchBarSlider extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'slider') - this._addLiveProperty('label', config.label) - this._addLiveProperty('minValue', config.minValue) - this._addLiveProperty('maxValue', config.maxValue) - this._addLiveProperty('value', config.value) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'slider'); + this._addLiveProperty('label', config.label); + this._addLiveProperty('minValue', config.minValue); + this._addLiveProperty('maxValue', config.maxValue); + this._addLiveProperty('value', config.value); if (typeof config.change === 'function') { this._addImmutableProperty('onInteraction', (details) => { - this._value = details.value - config.change(details.value) - }) + this._value = details.value; + config.change(details.value); + }); } } -} +}; TouchBar.TouchBarSpacer = class TouchBarSpacer extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'spacer') - this._addImmutableProperty('size', config.size) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'spacer'); + this._addImmutableProperty('size', config.size); } -} +}; TouchBar.TouchBarSegmentedControl = class TouchBarSegmentedControl extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - this._addImmutableProperty('type', 'segmented_control') - this._addLiveProperty('segmentStyle', config.segmentStyle) - this._addLiveProperty('segments', config.segments || []) - this._addLiveProperty('selectedIndex', config.selectedIndex) - this._addLiveProperty('mode', config.mode) + super(); + if (config == null) config = {}; + this._addImmutableProperty('type', 'segmented_control'); + this._addLiveProperty('segmentStyle', config.segmentStyle); + this._addLiveProperty('segments', config.segments || []); + this._addLiveProperty('selectedIndex', config.selectedIndex); + this._addLiveProperty('mode', config.mode); if (typeof config.change === 'function') { this._addImmutableProperty('onInteraction', (details) => { - this._selectedIndex = details.selectedIndex - config.change(details.selectedIndex, details.isSelected) - }) + this._selectedIndex = details.selectedIndex; + config.change(details.selectedIndex, details.isSelected); + }); } } -} +}; TouchBar.TouchBarScrubber = class TouchBarScrubber extends TouchBarItem { constructor (config) { - super() - if (config == null) config = {} - let { select, highlight } = config - this._addImmutableProperty('type', 'scrubber') - this._addLiveProperty('items', config.items) - this._addLiveProperty('selectedStyle', config.selectedStyle || null) - this._addLiveProperty('overlayStyle', config.overlayStyle || null) - this._addLiveProperty('showArrowButtons', config.showArrowButtons || false) - this._addLiveProperty('mode', config.mode || 'free') - - const cont = typeof config.continuous === 'undefined' ? true : config.continuous - this._addLiveProperty('continuous', cont) + super(); + if (config == null) config = {}; + let { select, highlight } = config; + this._addImmutableProperty('type', 'scrubber'); + this._addLiveProperty('items', config.items); + this._addLiveProperty('selectedStyle', config.selectedStyle || null); + this._addLiveProperty('overlayStyle', config.overlayStyle || null); + this._addLiveProperty('showArrowButtons', config.showArrowButtons || false); + this._addLiveProperty('mode', config.mode || 'free'); + + const cont = typeof config.continuous === 'undefined' ? true : config.continuous; + this._addLiveProperty('continuous', cont); if (typeof select === 'function' || typeof highlight === 'function') { - if (select == null) select = () => {} - if (highlight == null) highlight = () => {} + if (select == null) select = () => {}; + if (highlight == null) highlight = () => {}; this._addImmutableProperty('onInteraction', (details) => { if (details.type === 'select' && typeof select === 'function') { - select(details.selectedIndex) + select(details.selectedIndex); } else if (details.type === 'highlight' && typeof highlight === 'function') { - highlight(details.highlightedIndex) + highlight(details.highlightedIndex); } - }) + }); } } -} +}; -module.exports = TouchBar +module.exports = TouchBar; diff --git a/lib/browser/api/tray.js b/lib/browser/api/tray.js index b7402858bf181..ac8cc88724fcb 100644 --- a/lib/browser/api/tray.js +++ b/lib/browser/api/tray.js @@ -1,9 +1,9 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') -const { deprecate } = require('electron') -const { Tray } = process.electronBinding('tray') +const { EventEmitter } = require('events'); +const { deprecate } = require('electron'); +const { Tray } = process.electronBinding('tray'); -Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype) +Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype); -module.exports = Tray +module.exports = Tray; diff --git a/lib/browser/api/view.js b/lib/browser/api/view.js index 578a2306a1855..246e9a2b491c6 100644 --- a/lib/browser/api/view.js +++ b/lib/browser/api/view.js @@ -1,11 +1,11 @@ -'use strict' +'use strict'; -const { EventEmitter } = require('events') -const { View } = process.electronBinding('view') +const { EventEmitter } = require('events'); +const { View } = process.electronBinding('view'); -Object.setPrototypeOf(View.prototype, EventEmitter.prototype) +Object.setPrototypeOf(View.prototype, EventEmitter.prototype); View.prototype._init = function () { -} +}; -module.exports = View +module.exports = View; diff --git a/lib/browser/api/views/box-layout.js b/lib/browser/api/views/box-layout.js index 5c4d98fd5600b..f9c89f8861d47 100644 --- a/lib/browser/api/views/box-layout.js +++ b/lib/browser/api/views/box-layout.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { LayoutManager } = electron -const { BoxLayout } = process.electronBinding('box_layout') +const { LayoutManager } = electron; +const { BoxLayout } = process.electronBinding('box_layout'); -Object.setPrototypeOf(BoxLayout.prototype, LayoutManager.prototype) +Object.setPrototypeOf(BoxLayout.prototype, LayoutManager.prototype); BoxLayout.prototype._init = function () { // Call parent class's _init. - LayoutManager.prototype._init.call(this) -} + LayoutManager.prototype._init.call(this); +}; -module.exports = BoxLayout +module.exports = BoxLayout; diff --git a/lib/browser/api/views/button.js b/lib/browser/api/views/button.js index cb4cbc82962c7..d3b094eb0fb6b 100644 --- a/lib/browser/api/views/button.js +++ b/lib/browser/api/views/button.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { View } = electron -const { Button } = process.electronBinding('button') +const { View } = electron; +const { Button } = process.electronBinding('button'); -Object.setPrototypeOf(Button.prototype, View.prototype) +Object.setPrototypeOf(Button.prototype, View.prototype); Button.prototype._init = function () { // Call parent class's _init. - View.prototype._init.call(this) -} + View.prototype._init.call(this); +}; -module.exports = Button +module.exports = Button; diff --git a/lib/browser/api/views/label-button.js b/lib/browser/api/views/label-button.js index ff2f990e0d0e4..0a205a177dfe2 100644 --- a/lib/browser/api/views/label-button.js +++ b/lib/browser/api/views/label-button.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { Button } = electron -const { LabelButton } = process.electronBinding('label_button') +const { Button } = electron; +const { LabelButton } = process.electronBinding('label_button'); -Object.setPrototypeOf(LabelButton.prototype, Button.prototype) +Object.setPrototypeOf(LabelButton.prototype, Button.prototype); LabelButton.prototype._init = function () { // Call parent class's _init. - Button.prototype._init.call(this) -} + Button.prototype._init.call(this); +}; -module.exports = LabelButton +module.exports = LabelButton; diff --git a/lib/browser/api/views/layout-manager.js b/lib/browser/api/views/layout-manager.js index b9da420ad6986..e7b3b8c8f1360 100644 --- a/lib/browser/api/views/layout-manager.js +++ b/lib/browser/api/views/layout-manager.js @@ -1,8 +1,8 @@ -'use strict' +'use strict'; -const { LayoutManager } = process.electronBinding('layout_manager') +const { LayoutManager } = process.electronBinding('layout_manager'); LayoutManager.prototype._init = function () { -} +}; -module.exports = LayoutManager +module.exports = LayoutManager; diff --git a/lib/browser/api/views/md-text-button.js b/lib/browser/api/views/md-text-button.js index 8671a80dbc377..2528bf487e542 100644 --- a/lib/browser/api/views/md-text-button.js +++ b/lib/browser/api/views/md-text-button.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { LabelButton } = electron -const { MdTextButton } = process.electronBinding('md_text_button') +const { LabelButton } = electron; +const { MdTextButton } = process.electronBinding('md_text_button'); -Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype) +Object.setPrototypeOf(MdTextButton.prototype, LabelButton.prototype); MdTextButton.prototype._init = function () { // Call parent class's _init. - LabelButton.prototype._init.call(this) -} + LabelButton.prototype._init.call(this); +}; -module.exports = MdTextButton +module.exports = MdTextButton; diff --git a/lib/browser/api/views/resize-area.js b/lib/browser/api/views/resize-area.js index e8067aa425469..d36c3805f54af 100644 --- a/lib/browser/api/views/resize-area.js +++ b/lib/browser/api/views/resize-area.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { View } = electron -const { ResizeArea } = process.electronBinding('resize_area') +const { View } = electron; +const { ResizeArea } = process.electronBinding('resize_area'); -Object.setPrototypeOf(ResizeArea.prototype, View.prototype) +Object.setPrototypeOf(ResizeArea.prototype, View.prototype); ResizeArea.prototype._init = function () { // Call parent class's _init. - View.prototype._init.call(this) -} + View.prototype._init.call(this); +}; -module.exports = ResizeArea +module.exports = ResizeArea; diff --git a/lib/browser/api/views/text-field.js b/lib/browser/api/views/text-field.js index fafd5bccd01db..eeb740019ada1 100644 --- a/lib/browser/api/views/text-field.js +++ b/lib/browser/api/views/text-field.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { View } = electron -const { TextField } = process.electronBinding('text_field') +const { View } = electron; +const { TextField } = process.electronBinding('text_field'); -Object.setPrototypeOf(TextField.prototype, View.prototype) +Object.setPrototypeOf(TextField.prototype, View.prototype); TextField.prototype._init = function () { // Call parent class's _init. - View.prototype._init.call(this) -} + View.prototype._init.call(this); +}; -module.exports = TextField +module.exports = TextField; diff --git a/lib/browser/api/web-contents-view.js b/lib/browser/api/web-contents-view.js index eb028bb0908fa..12e53faa1a304 100644 --- a/lib/browser/api/web-contents-view.js +++ b/lib/browser/api/web-contents-view.js @@ -1,15 +1,15 @@ -'use strict' +'use strict'; -const electron = require('electron') +const electron = require('electron'); -const { View } = electron -const { WebContentsView } = process.electronBinding('web_contents_view') +const { View } = electron; +const { WebContentsView } = process.electronBinding('web_contents_view'); -Object.setPrototypeOf(WebContentsView.prototype, View.prototype) +Object.setPrototypeOf(WebContentsView.prototype, View.prototype); WebContentsView.prototype._init = function () { // Call parent class's _init. - View.prototype._init.call(this) -} + View.prototype._init.call(this); +}; -module.exports = WebContentsView +module.exports = WebContentsView; diff --git a/lib/browser/api/web-contents.js b/lib/browser/api/web-contents.js index 0105a2322bd07..538a5b903eb52 100644 --- a/lib/browser/api/web-contents.js +++ b/lib/browser/api/web-contents.js @@ -1,25 +1,26 @@ -'use strict' +'use strict'; -const features = process.electronBinding('features') -const { EventEmitter } = require('events') -const electron = require('electron') -const path = require('path') -const url = require('url') -const { app, ipcMain, session, deprecate } = electron +const features = process.electronBinding('features'); +const { EventEmitter } = require('events'); +const electron = require('electron'); +const path = require('path'); +const url = require('url'); +const { app, ipcMain, session, deprecate } = electron; -const NavigationController = require('@electron/internal/browser/navigation-controller') -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') -const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') +const { internalWindowOpen } = require('@electron/internal/browser/guest-window-manager'); +const NavigationController = require('@electron/internal/browser/navigation-controller'); +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); +const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); // session is not used here, the purpose is to make sure session is initalized // before the webContents module. // eslint-disable-next-line session -let nextId = 0 +let nextId = 0; const getNextId = function () { - return ++nextId -} + return ++nextId; +}; // Stock page sizes const PDFPageSizes = { @@ -60,7 +61,7 @@ const PDFPageSizes = { width_microns: 279400, custom_display_name: 'Tabloid' } -} +}; // Default printing setting const defaultPrintingSetting = { @@ -71,7 +72,6 @@ const defaultPrintingSetting = { headerFooterEnabled: false, marginsType: 0, isFirstRequest: false, - requestID: getNextId(), previewUIID: 0, previewModifiable: true, printToPDF: true, @@ -91,83 +91,83 @@ const defaultPrintingSetting = { collate: true, shouldPrintBackgrounds: false, shouldPrintSelectionOnly: false -} +}; // JavaScript implementations of WebContents. -const binding = process.electronBinding('web_contents') -const { WebContents } = binding +const binding = process.electronBinding('web_contents'); +const { WebContents } = binding; -Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype) -Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype) +Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype); +Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype); // WebContents::send(channel, args..) // WebContents::sendToAll(channel, args..) WebContents.prototype.send = function (channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } - const internal = false - const sendToAll = false + const internal = false; + const sendToAll = false; - return this._send(internal, sendToAll, channel, args) -} + return this._send(internal, sendToAll, channel, args); +}; WebContents.prototype.sendToAll = function (channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } - const internal = false - const sendToAll = true + const internal = false; + const sendToAll = true; - return this._send(internal, sendToAll, channel, args) -} + return this._send(internal, sendToAll, channel, args); +}; WebContents.prototype._sendInternal = function (channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } - const internal = true - const sendToAll = false + const internal = true; + const sendToAll = false; - return this._send(internal, sendToAll, channel, args) -} + return this._send(internal, sendToAll, channel, args); +}; WebContents.prototype._sendInternalToAll = function (channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } - const internal = true - const sendToAll = true + const internal = true; + const sendToAll = true; - return this._send(internal, sendToAll, channel, args) -} + return this._send(internal, sendToAll, channel, args); +}; WebContents.prototype.sendToFrame = function (frameId, channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } else if (typeof frameId !== 'number') { - throw new Error('Missing required frameId argument') + throw new Error('Missing required frameId argument'); } - const internal = false - const sendToAll = false + const internal = false; + const sendToAll = false; - return this._sendToFrame(internal, sendToAll, frameId, channel, args) -} + return this._sendToFrame(internal, sendToAll, frameId, channel, args); +}; WebContents.prototype._sendToFrameInternal = function (frameId, channel, ...args) { if (typeof channel !== 'string') { - throw new Error('Missing required channel argument') + throw new Error('Missing required channel argument'); } else if (typeof frameId !== 'number') { - throw new Error('Missing required frameId argument') + throw new Error('Missing required frameId argument'); } - const internal = true - const sendToAll = false + const internal = true; + const sendToAll = false; - return this._sendToFrame(internal, sendToAll, frameId, channel, args) -} + return this._sendToFrame(internal, sendToAll, frameId, channel, args); +}; // Following methods are mapped to webFrame. const webFrameMethods = [ @@ -176,59 +176,65 @@ const webFrameMethods = [ 'removeInsertedCSS', 'setLayoutZoomLevelLimits', 'setVisualZoomLevelLimits' -] +]; for (const method of webFrameMethods) { WebContents.prototype[method] = function (...args) { - return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args) - } + return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args); + }; } -const executeJavaScript = (contents, code, hasUserGesture) => { - return ipcMainUtils.invokeInWebContents(contents, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture) -} +const waitTillCanExecuteJavaScript = async (webContents) => { + if (webContents.getURL() && !webContents.isLoadingMainFrame()) return; + + return new Promise((resolve) => { + webContents.once('did-stop-loading', () => { + resolve(); + }); + }); +}; // Make sure WebContents::executeJavaScript would run the code only when the // WebContents has been loaded. -WebContents.prototype.executeJavaScript = function (code, hasUserGesture) { - if (this.getURL() && !this.isLoadingMainFrame()) { - return executeJavaScript(this, code, hasUserGesture) - } else { - return new Promise((resolve, reject) => { - this.once('did-stop-loading', () => { - executeJavaScript(this, code, hasUserGesture).then(resolve, reject) - }) - }) - } -} +WebContents.prototype.executeJavaScript = async function (code, hasUserGesture) { + await waitTillCanExecuteJavaScript(this); + return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', code, hasUserGesture); +}; +WebContents.prototype.executeJavaScriptInIsolatedWorld = async function (code, hasUserGesture) { + await waitTillCanExecuteJavaScript(this); + return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScriptInIsolatedWorld', code, hasUserGesture); +}; // Translate the options of printToPDF. WebContents.prototype.printToPDF = function (options) { - const printingSetting = Object.assign({}, defaultPrintingSetting) + const printingSetting = { + ...defaultPrintingSetting, + requestID: getNextId() + }; if (options.landscape) { - printingSetting.landscape = options.landscape + printingSetting.landscape = options.landscape; } if (options.fitToPageEnabled) { - printingSetting.fitToPageEnabled = options.fitToPageEnabled + printingSetting.fitToPageEnabled = options.fitToPageEnabled; } if (options.scaleFactor) { - printingSetting.scaleFactor = options.scaleFactor + printingSetting.scaleFactor = options.scaleFactor; } if (options.marginsType) { - printingSetting.marginsType = options.marginsType + printingSetting.marginsType = options.marginsType; } if (options.printSelectionOnly) { - printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly + printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly; } if (options.printBackground) { - printingSetting.shouldPrintBackgrounds = options.printBackground + printingSetting.shouldPrintBackgrounds = options.printBackground; } if (options.pageSize) { - const pageSize = options.pageSize + const pageSize = options.pageSize; if (typeof pageSize === 'object') { if (!pageSize.height || !pageSize.width) { - return Promise.reject(new Error('Must define height and width for pageSize')) + return Promise.reject(new Error('Must define height and width for pageSize')); } // Dimensions in Microns // 1 meter = 10^6 microns @@ -237,46 +243,49 @@ WebContents.prototype.printToPDF = function (options) { custom_display_name: 'Custom', height_microns: Math.ceil(pageSize.height), width_microns: Math.ceil(pageSize.width) - } + }; } else if (PDFPageSizes[pageSize]) { - printingSetting.mediaSize = PDFPageSizes[pageSize] + printingSetting.mediaSize = PDFPageSizes[pageSize]; } else { - return Promise.reject(new Error(`Does not support pageSize with ${pageSize}`)) + return Promise.reject(new Error(`Does not support pageSize with ${pageSize}`)); } } else { - printingSetting.mediaSize = PDFPageSizes['A4'] + printingSetting.mediaSize = PDFPageSizes['A4']; } // Chromium expects this in a 0-100 range number, not as float - printingSetting.scaleFactor = Math.ceil(printingSetting.scaleFactor) % 100 + printingSetting.scaleFactor = Math.ceil(printingSetting.scaleFactor) % 100; + // PrinterType enum from //printing/print_job_constants.h + printingSetting.printerType = 2; if (features.isPrintingEnabled()) { - return this._printToPDF(printingSetting) + return this._printToPDF(printingSetting); } else { - return Promise.reject(new Error('Printing feature is disabled')) + return Promise.reject(new Error('Printing feature is disabled')); } -} +}; WebContents.prototype.print = function (...args) { if (features.isPrintingEnabled()) { - this._print(...args) + this._print(...args); } else { - console.error('Error: Printing feature is disabled.') + console.error('Error: Printing feature is disabled.'); } -} +}; WebContents.prototype.getPrinters = function () { if (features.isPrintingEnabled()) { - return this._getPrinters() + return this._getPrinters(); } else { - console.error('Error: Printing feature is disabled.') + console.error('Error: Printing feature is disabled.'); + return []; } -} +}; WebContents.prototype.loadFile = function (filePath, options = {}) { if (typeof filePath !== 'string') { - throw new Error('Must pass filePath as a string') + throw new Error('Must pass filePath as a string'); } - const { query, search, hash } = options + const { query, search, hash } = options; return this.loadURL(url.format({ protocol: 'file', @@ -285,99 +294,105 @@ WebContents.prototype.loadFile = function (filePath, options = {}) { query, search, hash - })) -} + })); +}; const addReplyToEvent = (event) => { event.reply = (...args) => { - event.sender.sendToFrame(event.frameId, ...args) - } -} + event.sender.sendToFrame(event.frameId, ...args); + }; +}; const addReplyInternalToEvent = (event) => { Object.defineProperty(event, '_replyInternal', { configurable: false, enumerable: false, value: (...args) => { - event.sender._sendToFrameInternal(event.frameId, ...args) + event.sender._sendToFrameInternal(event.frameId, ...args); } - }) -} + }); +}; const addReturnValueToEvent = (event) => { Object.defineProperty(event, 'returnValue', { set: (value) => event.sendReply([value]), get: () => {} - }) -} + }); +}; + +let warnedAboutRendererProcessReuse = false; // Add JavaScript wrappers for WebContents class. WebContents.prototype._init = function () { + if (!process.noDeprecation && !warnedAboutRendererProcessReuse && app._allowRendererProcessReuseIsDefaultValue) { + deprecate.log('The default value of app.allowRendererProcessReuse is deprecated, it is currently "false". It will change to be "true" in Electron 9. For more information please check https://github.com/electron/electron/issues/18397'); + warnedAboutRendererProcessReuse = true; + } // The navigation controller. - NavigationController.call(this, this) + NavigationController.call(this, this); // Every remote callback from renderer process would add a listener to the // render-view-deleted event, so ignore the listeners warning. - this.setMaxListeners(0) + this.setMaxListeners(0); // Dispatch IPC messages to the ipc module. this.on('-ipc-message', function (event, internal, channel, args) { if (internal) { - addReplyInternalToEvent(event) - ipcMainInternal.emit(channel, event, ...args) + addReplyInternalToEvent(event); + ipcMainInternal.emit(channel, event, ...args); } else { - addReplyToEvent(event) - this.emit('ipc-message', event, channel, ...args) - ipcMain.emit(channel, event, ...args) + addReplyToEvent(event); + this.emit('ipc-message', event, channel, ...args); + ipcMain.emit(channel, event, ...args); } - }) + }); this.on('-ipc-invoke', function (event, internal, channel, args) { - event._reply = (result) => event.sendReply({ result }) + event._reply = (result) => event.sendReply({ result }); event._throw = (error) => { - console.error(`Error occurred in handler for '${channel}':`, error) - event.sendReply({ error: error.toString() }) - } - const target = internal ? ipcMainInternal : ipcMain + console.error(`Error occurred in handler for '${channel}':`, error); + event.sendReply({ error: error.toString() }); + }; + const target = internal ? ipcMainInternal : ipcMain; if (target._invokeHandlers.has(channel)) { - target._invokeHandlers.get(channel)(event, ...args) + target._invokeHandlers.get(channel)(event, ...args); } else { - event._throw(`No handler registered for '${channel}'`) + event._throw(`No handler registered for '${channel}'`); } - }) + }); this.on('-ipc-message-sync', function (event, internal, channel, args) { - addReturnValueToEvent(event) + addReturnValueToEvent(event); if (internal) { - addReplyInternalToEvent(event) - ipcMainInternal.emit(channel, event, ...args) + addReplyInternalToEvent(event); + ipcMainInternal.emit(channel, event, ...args); } else { - addReplyToEvent(event) - this.emit('ipc-message-sync', event, channel, ...args) - ipcMain.emit(channel, event, ...args) + addReplyToEvent(event); + this.emit('ipc-message-sync', event, channel, ...args); + ipcMain.emit(channel, event, ...args); } - }) + }); // Handle context menu action request from pepper plugin. this.on('pepper-context-menu', function (event, params, callback) { // Access Menu via electron.Menu to prevent circular require. - const menu = electron.Menu.buildFromTemplate(params.menu) + const menu = electron.Menu.buildFromTemplate(params.menu); menu.popup({ window: event.sender.getOwnerBrowserWindow(), x: params.x, y: params.y, callback - }) - }) + }); + }); this.on('crashed', (event, ...args) => { - app.emit('renderer-process-crashed', event, this, ...args) - }) + app.emit('renderer-process-crashed', event, this, ...args); + }); // The devtools requests the webContents to reload. this.on('devtools-reload-page', function () { - this.reload() - }) + this.reload(); + }); // Handle window.open for BrowserWindow and BrowserView. if (['browserView', 'window'].includes(this.getType())) { @@ -389,11 +404,9 @@ WebContents.prototype._init = function () { show: true, width: 800, height: 600 - } - ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', - event, url, referrer, frameName, disposition, - options, additionalFeatures, postData) - }) + }; + internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures, postData); + }); // Create a new browser window for the native implementation of // "window.open", used in sandbox and nativeWindowOpen mode. @@ -401,8 +414,8 @@ WebContents.prototype._init = function () { userGesture, left, top, width, height, url, frameName) => { if ((disposition !== 'foreground-tab' && disposition !== 'new-window' && disposition !== 'background-tab')) { - event.preventDefault() - return + event.preventDefault(); + return; } const options = { @@ -412,51 +425,59 @@ WebContents.prototype._init = function () { width: width || 800, height: height || 600, webContents - } - const referrer = { url: '', policy: 'default' } - ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', - event, url, referrer, frameName, disposition, options) - }) + }; + const referrer = { url: '', policy: 'default' }; + internalWindowOpen(event, url, referrer, frameName, disposition, options); + }); + + const prefs = this.getWebPreferences() || {}; + if (prefs.webviewTag && prefs.contextIsolation) { + electron.deprecate.log('Security Warning: A WebContents was just created with both webviewTag and contextIsolation enabled. This combination is fundamentally less secure and effectively bypasses the protections of contextIsolation. We strongly recommend you move away from webviews to OOPIF or BrowserView in order for your app to be more secure'); + } } - const event = process.electronBinding('event').createEmpty() - app.emit('web-contents-created', event, this) -} + this.on('login', (event, ...args) => { + app.emit('login', event, this, ...args); + }); + + const event = process.electronBinding('event').createEmpty(); + app.emit('web-contents-created', event, this); +}; // Deprecations -deprecate.fnToProperty(WebContents.prototype, 'audioMuted', '_isAudioMuted', '_setAudioMuted') -deprecate.fnToProperty(WebContents.prototype, 'userAgent', '_getUserAgent', '_setUserAgent') -deprecate.fnToProperty(WebContents.prototype, 'zoomLevel', '_getZoomLevel', '_setZoomLevel') -deprecate.fnToProperty(WebContents.prototype, 'zoomFactor', '_getZoomFactor', '_setZoomFactor') -deprecate.fnToProperty(WebContents.prototype, 'frameRate', '_getFrameRate', '_setFrameRate') +deprecate.fnToProperty(WebContents.prototype, 'audioMuted', '_isAudioMuted', '_setAudioMuted'); +deprecate.fnToProperty(WebContents.prototype, 'userAgent', '_getUserAgent', '_setUserAgent'); +deprecate.fnToProperty(WebContents.prototype, 'zoomLevel', '_getZoomLevel', '_setZoomLevel'); +deprecate.fnToProperty(WebContents.prototype, 'zoomFactor', '_getZoomFactor', '_setZoomFactor'); +deprecate.fnToProperty(WebContents.prototype, 'frameRate', '_getFrameRate', '_setFrameRate'); // JavaScript wrapper of Debugger. -const { Debugger } = process.electronBinding('debugger') -Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype) +const { Debugger } = process.electronBinding('debugger'); +Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype); // Public APIs. module.exports = { create (options = {}) { - return binding.create(options) + return binding.create(options); }, fromId (id) { - return binding.fromId(id) + return binding.fromId(id); }, getFocusedWebContents () { - let focused = null + let focused = null; for (const contents of binding.getAllWebContents()) { - if (!contents.isFocused()) continue - if (focused == null) focused = contents + if (!contents.isFocused()) continue; + if (focused == null) focused = contents; // Return webview web contents which may be embedded inside another // web contents that is also reporting as focused - if (contents.getType() === 'webview') return contents + if (contents.getType() === 'webview') return contents; } - return focused + return focused; }, getAllWebContents () { - return binding.getAllWebContents() + return binding.getAllWebContents(); } -} +}; diff --git a/lib/browser/chrome-extension.js b/lib/browser/chrome-extension.js index c56e3f473f72b..cbcd70cff4fec 100644 --- a/lib/browser/chrome-extension.js +++ b/lib/browser/chrome-extension.js @@ -1,62 +1,62 @@ -'use strict' +'use strict'; if (process.electronBinding('features').isExtensionsEnabled()) { - throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled') + throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled'); } -const { app, webContents, BrowserWindow } = require('electron') -const { getAllWebContents } = process.electronBinding('web_contents') -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') -const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') +const { app, webContents, BrowserWindow } = require('electron'); +const { getAllWebContents } = process.electronBinding('web_contents'); +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); +const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); -const { Buffer } = require('buffer') -const fs = require('fs') -const path = require('path') -const url = require('url') -const util = require('util') +const { Buffer } = require('buffer'); +const fs = require('fs'); +const path = require('path'); +const url = require('url'); +const util = require('util'); // Mapping between extensionId(hostname) and manifest. -const manifestMap = {} // extensionId => manifest -const manifestNameMap = {} // name => manifest -const devToolsExtensionNames = new Set() +const manifestMap = {}; // extensionId => manifest +const manifestNameMap = {}; // name => manifest +const devToolsExtensionNames = new Set(); const generateExtensionIdFromName = function (name) { - return name.replace(/[\W_]+/g, '-').toLowerCase() -} + return name.replace(/[\W_]+/g, '-').toLowerCase(); +}; const isWindowOrWebView = function (webContents) { - const type = webContents.getType() - return type === 'window' || type === 'webview' -} + const type = webContents.getType(); + return type === 'window' || type === 'webview'; +}; const isBackgroundPage = function (webContents) { - return webContents.getType() === 'backgroundPage' -} + return webContents.getType() === 'backgroundPage'; +}; // Create or get manifest object from |srcDirectory|. const getManifestFromPath = function (srcDirectory) { - let manifest - let manifestContent + let manifest; + let manifestContent; try { - manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json')) + manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json')); } catch (readError) { - console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`) - console.warn(readError.stack || readError) - throw readError + console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`); + console.warn(readError.stack || readError); + throw readError; } try { - manifest = JSON.parse(manifestContent) + manifest = JSON.parse(manifestContent); } catch (parseError) { - console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`) - console.warn(parseError.stack || parseError) - throw parseError + console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`); + console.warn(parseError.stack || parseError); + throw parseError; } if (!manifestNameMap[manifest.name]) { - const extensionId = generateExtensionIdFromName(manifest.name) - manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest + const extensionId = generateExtensionIdFromName(manifest.name); + manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest; Object.assign(manifest, { srcDirectory: srcDirectory, extensionId: extensionId, @@ -68,31 +68,31 @@ const getManifestFromPath = function (srcDirectory) { hostname: extensionId, pathname: manifest.devtools_page }) - }) - return manifest + }); + return manifest; } else if (manifest && manifest.name) { - console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`) - return manifest + console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`); + return manifest; } -} +}; // Manage the background pages. -const backgroundPages = {} +const backgroundPages = {}; const startBackgroundPages = function (manifest) { - if (backgroundPages[manifest.extensionId] || !manifest.background) return + if (backgroundPages[manifest.extensionId] || !manifest.background) return; - let html - let name + let html; + let name; if (manifest.background.page) { - name = manifest.background.page - html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page)) + name = manifest.background.page; + html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page)); } else { - name = '_generated_background_page.html' + name = '_generated_background_page.html'; const scripts = manifest.background.scripts.map((name) => { - return `` - }).join('') - html = Buffer.from(`${scripts}`) + return ``; + }).join(''); + html = Buffer.from(`${scripts}`); } const contents = webContents.create({ @@ -100,36 +100,36 @@ const startBackgroundPages = function (manifest) { type: 'backgroundPage', sandbox: true, enableRemoteModule: false - }) - backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name } + }); + backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name }; contents.loadURL(url.format({ protocol: 'chrome-extension', slashes: true, hostname: manifest.extensionId, pathname: name - })) -} + })); +}; const removeBackgroundPages = function (manifest) { - if (!backgroundPages[manifest.extensionId]) return + if (!backgroundPages[manifest.extensionId]) return; - backgroundPages[manifest.extensionId].webContents.destroy() - delete backgroundPages[manifest.extensionId] -} + backgroundPages[manifest.extensionId].webContents.destroy(); + delete backgroundPages[manifest.extensionId]; +}; const sendToBackgroundPages = function (...args) { for (const page of Object.values(backgroundPages)) { if (!page.webContents.isDestroyed()) { - page.webContents._sendInternalToAll(...args) + page.webContents._sendInternalToAll(...args); } } -} +}; // Dispatch web contents events to Chrome APIs const hookWebContentsEvents = function (webContents) { - const tabId = webContents.id + const tabId = webContents.id; - sendToBackgroundPages('CHROME_TABS_ONCREATED') + sendToBackgroundPages('CHROME_TABS_ONCREATED'); webContents.on('will-navigate', (event, url) => { sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', { @@ -139,8 +139,8 @@ const hookWebContentsEvents = function (webContents) { tabId: tabId, timeStamp: Date.now(), url: url - }) - }) + }); + }); webContents.on('did-navigate', (event, url) => { sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', { @@ -150,189 +150,189 @@ const hookWebContentsEvents = function (webContents) { tabId: tabId, timeStamp: Date.now(), url: url - }) - }) + }); + }); webContents.once('destroyed', () => { - sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId) - }) -} + sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId); + }); +}; // Handle the chrome.* API messages. -let nextId = 0 +let nextId = 0; ipcMainUtils.handleSync('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) { if (isBackgroundPage(event.sender)) { - throw new Error('chrome.runtime.connect is not supported in background page') + throw new Error('chrome.runtime.connect is not supported in background page'); } - const page = backgroundPages[extensionId] + const page = backgroundPages[extensionId]; if (!page || page.webContents.isDestroyed()) { - throw new Error(`Connect to unknown extension ${extensionId}`) + throw new Error(`Connect to unknown extension ${extensionId}`); } - const tabId = page.webContents.id - const portId = ++nextId + const tabId = page.webContents.id; + const portId = ++nextId; event.sender.once('render-view-deleted', () => { - if (page.webContents.isDestroyed()) return - page.webContents._sendInternalToAll(`CHROME_PORT_DISCONNECT_${portId}`) - }) - page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo) + if (page.webContents.isDestroyed()) return; + page.webContents._sendInternalToAll(`CHROME_PORT_DISCONNECT_${portId}`); + }); + page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo); - return { tabId, portId } -}) + return { tabId, portId }; +}); ipcMainUtils.handleSync('CHROME_EXTENSION_MANIFEST', function (event, extensionId) { - const manifest = manifestMap[extensionId] + const manifest = manifestMap[extensionId]; if (!manifest) { - throw new Error(`Invalid extensionId: ${extensionId}`) + throw new Error(`Invalid extensionId: ${extensionId}`); } - return manifest -}) + return manifest; +}); ipcMainInternal.handle('CHROME_RUNTIME_SEND_MESSAGE', async function (event, extensionId, message) { if (isBackgroundPage(event.sender)) { - throw new Error('chrome.runtime.sendMessage is not supported in background page') + throw new Error('chrome.runtime.sendMessage is not supported in background page'); } - const page = backgroundPages[extensionId] + const page = backgroundPages[extensionId]; if (!page || page.webContents.isDestroyed()) { - throw new Error(`Connect to unknown extension ${extensionId}`) + throw new Error(`Connect to unknown extension ${extensionId}`); } - return ipcMainUtils.invokeInWebContents(page.webContents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message) -}) + return ipcMainUtils.invokeInWebContents(page.webContents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message); +}); ipcMainInternal.handle('CHROME_TABS_SEND_MESSAGE', async function (event, tabId, extensionId, message) { - const contents = webContents.fromId(tabId) + const contents = webContents.fromId(tabId); if (!contents) { - throw new Error(`Sending message to unknown tab ${tabId}`) + throw new Error(`Sending message to unknown tab ${tabId}`); } - const senderTabId = isBackgroundPage(event.sender) ? null : event.sender.id + const senderTabId = isBackgroundPage(event.sender) ? null : event.sender.id; - return ipcMainUtils.invokeInWebContents(contents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message) -}) + return ipcMainUtils.invokeInWebContents(contents, true, `CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message); +}); const getLanguage = () => { - return app.getLocale().replace(/-.*$/, '').toLowerCase() -} + return app.getLocale().replace(/-.*$/, '').toLowerCase(); +}; const getMessagesPath = (extensionId) => { - const metadata = manifestMap[extensionId] + const metadata = manifestMap[extensionId]; if (!metadata) { - throw new Error(`Invalid extensionId: ${extensionId}`) + throw new Error(`Invalid extensionId: ${extensionId}`); } - const localesDirectory = path.join(metadata.srcDirectory, '_locales') - const language = getLanguage() + const localesDirectory = path.join(metadata.srcDirectory, '_locales'); + const language = getLanguage(); try { - const filename = path.join(localesDirectory, language, 'messages.json') - fs.accessSync(filename, fs.constants.R_OK) - return filename + const filename = path.join(localesDirectory, language, 'messages.json'); + fs.accessSync(filename, fs.constants.R_OK); + return filename; } catch { - const defaultLocale = metadata.default_locale || 'en' - return path.join(localesDirectory, defaultLocale, 'messages.json') + const defaultLocale = metadata.default_locale || 'en'; + return path.join(localesDirectory, defaultLocale, 'messages.json'); } -} +}; ipcMainUtils.handleSync('CHROME_GET_MESSAGES', async function (event, extensionId) { - const messagesPath = getMessagesPath(extensionId) - return fs.promises.readFile(messagesPath, 'utf8') -}) + const messagesPath = getMessagesPath(extensionId); + return fs.promises.readFile(messagesPath, 'utf8'); +}); -const validStorageTypes = new Set(['sync', 'local']) +const validStorageTypes = new Set(['sync', 'local']); const getChromeStoragePath = (storageType, extensionId) => { if (!validStorageTypes.has(storageType)) { - throw new Error(`Invalid storageType: ${storageType}`) + throw new Error(`Invalid storageType: ${storageType}`); } if (!manifestMap[extensionId]) { - throw new Error(`Invalid extensionId: ${extensionId}`) + throw new Error(`Invalid extensionId: ${extensionId}`); } - return path.join(app.getPath('userData'), `/Chrome Storage/${extensionId}-${storageType}.json`) -} + return path.join(app.getPath('userData'), `/Chrome Storage/${extensionId}-${storageType}.json`); +}; ipcMainInternal.handle('CHROME_STORAGE_READ', async function (event, storageType, extensionId) { - const filePath = getChromeStoragePath(storageType, extensionId) + const filePath = getChromeStoragePath(storageType, extensionId); try { - return await fs.promises.readFile(filePath, 'utf8') + return await fs.promises.readFile(filePath, 'utf8'); } catch (error) { if (error.code === 'ENOENT') { - return null + return null; } else { - throw error + throw error; } } -}) +}); ipcMainInternal.handle('CHROME_STORAGE_WRITE', async function (event, storageType, extensionId, data) { - const filePath = getChromeStoragePath(storageType, extensionId) + const filePath = getChromeStoragePath(storageType, extensionId); try { - await fs.promises.mkdir(path.dirname(filePath), { recursive: true }) + await fs.promises.mkdir(path.dirname(filePath), { recursive: true }); } catch { // we just ignore the errors of mkdir } - return fs.promises.writeFile(filePath, data, 'utf8') -}) + return fs.promises.writeFile(filePath, data, 'utf8'); +}); const isChromeExtension = function (pageURL) { - const { protocol } = url.parse(pageURL) - return protocol === 'chrome-extension:' -} + const { protocol } = url.parse(pageURL); + return protocol === 'chrome-extension:'; +}; const assertChromeExtension = function (contents, api) { - const pageURL = contents._getURL() + const pageURL = contents._getURL(); if (!isChromeExtension(pageURL)) { - console.error(`Blocked ${pageURL} from calling ${api}`) - throw new Error(`Blocked ${api}`) + console.error(`Blocked ${pageURL} from calling ${api}`); + throw new Error(`Blocked ${api}`); } -} +}; ipcMainInternal.handle('CHROME_TABS_EXECUTE_SCRIPT', async function (event, tabId, extensionId, details) { - assertChromeExtension(event.sender, 'chrome.tabs.executeScript()') + assertChromeExtension(event.sender, 'chrome.tabs.executeScript()'); - const contents = webContents.fromId(tabId) + const contents = webContents.fromId(tabId); if (!contents) { - throw new Error(`Sending message to unknown tab ${tabId}`) + throw new Error(`Sending message to unknown tab ${tabId}`); } - let code, url + let code, url; if (details.file) { - const manifest = manifestMap[extensionId] - code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file))) - url = `chrome-extension://${extensionId}${details.file}` + const manifest = manifestMap[extensionId]; + code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file))); + url = `chrome-extension://${extensionId}${details.file}`; } else { - code = details.code - url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js` + code = details.code; + url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js`; } - return ipcMainUtils.invokeInWebContents(contents, false, 'CHROME_TABS_EXECUTE_SCRIPT', extensionId, url, code) -}) + return ipcMainUtils.invokeInWebContents(contents, false, 'CHROME_TABS_EXECUTE_SCRIPT', extensionId, url, code); +}); exports.getContentScripts = () => { - return Object.values(contentScripts) -} + return Object.values(contentScripts); +}; // Transfer the content scripts to renderer. -const contentScripts = {} +const contentScripts = {}; const injectContentScripts = function (manifest) { - if (contentScripts[manifest.name] || !manifest.content_scripts) return + if (contentScripts[manifest.name] || !manifest.content_scripts) return; const readArrayOfFiles = function (relativePath) { return { url: `chrome-extension://${manifest.extensionId}/${relativePath}`, code: String(fs.readFileSync(path.join(manifest.srcDirectory, relativePath))) - } - } + }; + }; const contentScriptToEntry = function (script) { return { @@ -341,25 +341,25 @@ const injectContentScripts = function (manifest) { css: script.css ? script.css.map(readArrayOfFiles) : [], runAt: script.run_at || 'document_idle', allFrames: script.all_frames || false - } - } + }; + }; try { const entry = { extensionId: manifest.extensionId, contentScripts: manifest.content_scripts.map(contentScriptToEntry) - } - contentScripts[manifest.name] = entry + }; + contentScripts[manifest.name] = entry; } catch (e) { - console.error('Failed to read content scripts', e) + console.error('Failed to read content scripts', e); } -} +}; const removeContentScripts = function (manifest) { - if (!contentScripts[manifest.name]) return + if (!contentScripts[manifest.name]) return; - delete contentScripts[manifest.name] -} + delete contentScripts[manifest.name]; +}; // Transfer the |manifest| to a format that can be recognized by the // |DevToolsAPI.addExtensions|. @@ -369,166 +369,166 @@ const manifestToExtensionInfo = function (manifest) { srcDirectory: manifest.srcDirectory, name: manifest.name, exposeExperimentalAPIs: true - } -} + }; +}; // Load the extensions for the window. const loadExtension = function (manifest) { - startBackgroundPages(manifest) - injectContentScripts(manifest) -} + startBackgroundPages(manifest); + injectContentScripts(manifest); +}; const loadDevToolsExtensions = function (win, manifests) { - if (!win.devToolsWebContents) return + if (!win.devToolsWebContents) return; - manifests.forEach(loadExtension) + manifests.forEach(loadExtension); - const extensionInfoArray = manifests.map(manifestToExtensionInfo) + const extensionInfoArray = manifests.map(manifestToExtensionInfo); extensionInfoArray.forEach((extension) => { - win.devToolsWebContents._grantOriginAccess(extension.startPage) - }) + win.devToolsWebContents._grantOriginAccess(extension.startPage); + }); extensionInfoArray.forEach((extensionInfo) => { - win.devToolsWebContents.executeJavaScript(`Extensions.extensionServer._addExtension(${JSON.stringify(extensionInfo)})`) - }) -} + win.devToolsWebContents.executeJavaScript(`Extensions.extensionServer._addExtension(${JSON.stringify(extensionInfo)})`); + }); +}; app.on('web-contents-created', function (event, webContents) { - if (!isWindowOrWebView(webContents)) return + if (!isWindowOrWebView(webContents)) return; - hookWebContentsEvents(webContents) + hookWebContentsEvents(webContents); webContents.on('devtools-opened', function () { - loadDevToolsExtensions(webContents, Object.values(manifestMap)) - }) -}) + loadDevToolsExtensions(webContents, Object.values(manifestMap)); + }); +}); // The chrome-extension: can map a extension URL request to real file path. const chromeExtensionHandler = function (request, callback) { - const parsed = url.parse(request.url) - if (!parsed.hostname || !parsed.path) return callback() + const parsed = url.parse(request.url); + if (!parsed.hostname || !parsed.path) return callback(); - const manifest = manifestMap[parsed.hostname] - if (!manifest) return callback() + const manifest = manifestMap[parsed.hostname]; + if (!manifest) return callback(); - const page = backgroundPages[parsed.hostname] + const page = backgroundPages[parsed.hostname]; if (page && parsed.path === `/${page.name}`) { // Disabled due to false positive in StandardJS // eslint-disable-next-line standard/no-callback-literal return callback({ mimeType: 'text/html', data: page.html - }) + }); } fs.readFile(path.join(manifest.srcDirectory, parsed.path), function (err, content) { if (err) { // Disabled due to false positive in StandardJS // eslint-disable-next-line standard/no-callback-literal - return callback(-6) // FILE_NOT_FOUND + return callback(-6); // FILE_NOT_FOUND } else { - return callback(content) + return callback(content); } - }) -} + }); +}; app.on('session-created', function (ses) { - ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler) -}) + ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler); +}); // The persistent path of "DevTools Extensions" preference file. -let loadedDevToolsExtensionsPath = null +let loadedDevToolsExtensionsPath = null; app.on('will-quit', function () { try { const loadedDevToolsExtensions = Array.from(devToolsExtensionNames) - .map(name => manifestNameMap[name].srcDirectory) + .map(name => manifestNameMap[name].srcDirectory); if (loadedDevToolsExtensions.length > 0) { try { - fs.mkdirSync(path.dirname(loadedDevToolsExtensionsPath)) + fs.mkdirSync(path.dirname(loadedDevToolsExtensionsPath)); } catch { // Ignore error } - fs.writeFileSync(loadedDevToolsExtensionsPath, JSON.stringify(loadedDevToolsExtensions)) + fs.writeFileSync(loadedDevToolsExtensionsPath, JSON.stringify(loadedDevToolsExtensions)); } else { - fs.unlinkSync(loadedDevToolsExtensionsPath) + fs.unlinkSync(loadedDevToolsExtensionsPath); } } catch { // Ignore error } -}) +}); // We can not use protocol or BrowserWindow until app is ready. app.once('ready', function () { // The public API to add/remove extensions. BrowserWindow.addExtension = function (srcDirectory) { - const manifest = getManifestFromPath(srcDirectory) + const manifest = getManifestFromPath(srcDirectory); if (manifest) { - loadExtension(manifest) + loadExtension(manifest); for (const webContents of getAllWebContents()) { if (isWindowOrWebView(webContents)) { - loadDevToolsExtensions(webContents, [manifest]) + loadDevToolsExtensions(webContents, [manifest]); } } - return manifest.name + return manifest.name; } - } + }; BrowserWindow.removeExtension = function (name) { - const manifest = manifestNameMap[name] - if (!manifest) return + const manifest = manifestNameMap[name]; + if (!manifest) return; - removeBackgroundPages(manifest) - removeContentScripts(manifest) - delete manifestMap[manifest.extensionId] - delete manifestNameMap[name] - } + removeBackgroundPages(manifest); + removeContentScripts(manifest); + delete manifestMap[manifest.extensionId]; + delete manifestNameMap[name]; + }; BrowserWindow.getExtensions = function () { - const extensions = {} + const extensions = {}; Object.keys(manifestNameMap).forEach(function (name) { - const manifest = manifestNameMap[name] - extensions[name] = { name: manifest.name, version: manifest.version } - }) - return extensions - } + const manifest = manifestNameMap[name]; + extensions[name] = { name: manifest.name, version: manifest.version }; + }); + return extensions; + }; BrowserWindow.addDevToolsExtension = function (srcDirectory) { - const manifestName = BrowserWindow.addExtension(srcDirectory) + const manifestName = BrowserWindow.addExtension(srcDirectory); if (manifestName) { - devToolsExtensionNames.add(manifestName) + devToolsExtensionNames.add(manifestName); } - return manifestName - } + return manifestName; + }; BrowserWindow.removeDevToolsExtension = function (name) { - BrowserWindow.removeExtension(name) - devToolsExtensionNames.delete(name) - } + BrowserWindow.removeExtension(name); + devToolsExtensionNames.delete(name); + }; BrowserWindow.getDevToolsExtensions = function () { - const extensions = BrowserWindow.getExtensions() - const devExtensions = {} + const extensions = BrowserWindow.getExtensions(); + const devExtensions = {}; Array.from(devToolsExtensionNames).forEach(function (name) { - if (!extensions[name]) return - devExtensions[name] = extensions[name] - }) - return devExtensions - } + if (!extensions[name]) return; + devExtensions[name] = extensions[name]; + }); + return devExtensions; + }; // Load persisted extensions. - loadedDevToolsExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions') + loadedDevToolsExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions'); try { - const loadedDevToolsExtensions = JSON.parse(fs.readFileSync(loadedDevToolsExtensionsPath)) + const loadedDevToolsExtensions = JSON.parse(fs.readFileSync(loadedDevToolsExtensionsPath)); if (Array.isArray(loadedDevToolsExtensions)) { for (const srcDirectory of loadedDevToolsExtensions) { // Start background pages and set content scripts. - BrowserWindow.addDevToolsExtension(srcDirectory) + BrowserWindow.addDevToolsExtension(srcDirectory); } } } catch (error) { if (process.env.ELECTRON_ENABLE_LOGGING && error.code !== 'ENOENT') { - console.error('Failed to load browser extensions from directory:', loadedDevToolsExtensionsPath) - console.error(error) + console.error('Failed to load browser extensions from directory:', loadedDevToolsExtensionsPath); + console.error(error); } } -}) +}); diff --git a/lib/browser/crash-reporter-init.js b/lib/browser/crash-reporter-init.js index 4df564e8b3a29..566e320cf59fc 100644 --- a/lib/browser/crash-reporter-init.js +++ b/lib/browser/crash-reporter-init.js @@ -1,25 +1,25 @@ -'use strict' +'use strict'; -const { app } = require('electron') -const path = require('path') +const { app } = require('electron'); +const path = require('path'); const getTempDirectory = function () { try { - return app.getPath('temp') + return app.getPath('temp'); } catch { // Delibrately laze-load the os module, this file is on the hot // path when booting Electron and os takes between 5 - 8ms to load and we do not need it yet - return require('os').tmpdir() + return require('os').tmpdir(); } -} +}; exports.crashReporterInit = function (options) { - const productName = options.productName || app.name - const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`) + const productName = options.productName || app.name; + const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`); return { productName, crashesDirectory, appVersion: app.getVersion() - } -} + }; +}; diff --git a/lib/browser/default-menu.ts b/lib/browser/default-menu.ts index 9f15002ff5ad6..b71c4f07d632c 100644 --- a/lib/browser/default-menu.ts +++ b/lib/browser/default-menu.ts @@ -1,11 +1,11 @@ -import { shell, Menu } from 'electron' +import { shell, Menu } from 'electron'; -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); -const isMac = process.platform === 'darwin' +const isMac = process.platform === 'darwin'; export const setDefaultApplicationMenu = () => { - if (v8Util.getHiddenValue(global, 'applicationMenuSet')) return + if (v8Util.getHiddenValue(global, 'applicationMenuSet')) return; const helpMenu: Electron.MenuItemConstructorOptions = { role: 'help', @@ -13,32 +13,32 @@ export const setDefaultApplicationMenu = () => { { label: 'Learn More', click: async () => { - await shell.openExternal('https://electronjs.org') + await shell.openExternal('https://electronjs.org'); } }, { label: 'Documentation', click: async () => { - const version = process.versions.electron - await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`) + const version = process.versions.electron; + await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`); } }, { label: 'Community Discussions', click: async () => { - await shell.openExternal('https://discuss.atom.io/c/electron') + await shell.openExternal('https://discuss.atom.io/c/electron'); } }, { label: 'Search Issues', click: async () => { - await shell.openExternal('https://github.com/electron/electron/issues') + await shell.openExternal('https://github.com/electron/electron/issues'); } } ] - } + }; - const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' } + const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' }; const template: Electron.MenuItemConstructorOptions[] = [ ...(isMac ? [macAppMenu] : []), { role: 'fileMenu' }, @@ -46,8 +46,8 @@ export const setDefaultApplicationMenu = () => { { role: 'viewMenu' }, { role: 'windowMenu' }, helpMenu - ] + ]; - const menu = Menu.buildFromTemplate(template) - Menu.setApplicationMenu(menu) -} + const menu = Menu.buildFromTemplate(template); + Menu.setApplicationMenu(menu); +}; diff --git a/lib/browser/desktop-capturer.ts b/lib/browser/desktop-capturer.ts index 2797b1c9c1ac8..aa058ac74f4fe 100644 --- a/lib/browser/desktop-capturer.ts +++ b/lib/browser/desktop-capturer.ts @@ -1,66 +1,66 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'events'; -const { createDesktopCapturer } = process.electronBinding('desktop_capturer') +const { createDesktopCapturer } = process.electronBinding('desktop_capturer'); -const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b) +const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b); let currentlyRunning: { options: ElectronInternal.GetSourcesOptions; getSources: Promise; -}[] = [] +}[] = []; export const getSources = (event: Electron.IpcMainEvent, options: ElectronInternal.GetSourcesOptions) => { for (const running of currentlyRunning) { if (deepEqual(running.options, options)) { // If a request is currently running for the same options // return that promise - return running.getSources + return running.getSources; } } const getSources = new Promise((resolve, reject) => { - let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer() + let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer(); const stopRunning = () => { if (capturer) { - capturer.emit = null - capturer = null + capturer.emit = null; + capturer = null; } // Remove from currentlyRunning once we resolve or reject - currentlyRunning = currentlyRunning.filter(running => running.options !== options) - } + currentlyRunning = currentlyRunning.filter(running => running.options !== options); + }; - const emitter = new EventEmitter() + const emitter = new EventEmitter(); emitter.once('error', (event, error: string) => { - stopRunning() - reject(error) - }) + stopRunning(); + reject(error); + }); emitter.once('finished', (event, sources: Electron.DesktopCapturerSource[], fetchWindowIcons: boolean) => { - stopRunning() + stopRunning(); resolve(sources.map(source => ({ id: source.id, name: source.name, thumbnail: source.thumbnail.toDataURL(), display_id: source.display_id, appIcon: (fetchWindowIcons && source.appIcon) ? source.appIcon.toDataURL() : null - }))) - }) + }))); + }); - capturer.emit = emitter.emit.bind(emitter) - capturer.startHandling(options.captureWindow, options.captureScreen, options.thumbnailSize, options.fetchWindowIcons) + capturer.emit = emitter.emit.bind(emitter); + capturer.startHandling(options.captureWindow, options.captureScreen, options.thumbnailSize, options.fetchWindowIcons); // If the WebContents is destroyed before receiving result, just remove the // reference to emit and the capturer itself so that it never dispatches // back to the renderer - event.sender.once('destroyed', () => stopRunning()) - }) + event.sender.once('destroyed', () => stopRunning()); + }); currentlyRunning.push({ options, getSources - }) + }); - return getSources -} + return getSources; +}; diff --git a/lib/browser/devtools.ts b/lib/browser/devtools.ts index 090ed6be82546..2e134979494fd 100644 --- a/lib/browser/devtools.ts +++ b/lib/browser/devtools.ts @@ -1,9 +1,9 @@ -import { dialog, Menu } from 'electron' -import * as fs from 'fs' -import * as url from 'url' +import { dialog, Menu } from 'electron'; +import * as fs from 'fs'; +import * as url from 'url'; -import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal' -import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils' +import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal'; +import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils'; const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id: number) => void) { return items.map(function (item) { @@ -23,15 +23,15 @@ const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id: type: 'normal', label: item.label, enabled: item.enabled - } + }; if (item.id != null) { - transformed.click = () => handler(item.id) + transformed.click = () => handler(item.id); } - return transformed - }) -} + return transformed; + }); +}; const getEditMenuItems = function (): Electron.MenuItemConstructorOptions[] { return [ @@ -44,56 +44,56 @@ const getEditMenuItems = function (): Electron.MenuItemConstructorOptions[] { { role: 'pasteAndMatchStyle' }, { role: 'delete' }, { role: 'selectAll' } - ] -} + ]; +}; const isChromeDevTools = function (pageURL: string) { - const { protocol } = url.parse(pageURL) - return protocol === 'devtools:' -} + const { protocol } = url.parse(pageURL); + return protocol === 'devtools:'; +}; const assertChromeDevTools = function (contents: Electron.WebContents, api: string) { - const pageURL = contents._getURL() + const pageURL = contents._getURL(); if (!isChromeDevTools(pageURL)) { - console.error(`Blocked ${pageURL} from calling ${api}`) - throw new Error(`Blocked ${api}`) + console.error(`Blocked ${pageURL} from calling ${api}`); + throw new Error(`Blocked ${api}`); } -} +}; ipcMainInternal.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron.IpcMainInvokeEvent, items: ContextMenuItem[], isEditMenu: boolean) { return new Promise(resolve => { - assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()') + assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()'); - const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve) - const menu = Menu.buildFromTemplate(template) - const window = event.sender.getOwnerBrowserWindow() + const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve); + const menu = Menu.buildFromTemplate(template); + const window = event.sender.getOwnerBrowserWindow(); - menu.popup({ window, callback: () => resolve() }) - }) -}) + menu.popup({ window, callback: () => resolve() }); + }); +}); ipcMainInternal.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Electron.IpcMainInvokeEvent) { - assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()') + assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()'); - const result = await dialog.showOpenDialog({}) - if (result.canceled) return [] + const result = await dialog.showOpenDialog({}); + if (result.canceled) return []; - const path = result.filePaths[0] - const data = await fs.promises.readFile(path) + const path = result.filePaths[0]; + const data = await fs.promises.readFile(path); - return [path, data] -}) + return [path, data]; +}); ipcMainUtils.handleSync('ELECTRON_INSPECTOR_CONFIRM', async function (event: Electron.IpcMainInvokeEvent, message: string = '', title: string = '') { - assertChromeDevTools(event.sender, 'window.confirm()') + assertChromeDevTools(event.sender, 'window.confirm()'); const options = { message: String(message), title: String(title), buttons: ['OK', 'Cancel'], cancelId: 1 - } - const window = event.sender.getOwnerBrowserWindow() - const { response } = await dialog.showMessageBox(window, options) - return response === 0 -}) + }; + const window = event.sender.getOwnerBrowserWindow(); + const { response } = await dialog.showMessageBox(window, options); + return response === 0; +}); diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index 1720b9b6027b2..48526617031f4 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -1,13 +1,14 @@ -'use strict' +'use strict'; -const { webContents } = require('electron') -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') -const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') -const parseFeaturesString = require('@electron/internal/common/parse-features-string') -const { syncMethods, asyncMethods } = require('@electron/internal/common/web-view-methods') +const { webContents } = require('electron'); +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); +const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); +const parseFeaturesString = require('@electron/internal/common/parse-features-string'); +const { syncMethods, asyncMethods, properties } = require('@electron/internal/common/web-view-methods'); +const { serialize } = require('@electron/internal/common/type-utils'); // Doesn't exist in early initialization. -let webViewManager = null +let webViewManager = null; const supportedWebViewEvents = [ 'load-commit', @@ -42,155 +43,155 @@ const supportedWebViewEvents = [ 'found-in-page', 'did-change-theme-color', 'update-target-url' -] +]; -const guestInstances = {} -const embedderElementsMap = {} +const guestInstances = {}; +const embedderElementsMap = {}; function sanitizeOptionsForGuest (options) { - const ret = { ...options } + const ret = { ...options }; // WebContents values can't be sent over IPC. - delete ret.webContents - return ret + delete ret.webContents; + return ret; } // Create a new guest instance. const createGuest = function (embedder, params) { if (webViewManager == null) { - webViewManager = process.electronBinding('web_view_manager') + webViewManager = process.electronBinding('web_view_manager'); } const guest = webContents.create({ type: 'webview', partition: params.partition, embedder: embedder - }) - const guestInstanceId = guest.id + }); + const guestInstanceId = guest.id; guestInstances[guestInstanceId] = { guest: guest, embedder: embedder - } + }; // Clear the guest from map when it is destroyed. guest.once('destroyed', () => { - if (guestInstanceId in guestInstances) { - detachGuest(embedder, guestInstanceId) + if (Object.prototype.hasOwnProperty.call(guestInstances, guestInstanceId)) { + detachGuest(embedder, guestInstanceId); } - }) + }); // Init guest web view after attached. guest.once('did-attach', function (event) { - params = this.attachParams - delete this.attachParams + params = this.attachParams; + delete this.attachParams; - const previouslyAttached = this.viewInstanceId != null - this.viewInstanceId = params.instanceId + const previouslyAttached = this.viewInstanceId != null; + this.viewInstanceId = params.instanceId; // Only load URL and set size on first attach if (previouslyAttached) { - return + return; } if (params.src) { - const opts = {} + const opts = {}; if (params.httpreferrer) { - opts.httpReferrer = params.httpreferrer + opts.httpReferrer = params.httpreferrer; } if (params.useragent) { - opts.userAgent = params.useragent + opts.userAgent = params.useragent; } - this.loadURL(params.src, opts) + this.loadURL(params.src, opts); } - embedder.emit('did-attach-webview', event, guest) - }) + embedder.emit('did-attach-webview', event, guest); + }); const sendToEmbedder = (channel, ...args) => { if (!embedder.isDestroyed()) { - embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args) + embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args); } - } + }; // Dispatch events to embedder. const fn = function (event) { guest.on(event, function (_, ...args) { - sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', event, ...args) - }) - } + sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', event, ...args); + }); + }; for (const event of supportedWebViewEvents) { - fn(event) + fn(event); } guest.on('new-window', function (event, url, frameName, disposition, options, additionalFeatures, referrer) { sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', 'new-window', url, frameName, disposition, sanitizeOptionsForGuest(options), - additionalFeatures, referrer) - }) + additionalFeatures, referrer); + }); // Dispatch guest's IPC messages to embedder. guest.on('ipc-message-host', function (_, channel, args) { - sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args) - }) + sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args); + }); // Notify guest of embedder window visibility when it is ready // FIXME Remove once https://github.com/electron/electron/issues/6828 is fixed guest.on('dom-ready', function () { - const guestInstance = guestInstances[guestInstanceId] + const guestInstance = guestInstances[guestInstanceId]; if (guestInstance != null && guestInstance.visibilityState != null) { - guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState) + guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState); } - }) + }); // Forward internal web contents event to embedder to handle // native window.open setup guest.on('-add-new-contents', (...args) => { if (guest.getLastWebPreferences().nativeWindowOpen === true) { - const embedder = getEmbedder(guestInstanceId) + const embedder = getEmbedder(guestInstanceId); if (embedder != null) { - embedder.emit('-add-new-contents', ...args) + embedder.emit('-add-new-contents', ...args); } } - }) + }); - return guestInstanceId -} + return guestInstanceId; +}; // Attach the guest to an element of embedder. const attachGuest = function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) { - const embedder = event.sender + const embedder = event.sender; // Destroy the old guest when attaching. - const key = `${embedder.id}-${elementInstanceId}` - const oldGuestInstanceId = embedderElementsMap[key] + const key = `${embedder.id}-${elementInstanceId}`; + const oldGuestInstanceId = embedderElementsMap[key]; if (oldGuestInstanceId != null) { // Reattachment to the same guest is just a no-op. if (oldGuestInstanceId === guestInstanceId) { - return + return; } - const oldGuestInstance = guestInstances[oldGuestInstanceId] + const oldGuestInstance = guestInstances[oldGuestInstanceId]; if (oldGuestInstance) { - oldGuestInstance.guest.detachFromOuterFrame() + oldGuestInstance.guest.detachFromOuterFrame(); } } - const guestInstance = guestInstances[guestInstanceId] + const guestInstance = guestInstances[guestInstanceId]; // If this isn't a valid guest instance then do nothing. if (!guestInstance) { - throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`) + throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`); } - const { guest } = guestInstance + const { guest } = guestInstance; if (guest.hostWebContents !== event.sender) { - throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`) + throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`); } // If this guest is already attached to an element then remove it if (guestInstance.elementInstanceId) { - const oldKey = `${guestInstance.embedder.id}-${guestInstance.elementInstanceId}` - delete embedderElementsMap[oldKey] + const oldKey = `${guestInstance.embedder.id}-${guestInstance.elementInstanceId}`; + delete embedderElementsMap[oldKey]; // Remove guest from embedder if moving across web views if (guest.viewInstanceId !== params.instanceId) { - webViewManager.removeGuest(guestInstance.embedder, guestInstanceId) - guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`) + webViewManager.removeGuest(guestInstance.embedder, guestInstanceId); + guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`); } } @@ -200,12 +201,12 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false, enableRemoteModule: params.enableremotemodule, plugins: params.plugins, - zoomFactor: embedder.getZoomFactor(), + zoomFactor: embedder.zoomFactor, disablePopups: !params.allowpopups, webSecurity: !params.disablewebsecurity, enableBlinkFeatures: params.blinkfeatures, disableBlinkFeatures: params.disableblinkfeatures - } + }; // parse the 'webpreferences' attribute string, if set // this uses the same parsing rules as window.open uses for its features @@ -213,14 +214,14 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn parseFeaturesString(params.webpreferences, function (key, value) { if (value === undefined) { // no value was specified, default it to true - value = true + value = true; } - webPreferences[key] = value - }) + webPreferences[key] = value; + }); } if (params.preload) { - webPreferences.preloadURL = params.preload + webPreferences.preloadURL = params.preload; } // Security options that guest will always inherit from embedder @@ -231,185 +232,217 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn ['nodeIntegration', false], ['enableRemoteModule', false], ['sandbox', true], - ['nodeIntegrationInSubFrames', false] - ]) + ['nodeIntegrationInSubFrames', false], + ['enableWebSQL', false] + ]); // Inherit certain option values from embedder - const lastWebPreferences = embedder.getLastWebPreferences() + const lastWebPreferences = embedder.getLastWebPreferences(); for (const [name, value] of inheritedWebPreferences) { if (lastWebPreferences[name] === value) { - webPreferences[name] = value + webPreferences[name] = value; } } - embedder.emit('will-attach-webview', event, webPreferences, params) + embedder.emit('will-attach-webview', event, webPreferences, params); if (event.defaultPrevented) { - if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId - guest.destroy() - return + if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId; + guest.destroy(); + return; } - guest.attachParams = params - embedderElementsMap[key] = guestInstanceId + guest.attachParams = params; + embedderElementsMap[key] = guestInstanceId; - guest.setEmbedder(embedder) - guestInstance.embedder = embedder - guestInstance.elementInstanceId = elementInstanceId + guest.setEmbedder(embedder); + guestInstance.embedder = embedder; + guestInstance.elementInstanceId = elementInstanceId; - watchEmbedder(embedder) + watchEmbedder(embedder); - webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences) - guest.attachToIframe(embedder, embedderFrameId) -} + webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences); + guest.attachToIframe(embedder, embedderFrameId); +}; // Remove an guest-embedder relationship. const detachGuest = function (embedder, guestInstanceId) { - const guestInstance = guestInstances[guestInstanceId] + const guestInstance = guestInstances[guestInstanceId]; + + if (!guestInstance) return; + if (embedder !== guestInstance.embedder) { - return + return; } - webViewManager.removeGuest(embedder, guestInstanceId) - delete guestInstances[guestInstanceId] + webViewManager.removeGuest(embedder, guestInstanceId); + delete guestInstances[guestInstanceId]; - const key = `${embedder.id}-${guestInstance.elementInstanceId}` - delete embedderElementsMap[key] -} + const key = `${embedder.id}-${guestInstance.elementInstanceId}`; + delete embedderElementsMap[key]; +}; // Once an embedder has had a guest attached we watch it for destruction to // destroy any remaining guests. -const watchedEmbedders = new Set() +const watchedEmbedders = new Set(); const watchEmbedder = function (embedder) { if (watchedEmbedders.has(embedder)) { - return + return; } - watchedEmbedders.add(embedder) + watchedEmbedders.add(embedder); // Forward embedder window visiblity change events to guest const onVisibilityChange = function (visibilityState) { - for (const guestInstanceId in guestInstances) { - const guestInstance = guestInstances[guestInstanceId] - guestInstance.visibilityState = visibilityState + for (const guestInstanceId of Object.keys(guestInstances)) { + const guestInstance = guestInstances[guestInstanceId]; + guestInstance.visibilityState = visibilityState; if (guestInstance.embedder === embedder) { - guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState) + guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState); } } - } - embedder.on('-window-visibility-change', onVisibilityChange) + }; + embedder.on('-window-visibility-change', onVisibilityChange); embedder.once('will-destroy', () => { // Usually the guestInstances is cleared when guest is destroyed, but it // may happen that the embedder gets manually destroyed earlier than guest, // and the embedder will be invalid in the usual code path. - for (const guestInstanceId in guestInstances) { - const guestInstance = guestInstances[guestInstanceId] + for (const guestInstanceId of Object.keys(guestInstances)) { + const guestInstance = guestInstances[guestInstanceId]; if (guestInstance.embedder === embedder) { - detachGuest(embedder, parseInt(guestInstanceId)) + detachGuest(embedder, parseInt(guestInstanceId)); } } // Clear the listeners. - embedder.removeListener('-window-visibility-change', onVisibilityChange) - watchedEmbedders.delete(embedder) - }) -} + embedder.removeListener('-window-visibility-change', onVisibilityChange); + watchedEmbedders.delete(embedder); + }); +}; -const isWebViewTagEnabledCache = new WeakMap() +const isWebViewTagEnabledCache = new WeakMap(); const isWebViewTagEnabled = function (contents) { if (!isWebViewTagEnabledCache.has(contents)) { - const webPreferences = contents.getLastWebPreferences() || {} - isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag) + const webPreferences = contents.getLastWebPreferences() || {}; + isWebViewTagEnabledCache.set(contents, !!webPreferences.webviewTag); } - return isWebViewTagEnabledCache.get(contents) -} + return isWebViewTagEnabledCache.get(contents); +}; const makeSafeHandler = function (channel, handler) { return (event, ...args) => { if (isWebViewTagEnabled(event.sender)) { - return handler(event, ...args) + return handler(event, ...args); } else { - console.error(` IPC message ${channel} sent by WebContents with disabled (${event.sender.id})`) - throw new Error(' disabled') + console.error(` IPC message ${channel} sent by WebContents with disabled (${event.sender.id})`); + throw new Error(' disabled'); } - } -} + }; +}; const handleMessage = function (channel, handler) { - ipcMainInternal.handle(channel, makeSafeHandler(channel, handler)) -} + ipcMainInternal.handle(channel, makeSafeHandler(channel, handler)); +}; const handleMessageSync = function (channel, handler) { - ipcMainUtils.handleSync(channel, makeSafeHandler(channel, handler)) -} + ipcMainUtils.handleSync(channel, makeSafeHandler(channel, handler)); +}; handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) { - return createGuest(event.sender, params) -}) + return createGuest(event.sender, params); +}); handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params) { - return createGuest(event.sender, params) -}) + return createGuest(event.sender, params); +}); handleMessage('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) { try { - attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params) + attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params); } catch (error) { - console.error(`Guest attach failed: ${error}`) + console.error(`Guest attach failed: ${error}`); } -}) +}); + +handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_DETACH_GUEST', function (event, guestInstanceId) { + return detachGuest(event.sender, guestInstanceId); +}); // this message is sent by the actual ipcMainInternal.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) { - const guest = getGuest(guestInstanceId) + const guest = getGuest(guestInstanceId); if (guest === event.sender) { - event.sender.emit('focus-change', {}, focus, guestInstanceId) + event.sender.emit('focus-change', {}, focus, guestInstanceId); } else { - console.error(`focus-change for guestInstanceId: ${guestInstanceId}`) + console.error(`focus-change for guestInstanceId: ${guestInstanceId}`); } -}) +}); handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) { - const guest = getGuestForWebContents(guestInstanceId, event.sender) + const guest = getGuestForWebContents(guestInstanceId, event.sender); if (!asyncMethods.has(method)) { - throw new Error(`Invalid method: ${method}`) + throw new Error(`Invalid method: ${method}`); } - return guest[method](...args) -}) + return guest[method](...args); +}); handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', function (event, guestInstanceId, method, args) { - const guest = getGuestForWebContents(guestInstanceId, event.sender) + const guest = getGuestForWebContents(guestInstanceId, event.sender); if (!syncMethods.has(method)) { - throw new Error(`Invalid method: ${method}`) + throw new Error(`Invalid method: ${method}`); + } + + return guest[method](...args); +}); + +handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_GET', function (event, guestInstanceId, property) { + const guest = getGuestForWebContents(guestInstanceId, event.sender); + if (!properties.has(property)) { + throw new Error(`Invalid property: ${property}`); + } + + return guest[property]; +}); + +handleMessageSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_SET', function (event, guestInstanceId, property, val) { + const guest = getGuestForWebContents(guestInstanceId, event.sender); + if (!properties.has(property)) { + throw new Error(`Invalid property: ${property}`); } - return guest[method](...args) -}) + guest[property] = val; +}); + +handleMessage('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', async function (event, guestInstanceId, args) { + const guest = getGuestForWebContents(guestInstanceId, event.sender); + + return serialize(await guest.capturePage(...args)); +}); // Returns WebContents from its guest id hosted in given webContents. const getGuestForWebContents = function (guestInstanceId, contents) { - const guest = getGuest(guestInstanceId) + const guest = getGuest(guestInstanceId); if (!guest) { - throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`) + throw new Error(`Invalid guestInstanceId: ${guestInstanceId}`); } if (guest.hostWebContents !== contents) { - throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`) + throw new Error(`Access denied to guestInstanceId: ${guestInstanceId}`); } - return guest -} + return guest; +}; // Returns WebContents from its guest id. const getGuest = function (guestInstanceId) { - const guestInstance = guestInstances[guestInstanceId] - if (guestInstance != null) return guestInstance.guest -} + const guestInstance = guestInstances[guestInstanceId]; + if (guestInstance != null) return guestInstance.guest; +}; // Returns the embedder of the guest. const getEmbedder = function (guestInstanceId) { - const guestInstance = guestInstances[guestInstanceId] - if (guestInstance != null) return guestInstance.embedder -} + const guestInstance = guestInstances[guestInstanceId]; + if (guestInstance != null) return guestInstance.embedder; +}; -exports.getGuestForWebContents = getGuestForWebContents -exports.isWebViewTagEnabled = isWebViewTagEnabled +exports.getGuestForWebContents = getGuestForWebContents; +exports.isWebViewTagEnabled = isWebViewTagEnabled; diff --git a/lib/browser/guest-window-manager.js b/lib/browser/guest-window-manager.js index 66861a55c389a..a9aa32779e99f 100644 --- a/lib/browser/guest-window-manager.js +++ b/lib/browser/guest-window-manager.js @@ -1,13 +1,14 @@ -'use strict' +'use strict'; -const { BrowserWindow, webContents } = require('electron') -const { isSameOrigin } = process.electronBinding('v8_util') -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') -const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') -const parseFeaturesString = require('@electron/internal/common/parse-features-string') +const electron = require('electron'); +const { BrowserWindow } = electron; +const { isSameOrigin } = process.electronBinding('v8_util'); +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); +const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); +const parseFeaturesString = require('@electron/internal/common/parse-features-string'); -const hasProp = {}.hasOwnProperty -const frameToGuest = new Map() +const hasProp = {}.hasOwnProperty; +const frameToGuest = new Map(); // Security options that child windows will always inherit from parent windows const inheritedWebPreferences = new Map([ @@ -18,108 +19,111 @@ const inheritedWebPreferences = new Map([ ['enableRemoteModule', false], ['sandbox', true], ['webviewTag', false], - ['nodeIntegrationInSubFrames', false] -]) + ['nodeIntegrationInSubFrames', false], + ['enableWebSQL', false] +]); // Copy attribute of |parent| to |child| if it is not defined in |child|. const mergeOptions = function (child, parent, visited) { // Check for circular reference. - if (visited == null) visited = new Set() - if (visited.has(parent)) return + if (visited == null) visited = new Set(); + if (visited.has(parent)) return; - visited.add(parent) + visited.add(parent); for (const key in parent) { - if (key === 'type') continue - if (!hasProp.call(parent, key)) continue - if (key in child && key !== 'webPreferences') continue + if (key === 'type') continue; + if (!hasProp.call(parent, key)) continue; + if (key in child && key !== 'webPreferences') continue; - const value = parent[key] + const value = parent[key]; if (typeof value === 'object' && !Array.isArray(value)) { - child[key] = mergeOptions(child[key] || {}, value, visited) + child[key] = mergeOptions(child[key] || {}, value, visited); } else { - child[key] = value + child[key] = value; } } - visited.delete(parent) + visited.delete(parent); - return child -} + return child; +}; // Merge |options| with the |embedder|'s window's options. const mergeBrowserWindowOptions = function (embedder, options) { if (options.webPreferences == null) { - options.webPreferences = {} + options.webPreferences = {}; } if (embedder.browserWindowOptions != null) { - let parentOptions = embedder.browserWindowOptions + let parentOptions = embedder.browserWindowOptions; // if parent's visibility is available, that overrides 'show' flag (#12125) - const win = BrowserWindow.fromWebContents(embedder.webContents) + const win = BrowserWindow.fromWebContents(embedder.webContents); if (win != null) { - parentOptions = { ...embedder.browserWindowOptions, show: win.isVisible() } + parentOptions = { ...embedder.browserWindowOptions, show: win.isVisible() }; } // Inherit the original options if it is a BrowserWindow. - mergeOptions(options, parentOptions) + mergeOptions(options, parentOptions); } else { // Or only inherit webPreferences if it is a webview. - mergeOptions(options.webPreferences, embedder.getLastWebPreferences()) + mergeOptions(options.webPreferences, embedder.getLastWebPreferences()); } // Inherit certain option values from parent window - const webPreferences = embedder.getLastWebPreferences() + const webPreferences = embedder.getLastWebPreferences(); for (const [name, value] of inheritedWebPreferences) { if (webPreferences[name] === value) { - options.webPreferences[name] = value + options.webPreferences[name] = value; } } - // Sets correct openerId here to give correct options to 'new-window' event handler - options.webPreferences.openerId = embedder.id + if (!webPreferences.nativeWindowOpen) { + // Sets correct openerId here to give correct options to 'new-window' event handler + options.webPreferences.openerId = embedder.id; + } - return options -} + return options; +}; // Setup a new guest with |embedder| const setupGuest = function (embedder, frameName, guest, options) { // When |embedder| is destroyed we should also destroy attached guest, and if // guest is closed by user then we should prevent |embedder| from double // closing guest. - const guestId = guest.webContents.id + const guestId = guest.webContents.id; const closedByEmbedder = function () { - guest.removeListener('closed', closedByUser) - guest.destroy() - } + guest.removeListener('closed', closedByUser); + guest.destroy(); + }; const closedByUser = function () { - embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId) - embedder.removeListener('current-render-view-deleted', closedByEmbedder) - } - embedder.once('current-render-view-deleted', closedByEmbedder) - guest.once('closed', closedByUser) + embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId); + embedder.removeListener('current-render-view-deleted', closedByEmbedder); + }; + embedder.once('current-render-view-deleted', closedByEmbedder); + guest.once('closed', closedByUser); if (frameName) { - frameToGuest.set(frameName, guest) - guest.frameName = frameName + frameToGuest.set(frameName, guest); + guest.frameName = frameName; guest.once('closed', function () { - frameToGuest.delete(frameName) - }) + frameToGuest.delete(frameName); + }); } - return guestId -} + return guestId; +}; // Create a new guest created by |embedder| with |options|. const createGuest = function (embedder, url, referrer, frameName, options, postData) { - let guest = frameToGuest.get(frameName) + let guest = frameToGuest.get(frameName); if (frameName && (guest != null)) { - guest.loadURL(url) - return guest.webContents.id + guest.loadURL(url); + return guest.webContents.id; } // Remember the embedder window's id. if (options.webPreferences == null) { - options.webPreferences = {} + options.webPreferences = {}; } - guest = new BrowserWindow(options) + guest = new BrowserWindow(options); if (!options.webContents) { // We should not call `loadURL` if the window was constructed from an // existing webContents (window.open in a sandboxed renderer). @@ -128,233 +132,244 @@ const createGuest = function (embedder, url, referrer, frameName, options, postD // webContents is not necessary (it will navigate there anyway). const loadOptions = { httpReferrer: referrer - } + }; if (postData != null) { - loadOptions.postData = postData - loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded' + loadOptions.postData = postData; + loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded'; if (postData.length > 0) { - const postDataFront = postData[0].bytes.toString() - const boundary = /^--.*[^-\r\n]/.exec(postDataFront) + const postDataFront = postData[0].bytes.toString(); + const boundary = /^--.*[^-\r\n]/.exec(postDataFront); if (boundary != null) { - loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}` + loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`; } } } - guest.loadURL(url, loadOptions) + guest.loadURL(url, loadOptions); } - return setupGuest(embedder, frameName, guest, options) -} + return setupGuest(embedder, frameName, guest, options); +}; const getGuestWindow = function (guestContents) { - let guestWindow = BrowserWindow.fromWebContents(guestContents) + let guestWindow = BrowserWindow.fromWebContents(guestContents); if (guestWindow == null) { - const hostContents = guestContents.hostWebContents + const hostContents = guestContents.hostWebContents; if (hostContents != null) { - guestWindow = BrowserWindow.fromWebContents(hostContents) + guestWindow = BrowserWindow.fromWebContents(hostContents); } } if (!guestWindow) { - throw new Error('getGuestWindow failed') + throw new Error('getGuestWindow failed'); } - return guestWindow -} + return guestWindow; +}; const isChildWindow = function (sender, target) { - return target.getLastWebPreferences().openerId === sender.id -} + return target.getLastWebPreferences().openerId === sender.id; +}; const isRelatedWindow = function (sender, target) { - return isChildWindow(sender, target) || isChildWindow(target, sender) -} + return isChildWindow(sender, target) || isChildWindow(target, sender); +}; const isScriptableWindow = function (sender, target) { - return isRelatedWindow(sender, target) && isSameOrigin(sender.getURL(), target.getURL()) -} + return isRelatedWindow(sender, target) && isSameOrigin(sender.getURL(), target.getURL()); +}; const isNodeIntegrationEnabled = function (sender) { - return sender.getLastWebPreferences().nodeIntegration === true -} + return sender.getLastWebPreferences().nodeIntegration === true; +}; // Checks whether |sender| can access the |target|: const canAccessWindow = function (sender, target) { return isChildWindow(sender, target) || isScriptableWindow(sender, target) || - isNodeIntegrationEnabled(sender) -} + isNodeIntegrationEnabled(sender); +}; // Routed window.open messages with raw options ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => { - if (url == null || url === '') url = 'about:blank' - if (frameName == null) frameName = '' - if (features == null) features = '' + // This should only be allowed for senders that have nativeWindowOpen: false + { + const webPreferences = event.sender.getLastWebPreferences(); + if (webPreferences.nativeWindowOpen || webPreferences.sandbox) { + event.returnValue = null; + throw new Error('GUEST_WINDOW_MANAGER_WINDOW_OPEN denied: expected native window.open'); + } + } + if (url == null || url === '') url = 'about:blank'; + if (frameName == null) frameName = ''; + if (features == null) features = ''; - const options = {} + const options = {}; - const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor'] - const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'preload', 'javascript', 'contextIsolation', 'webviewTag'] - const disposition = 'new-window' + const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']; + const webPreferences = ['zoomFactor', 'nodeIntegration', 'enableRemoteModule', 'javascript', 'contextIsolation', 'webviewTag']; + const disposition = 'new-window'; // Used to store additional features - const additionalFeatures = [] + const additionalFeatures = []; // Parse the features parseFeaturesString(features, function (key, value) { if (value === undefined) { - additionalFeatures.push(key) + additionalFeatures.push(key); } else { // Don't allow webPreferences to be set since it must be an object // that cannot be directly overridden - if (key === 'webPreferences') return + if (key === 'webPreferences') return; if (webPreferences.includes(key)) { if (options.webPreferences == null) { - options.webPreferences = {} + options.webPreferences = {}; } - options.webPreferences[key] = value + options.webPreferences[key] = value; } else { - options[key] = value + options[key] = value; } } - }) + }); if (options.left) { if (options.x == null) { - options.x = options.left + options.x = options.left; } } if (options.top) { if (options.y == null) { - options.y = options.top + options.y = options.top; } } if (options.title == null) { - options.title = frameName + options.title = frameName; } if (options.width == null) { - options.width = 800 + options.width = 800; } if (options.height == null) { - options.height = 600 + options.height = 600; } for (const name of ints) { if (options[name] != null) { - options[name] = parseInt(options[name], 10) + options[name] = parseInt(options[name], 10); } } - const referrer = { url: '', policy: 'default' } - ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event, - url, referrer, frameName, disposition, options, additionalFeatures) -}) + const referrer = { url: '', policy: 'default' }; + internalWindowOpen(event, url, referrer, frameName, disposition, options, additionalFeatures); +}); // Routed window.open messages with fully parsed options -ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, referrer, - frameName, disposition, options, additionalFeatures, postData) { - options = mergeBrowserWindowOptions(event.sender, options) - event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer) - const { newGuest } = event +function internalWindowOpen (event, url, referrer, frameName, disposition, options, additionalFeatures, postData) { + options = mergeBrowserWindowOptions(event.sender, options); + event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer); + const { newGuest } = event; if ((event.sender.getType() === 'webview' && event.sender.getLastWebPreferences().disablePopups) || event.defaultPrevented) { if (newGuest != null) { if (options.webContents === newGuest.webContents) { // the webContents is not changed, so set defaultPrevented to false to // stop the callers of this event from destroying the webContents. - event.defaultPrevented = false + event.defaultPrevented = false; } - event.returnValue = setupGuest(event.sender, frameName, newGuest, options) + event.returnValue = setupGuest(event.sender, frameName, newGuest, options); } else { - event.returnValue = null + event.returnValue = null; } } else { - event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData) + event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData); } -}) +} const makeSafeHandler = function (handler) { return (event, guestId, ...args) => { - const guestContents = webContents.fromId(guestId) + // Access webContents via electron to prevent circular require. + const guestContents = electron.webContents.fromId(guestId); if (!guestContents) { - throw new Error(`Invalid guestId: ${guestId}`) + throw new Error(`Invalid guestId: ${guestId}`); } - return handler(event, guestContents, ...args) - } -} + return handler(event, guestContents, ...args); + }; +}; const handleMessage = function (channel, handler) { - ipcMainInternal.handle(channel, makeSafeHandler(handler)) -} + ipcMainInternal.handle(channel, makeSafeHandler(handler)); +}; const handleMessageSync = function (channel, handler) { - ipcMainUtils.handleSync(channel, makeSafeHandler(handler)) -} + ipcMainUtils.handleSync(channel, makeSafeHandler(handler)); +}; -const assertCanAccessWindow = function (contents, guestContents) { - if (!canAccessWindow(contents, guestContents)) { - console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`) - throw new Error(`Access denied to guestId: ${guestContents.id}`) +const securityCheck = function (contents, guestContents, check) { + if (!check(contents, guestContents)) { + console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`); + throw new Error(`Access denied to guestId: ${guestContents.id}`); } -} +}; const windowMethods = new Set([ 'destroy', 'focus', 'blur' -]) +]); handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestContents, method, ...args) => { - assertCanAccessWindow(event.sender, guestContents) + securityCheck(event.sender, guestContents, canAccessWindow); if (!windowMethods.has(method)) { - console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`) - throw new Error(`Invalid method: ${method}`) + console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`); + throw new Error(`Invalid method: ${method}`); } - return getGuestWindow(guestContents)[method](...args) -}) + return getGuestWindow(guestContents)[method](...args); +}); handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestContents, message, targetOrigin, sourceOrigin) => { if (targetOrigin == null) { - targetOrigin = '*' + targetOrigin = '*'; } // The W3C does not seem to have word on how postMessage should work when the // origins do not match, so we do not do |canAccessWindow| check here since // postMessage across origins is useful and not harmful. + securityCheck(event.sender, guestContents, isRelatedWindow); + if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) { - const sourceId = event.sender.id - guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) + const sourceId = event.sender.id; + guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin); } -}) +}); const webContentsMethodsAsync = new Set([ 'loadURL', 'executeJavaScript', 'print' -]) +]); handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => { - assertCanAccessWindow(event.sender, guestContents) + securityCheck(event.sender, guestContents, canAccessWindow); if (!webContentsMethodsAsync.has(method)) { - console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`) - throw new Error(`Invalid method: ${method}`) + console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`); + throw new Error(`Invalid method: ${method}`); } - return guestContents[method](...args) -}) + return guestContents[method](...args); +}); const webContentsMethodsSync = new Set([ 'getURL' -]) +]); handleMessageSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => { - assertCanAccessWindow(event.sender, guestContents) + securityCheck(event.sender, guestContents, canAccessWindow); if (!webContentsMethodsSync.has(method)) { - console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`) - throw new Error(`Invalid method: ${method}`) + console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`); + throw new Error(`Invalid method: ${method}`); } - return guestContents[method](...args) -}) + return guestContents[method](...args); +}); + +exports.internalWindowOpen = internalWindowOpen; diff --git a/lib/browser/init.ts b/lib/browser/init.ts index 47a3924d4b9ee..d1bc099b8db3d 100644 --- a/lib/browser/init.ts +++ b/lib/browser/init.ts @@ -1,45 +1,47 @@ -import { Buffer } from 'buffer' -import * as fs from 'fs' -import * as path from 'path' -import * as util from 'util' +import { Buffer } from 'buffer'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as util from 'util'; -const Module = require('module') +const Module = require('module'); // We modified the original process.argv to let node.js load the init.js, // we need to restore it here. -process.argv.splice(1, 1) +process.argv.splice(1, 1); // Clear search paths. -require('../common/reset-search-paths') +require('../common/reset-search-paths'); // Import common settings. -require('@electron/internal/common/init') +require('@electron/internal/common/init'); if (process.platform === 'win32') { // Redirect node's console to use our own implementations, since node can not // handle console output when running as GUI program. - const consoleLog = (format: any, ...args: any[]) => { - return process.log(util.format(format, ...args) + '\n') - } + const consoleLog = (...args: any[]) => { + // @ts-ignore this typing is incorrect; 'format' is an optional parameter + // See https://nodejs.org/api/util.html#util_util_format_format_args + return process.log(util.format(...args) + '\n'); + }; const streamWrite: NodeJS.WritableStream['write'] = function (chunk: Buffer | string, encoding?: any, callback?: Function) { if (Buffer.isBuffer(chunk)) { - chunk = chunk.toString(encoding) + chunk = chunk.toString(encoding); } - process.log(chunk) + process.log(chunk); if (callback) { - callback() + callback(); } - return true - } - console.log = console.error = console.warn = consoleLog - process.stdout.write = process.stderr.write = streamWrite + return true; + }; + console.log = console.error = console.warn = consoleLog; + process.stdout.write = process.stderr.write = streamWrite; } // Don't quit on fatal error. process.on('uncaughtException', function (error) { // Do nothing if the user has a custom uncaught exception handler. if (process.listenerCount('uncaughtException') > 1) { - return + return; } // Show error in GUI. @@ -48,18 +50,18 @@ process.on('uncaughtException', function (error) { // so we import it inside the handler down here import('electron') .then(({ dialog }) => { - const stack = error.stack ? error.stack : `${error.name}: ${error.message}` - const message = 'Uncaught Exception:\n' + stack - dialog.showErrorBox('A JavaScript error occurred in the main process', message) - }) -}) + const stack = error.stack ? error.stack : `${error.name}: ${error.message}`; + const message = 'Uncaught Exception:\n' + stack; + dialog.showErrorBox('A JavaScript error occurred in the main process', message); + }); +}); // Emit 'exit' event on quit. -const { app } = require('electron') +const { app } = require('electron'); app.on('quit', function (event, exitCode) { - process.emit('exit', exitCode) -}) + process.emit('exit', exitCode); +}); if (process.platform === 'win32') { // If we are a Squirrel.Windows-installed app, set app user model ID @@ -76,138 +78,140 @@ if (process.platform === 'win32') { // form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call // app.setAppUserModelId with a matching identifier so that renderer processes // will inherit this value. - const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe') + const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe'); if (fs.existsSync(updateDotExe)) { - const packageDir = path.dirname(path.resolve(updateDotExe)) - const packageName = path.basename(packageDir).replace(/\s/g, '') - const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, '') + const packageDir = path.dirname(path.resolve(updateDotExe)); + const packageName = path.basename(packageDir).replace(/\s/g, ''); + const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, ''); - app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`) + app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`); } } // Map process.exit to app.exit, which quits gracefully. -process.exit = app.exit as () => never +process.exit = app.exit as () => never; // Load the RPC server. -require('@electron/internal/browser/rpc-server') +require('@electron/internal/browser/rpc-server'); // Load the guest view manager. -require('@electron/internal/browser/guest-view-manager') -require('@electron/internal/browser/guest-window-manager') +require('@electron/internal/browser/guest-view-manager'); +require('@electron/internal/browser/guest-window-manager'); // Now we try to load app's package.json. -let packagePath = null -let packageJson = null -const searchPaths = ['app', 'app.asar', 'default_app.asar'] +let packagePath = null; +let packageJson = null; +const searchPaths = ['app', 'app.asar', 'default_app.asar']; if (process.resourcesPath) { for (packagePath of searchPaths) { try { - packagePath = path.join(process.resourcesPath, packagePath) - packageJson = Module._load(path.join(packagePath, 'package.json')) - break + packagePath = path.join(process.resourcesPath, packagePath); + packageJson = Module._load(path.join(packagePath, 'package.json')); + break; } catch { - continue + continue; } } } if (packageJson == null) { process.nextTick(function () { - return process.exit(1) - }) - throw new Error('Unable to find a valid app') + return process.exit(1); + }); + throw new Error('Unable to find a valid app'); } // Set application's version. if (packageJson.version != null) { - app.setVersion(packageJson.version) + app.setVersion(packageJson.version); } // Set application's name. if (packageJson.productName != null) { - app.name = `${packageJson.productName}`.trim() + app.name = `${packageJson.productName}`.trim(); } else if (packageJson.name != null) { - app.name = `${packageJson.name}`.trim() + app.name = `${packageJson.name}`.trim(); } // Set application's desktop name. if (packageJson.desktopName != null) { - app.setDesktopName(packageJson.desktopName) + app.setDesktopName(packageJson.desktopName); } else { - app.setDesktopName(`${app.name}.desktop`) + app.setDesktopName(`${app.name}.desktop`); } // Set v8 flags, delibrately lazy load so that apps that do not use this // feature do not pay the price if (packageJson.v8Flags != null) { - require('v8').setFlagsFromString(packageJson.v8Flags) + require('v8').setFlagsFromString(packageJson.v8Flags); } -app._setDefaultAppPaths(packagePath) +app._setDefaultAppPaths(packagePath); // Load the chrome devtools support. -require('@electron/internal/browser/devtools') +require('@electron/internal/browser/devtools'); -const features = process.electronBinding('features') +const features = process.electronBinding('features'); // Load the chrome extension support. if (!features.isExtensionsEnabled()) { - require('@electron/internal/browser/chrome-extension') + require('@electron/internal/browser/chrome-extension'); } if (features.isRemoteModuleEnabled()) { - require('@electron/internal/browser/remote/server') + require('@electron/internal/browser/remote/server'); } // Load protocol module to ensure it is populated on app ready -require('@electron/internal/browser/api/protocol') +require('@electron/internal/browser/api/protocol'); // Set main startup script of the app. -const mainStartupScript = packageJson.main || 'index.js' +const mainStartupScript = packageJson.main || 'index.js'; -const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME'] +const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME']; function currentPlatformSupportsAppIndicator () { - if (process.platform !== 'linux') return false - const currentDesktop = process.env.XDG_CURRENT_DESKTOP + if (process.platform !== 'linux') return false; + const currentDesktop = process.env.XDG_CURRENT_DESKTOP; - if (!currentDesktop) return false - if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) return true + if (!currentDesktop) return false; + if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) return true; // ubuntu based or derived session (default ubuntu one, communitheme…) supports // indicator too. - if (/ubuntu/ig.test(currentDesktop)) return true + if (/ubuntu/ig.test(currentDesktop)) return true; - return false + return false; } // Workaround for electron/electron#5050 and electron/electron#9046 +process.env.ORIGINAL_XDG_CURRENT_DESKTOP = process.env.XDG_CURRENT_DESKTOP; if (currentPlatformSupportsAppIndicator()) { - process.env.XDG_CURRENT_DESKTOP = 'Unity' + process.env.XDG_CURRENT_DESKTOP = 'Unity'; } // Quit when all windows are closed and no other one is listening to this. app.on('window-all-closed', () => { if (app.listenerCount('window-all-closed') === 1) { - app.quit() + app.quit(); } -}) +}); + +const { setDefaultApplicationMenu } = require('@electron/internal/browser/default-menu'); -Promise.all([ - import('@electron/internal/browser/default-menu'), - app.whenReady() -]).then(([{ setDefaultApplicationMenu }]) => { - // Create default menu - setDefaultApplicationMenu() -}) +// Create default menu. +// +// Note that the task must be added before loading any app, so we can make sure +// the call is maded before any user window is created, otherwise the default +// menu may show even when user explicitly hides the menu. +app.once('ready', setDefaultApplicationMenu); if (packagePath) { // Finally load app's main.js and transfer control to C++. - process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false) - Module._load(path.join(packagePath, mainStartupScript), Module, true) + process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false); + Module._load(path.join(packagePath, mainStartupScript), Module, true); } else { - console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)') - console.error('This normally means you\'ve damaged the Electron package somehow') + console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)'); + console.error('This normally means you\'ve damaged the Electron package somehow'); } diff --git a/lib/browser/ipc-main-impl.ts b/lib/browser/ipc-main-impl.ts index 8f3fc9acd6243..8d6c91bbbaaf1 100644 --- a/lib/browser/ipc-main-impl.ts +++ b/lib/browser/ipc-main-impl.ts @@ -1,33 +1,33 @@ -import { EventEmitter } from 'events' -import { IpcMainInvokeEvent } from 'electron' +import { EventEmitter } from 'events'; +import { IpcMainInvokeEvent } from 'electron'; export class IpcMainImpl extends EventEmitter { private _invokeHandlers: Map void> = new Map(); handle: Electron.IpcMain['handle'] = (method, fn) => { if (this._invokeHandlers.has(method)) { - throw new Error(`Attempted to register a second handler for '${method}'`) + throw new Error(`Attempted to register a second handler for '${method}'`); } if (typeof fn !== 'function') { - throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`) + throw new Error(`Expected handler to be a function, but found type '${typeof fn}'`); } this._invokeHandlers.set(method, async (e, ...args) => { try { - (e as any)._reply(await Promise.resolve(fn(e, ...args))) + (e as any)._reply(await Promise.resolve(fn(e, ...args))); } catch (err) { - (e as any)._throw(err) + (e as any)._throw(err); } - }) + }); } handleOnce: Electron.IpcMain['handleOnce'] = (method, fn) => { this.handle(method, (e, ...args) => { - this.removeHandler(method) - return fn(e, ...args) - }) + this.removeHandler(method); + return fn(e, ...args); + }); } removeHandler (method: string) { - this._invokeHandlers.delete(method) + this._invokeHandlers.delete(method); } } diff --git a/lib/browser/ipc-main-internal-utils.ts b/lib/browser/ipc-main-internal-utils.ts index 3c2e4322a74b6..34063d923f4b8 100644 --- a/lib/browser/ipc-main-internal-utils.ts +++ b/lib/browser/ipc-main-internal-utils.ts @@ -1,44 +1,44 @@ -import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal' +import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal'; type IPCHandler = (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any export const handleSync = function (channel: string, handler: T) { ipcMainInternal.on(channel, async (event, ...args) => { try { - event.returnValue = [null, await handler(event, ...args)] + event.returnValue = [null, await handler(event, ...args)]; } catch (error) { - event.returnValue = [error] + event.returnValue = [error]; } - }) -} + }); +}; -let nextId = 0 +let nextId = 0; export function invokeInWebContents (sender: Electron.WebContentsInternal, sendToAll: boolean, command: string, ...args: any[]) { return new Promise((resolve, reject) => { - const requestId = ++nextId - const channel = `${command}_RESPONSE_${requestId}` + const requestId = ++nextId; + const channel = `${command}_RESPONSE_${requestId}`; ipcMainInternal.on(channel, function handler ( event, error: Electron.SerializedError, result: any ) { if (event.sender !== sender) { - console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`) - return + console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`); + return; } - ipcMainInternal.removeListener(channel, handler) + ipcMainInternal.removeListener(channel, handler); if (error) { - reject(error) + reject(error); } else { - resolve(result) + resolve(result); } - }) + }); if (sendToAll) { - sender._sendInternalToAll(command, requestId, ...args) + sender._sendInternalToAll(command, requestId, ...args); } else { - sender._sendInternal(command, requestId, ...args) + sender._sendInternal(command, requestId, ...args); } - }) + }); } diff --git a/lib/browser/ipc-main-internal.ts b/lib/browser/ipc-main-internal.ts index 9f296453b4279..f6c24537d324c 100644 --- a/lib/browser/ipc-main-internal.ts +++ b/lib/browser/ipc-main-internal.ts @@ -1,6 +1,6 @@ -import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl' +import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl'; -export const ipcMainInternal = new IpcMainImpl() as ElectronInternal.IpcMainInternal +export const ipcMainInternal = new IpcMainImpl() as ElectronInternal.IpcMainInternal; // Do not throw exception when channel name is "error". -ipcMainInternal.on('error', () => {}) +ipcMainInternal.on('error', () => {}); diff --git a/lib/browser/navigation-controller.js b/lib/browser/navigation-controller.js index 5176c8ae54102..38697889947c2 100644 --- a/lib/browser/navigation-controller.js +++ b/lib/browser/navigation-controller.js @@ -1,23 +1,23 @@ -'use strict' +'use strict'; -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); // The history operation in renderer is redirected to browser. ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK', function (event) { - event.sender.goBack() -}) + event.sender.goBack(); +}); ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD', function (event) { - event.sender.goForward() -}) + event.sender.goForward(); +}); ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', function (event, offset) { - event.sender.goToOffset(offset) -}) + event.sender.goToOffset(offset); +}); ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) { - event.returnValue = event.sender.length() -}) + event.returnValue = event.sender.length(); +}); // JavaScript implementation of Chromium's NavigationController. // Instead of relying on Chromium for history control, we compeletely do history @@ -26,64 +26,64 @@ ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) { // process is restarted everytime. const NavigationController = (function () { function NavigationController (webContents) { - this.webContents = webContents - this.clearHistory() + this.webContents = webContents; + this.clearHistory(); // webContents may have already navigated to a page. if (this.webContents._getURL()) { - this.currentIndex++ - this.history.push(this.webContents._getURL()) + this.currentIndex++; + this.history.push(this.webContents._getURL()); } this.webContents.on('navigation-entry-commited', (event, url, inPage, replaceEntry) => { if (this.inPageIndex > -1 && !inPage) { // Navigated to a new page, clear in-page mark. - this.inPageIndex = -1 + this.inPageIndex = -1; } else if (this.inPageIndex === -1 && inPage && !replaceEntry) { // Started in-page navigations. - this.inPageIndex = this.currentIndex + this.inPageIndex = this.currentIndex; } if (this.pendingIndex >= 0) { // Go to index. - this.currentIndex = this.pendingIndex - this.pendingIndex = -1 - this.history[this.currentIndex] = url + this.currentIndex = this.pendingIndex; + this.pendingIndex = -1; + this.history[this.currentIndex] = url; } else if (replaceEntry) { // Non-user initialized navigation. - this.history[this.currentIndex] = url + this.history[this.currentIndex] = url; } else { // Normal navigation. Clear history. - this.history = this.history.slice(0, this.currentIndex + 1) - this.currentIndex++ - this.history.push(url) + this.history = this.history.slice(0, this.currentIndex + 1); + this.currentIndex++; + this.history.push(url); } - }) + }); } NavigationController.prototype.loadURL = function (url, options) { if (options == null) { - options = {} + options = {}; } const p = new Promise((resolve, reject) => { const resolveAndCleanup = () => { - removeListeners() - resolve() - } + removeListeners(); + resolve(); + }; const rejectAndCleanup = (errorCode, errorDescription, url) => { - const err = new Error(`${errorDescription} (${errorCode}) loading '${typeof url === 'string' ? url.substr(0, 2048) : url}'`) - Object.assign(err, { errno: errorCode, code: errorDescription, url }) - removeListeners() - reject(err) - } + const err = new Error(`${errorDescription} (${errorCode}) loading '${typeof url === 'string' ? url.substr(0, 2048) : url}'`); + Object.assign(err, { errno: errorCode, code: errorDescription, url }); + removeListeners(); + reject(err); + }; const finishListener = () => { - resolveAndCleanup() - } + resolveAndCleanup(); + }; const failListener = (event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId) => { if (isMainFrame) { - rejectAndCleanup(errorCode, errorDescription, validatedURL) + rejectAndCleanup(errorCode, errorDescription, validatedURL); } - } + }; - let navigationStarted = false + let navigationStarted = false; const navigationListener = (event, url, isSameDocument, isMainFrame, frameProcessId, frameRoutingId, navigationId) => { if (isMainFrame) { if (navigationStarted && !isSameDocument) { @@ -96,11 +96,11 @@ const NavigationController = (function () { // considered navigation events but are triggered with isSameDocument. // We can ignore these to allow virtual routing on page load as long // as the routing does not leave the document - return rejectAndCleanup(-3, 'ERR_ABORTED', url) + return rejectAndCleanup(-3, 'ERR_ABORTED', url); } - navigationStarted = true + navigationStarted = true; } - } + }; const stopLoadingListener = () => { // By the time we get here, either 'finish' or 'fail' should have fired // if the navigation occurred. However, in some situations (e.g. when @@ -110,133 +110,134 @@ const NavigationController = (function () { // TODO(jeremy): enumerate all the cases in which this can happen. If // the only one is with a bad scheme, perhaps ERR_INVALID_ARGUMENT // would be more appropriate. - rejectAndCleanup(-2, 'ERR_FAILED', url) - } + rejectAndCleanup(-2, 'ERR_FAILED', url); + }; const removeListeners = () => { - this.webContents.removeListener('did-finish-load', finishListener) - this.webContents.removeListener('did-fail-load', failListener) - this.webContents.removeListener('did-start-navigation', navigationListener) - this.webContents.removeListener('did-stop-loading', stopLoadingListener) - } - this.webContents.on('did-finish-load', finishListener) - this.webContents.on('did-fail-load', failListener) - this.webContents.on('did-start-navigation', navigationListener) - this.webContents.on('did-stop-loading', stopLoadingListener) - }) + this.webContents.removeListener('did-finish-load', finishListener); + this.webContents.removeListener('did-fail-load', failListener); + this.webContents.removeListener('did-start-navigation', navigationListener); + this.webContents.removeListener('did-stop-loading', stopLoadingListener); + }; + this.webContents.on('did-finish-load', finishListener); + this.webContents.on('did-fail-load', failListener); + this.webContents.on('did-start-navigation', navigationListener); + this.webContents.on('did-stop-loading', stopLoadingListener); + }); // Add a no-op rejection handler to silence the unhandled rejection error. - p.catch(() => {}) - this.pendingIndex = -1 - this.webContents._loadURL(url, options) - this.webContents.emit('load-url', url, options) - return p - } + p.catch(() => {}); + this.pendingIndex = -1; + this.webContents._loadURL(url, options); + this.webContents.emit('load-url', url, options); + return p; + }; NavigationController.prototype.getURL = function () { if (this.currentIndex === -1) { - return '' + return ''; } else { - return this.history[this.currentIndex] + return this.history[this.currentIndex]; } - } + }; NavigationController.prototype.stop = function () { - this.pendingIndex = -1 - return this.webContents._stop() - } + this.pendingIndex = -1; + return this.webContents._stop(); + }; NavigationController.prototype.reload = function () { - this.pendingIndex = this.currentIndex - return this.webContents._loadURL(this.getURL(), {}) - } + this.pendingIndex = this.currentIndex; + return this.webContents._loadURL(this.getURL(), {}); + }; NavigationController.prototype.reloadIgnoringCache = function () { - this.pendingIndex = this.currentIndex + this.pendingIndex = this.currentIndex; return this.webContents._loadURL(this.getURL(), { - extraHeaders: 'pragma: no-cache\n' - }) - } + extraHeaders: 'pragma: no-cache\n', + reloadIgnoringCache: true + }); + }; NavigationController.prototype.canGoBack = function () { - return this.getActiveIndex() > 0 - } + return this.getActiveIndex() > 0; + }; NavigationController.prototype.canGoForward = function () { - return this.getActiveIndex() < this.history.length - 1 - } + return this.getActiveIndex() < this.history.length - 1; + }; NavigationController.prototype.canGoToIndex = function (index) { - return index >= 0 && index < this.history.length - } + return index >= 0 && index < this.history.length; + }; NavigationController.prototype.canGoToOffset = function (offset) { - return this.canGoToIndex(this.currentIndex + offset) - } + return this.canGoToIndex(this.currentIndex + offset); + }; NavigationController.prototype.clearHistory = function () { - this.history = [] - this.currentIndex = -1 - this.pendingIndex = -1 - this.inPageIndex = -1 - } + this.history = []; + this.currentIndex = -1; + this.pendingIndex = -1; + this.inPageIndex = -1; + }; NavigationController.prototype.goBack = function () { if (!this.canGoBack()) { - return + return; } - this.pendingIndex = this.getActiveIndex() - 1 + this.pendingIndex = this.getActiveIndex() - 1; if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) { - return this.webContents._goBack() + return this.webContents._goBack(); } else { - return this.webContents._loadURL(this.history[this.pendingIndex], {}) + return this.webContents._loadURL(this.history[this.pendingIndex], {}); } - } + }; NavigationController.prototype.goForward = function () { if (!this.canGoForward()) { - return + return; } - this.pendingIndex = this.getActiveIndex() + 1 + this.pendingIndex = this.getActiveIndex() + 1; if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) { - return this.webContents._goForward() + return this.webContents._goForward(); } else { - return this.webContents._loadURL(this.history[this.pendingIndex], {}) + return this.webContents._loadURL(this.history[this.pendingIndex], {}); } - } + }; NavigationController.prototype.goToIndex = function (index) { if (!this.canGoToIndex(index)) { - return + return; } - this.pendingIndex = index - return this.webContents._loadURL(this.history[this.pendingIndex], {}) - } + this.pendingIndex = index; + return this.webContents._loadURL(this.history[this.pendingIndex], {}); + }; NavigationController.prototype.goToOffset = function (offset) { if (!this.canGoToOffset(offset)) { - return + return; } - const pendingIndex = this.currentIndex + offset + const pendingIndex = this.currentIndex + offset; if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) { - this.pendingIndex = pendingIndex - return this.webContents._goToOffset(offset) + this.pendingIndex = pendingIndex; + return this.webContents._goToOffset(offset); } else { - return this.goToIndex(pendingIndex) + return this.goToIndex(pendingIndex); } - } + }; NavigationController.prototype.getActiveIndex = function () { if (this.pendingIndex === -1) { - return this.currentIndex + return this.currentIndex; } else { - return this.pendingIndex + return this.pendingIndex; } - } + }; NavigationController.prototype.length = function () { - return this.history.length - } + return this.history.length; + }; - return NavigationController -})() + return NavigationController; +})(); -module.exports = NavigationController +module.exports = NavigationController; diff --git a/lib/browser/remote/objects-registry.ts b/lib/browser/remote/objects-registry.ts index 0de48c3960d6f..1142c8416fc5e 100644 --- a/lib/browser/remote/objects-registry.ts +++ b/lib/browser/remote/objects-registry.ts @@ -1,12 +1,12 @@ -'use strict' +'use strict'; -import { WebContents } from 'electron' +import { WebContents } from 'electron'; -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); const getOwnerKey = (webContents: WebContents, contextId: string) => { - return `${webContents.id}-${contextId}` -} + return `${webContents.id}-${contextId}`; +}; class ObjectsRegistry { private nextId: number = 0 @@ -23,29 +23,29 @@ class ObjectsRegistry { // registered then the already assigned ID would be returned. add (webContents: WebContents, contextId: string, obj: any) { // Get or assign an ID to the object. - const id = this.saveToStorage(obj) + const id = this.saveToStorage(obj); // Add object to the set of referenced objects. - const ownerKey = getOwnerKey(webContents, contextId) - let owner = this.owners[ownerKey] + const ownerKey = getOwnerKey(webContents, contextId); + let owner = this.owners[ownerKey]; if (!owner) { - owner = this.owners[ownerKey] = new Map() - this.registerDeleteListener(webContents, contextId) + owner = this.owners[ownerKey] = new Map(); + this.registerDeleteListener(webContents, contextId); } if (!owner.has(id)) { - owner.set(id, 0) + owner.set(id, 0); // Increase reference count if not referenced before. - this.storage[id].count++ + this.storage[id].count++; } - owner.set(id, owner.get(id)! + 1) - return id + owner.set(id, owner.get(id)! + 1); + return id; } // Get an object according to its ID. get (id: number) { - const pointer = this.storage[id] - if (pointer != null) return pointer.object + const pointer = this.storage[id]; + if (pointer != null) return pointer.object; } // Dereference an object according to its ID. @@ -60,79 +60,79 @@ class ObjectsRegistry { // For more details on why we do renderer side ref counting see // https://github.com/electron/electron/pull/17464 remove (webContents: WebContents, contextId: string, id: number, rendererSideRefCount: number) { - const ownerKey = getOwnerKey(webContents, contextId) - const owner = this.owners[ownerKey] + const ownerKey = getOwnerKey(webContents, contextId); + const owner = this.owners[ownerKey]; if (owner && owner.has(id)) { - const newRefCount = owner.get(id)! - rendererSideRefCount + const newRefCount = owner.get(id)! - rendererSideRefCount; // Only completely remove if the number of references GCed in the // renderer is the same as the number of references we sent them if (newRefCount <= 0) { // Remove the reference in owner. - owner.delete(id) + owner.delete(id); // Dereference from the storage. - this.dereference(id) + this.dereference(id); } else { - owner.set(id, newRefCount) + owner.set(id, newRefCount); } } } // Clear all references to objects refrenced by the WebContents. clear (webContents: WebContents, contextId: string) { - const ownerKey = getOwnerKey(webContents, contextId) - const owner = this.owners[ownerKey] - if (!owner) return + const ownerKey = getOwnerKey(webContents, contextId); + const owner = this.owners[ownerKey]; + if (!owner) return; - for (const id of owner.keys()) this.dereference(id) + for (const id of owner.keys()) this.dereference(id); - delete this.owners[ownerKey] + delete this.owners[ownerKey]; } // Private: Saves the object into storage and assigns an ID for it. saveToStorage (object: any) { - let id: number = v8Util.getHiddenValue(object, 'atomId') + let id: number = v8Util.getHiddenValue(object, 'atomId'); if (!id) { - id = ++this.nextId + id = ++this.nextId; this.storage[id] = { count: 0, object: object - } - v8Util.setHiddenValue(object, 'atomId', id) + }; + v8Util.setHiddenValue(object, 'atomId', id); } - return id + return id; } // Private: Dereference the object from store. dereference (id: number) { - const pointer = this.storage[id] + const pointer = this.storage[id]; if (pointer == null) { - return + return; } - pointer.count -= 1 + pointer.count -= 1; if (pointer.count === 0) { - v8Util.deleteHiddenValue(pointer.object, 'atomId') - delete this.storage[id] + v8Util.deleteHiddenValue(pointer.object, 'atomId'); + delete this.storage[id]; } } // Private: Clear the storage when renderer process is destroyed. registerDeleteListener (webContents: WebContents, contextId: string) { // contextId => ${processHostId}-${contextCount} - const processHostId = contextId.split('-')[0] + const processHostId = contextId.split('-')[0]; const listener = (_: any, deletedProcessHostId: string) => { if (deletedProcessHostId && deletedProcessHostId.toString() === processHostId) { - webContents.removeListener('render-view-deleted' as any, listener) - this.clear(webContents, contextId) + webContents.removeListener('render-view-deleted' as any, listener); + this.clear(webContents, contextId); } - } + }; // Note that the "render-view-deleted" event may not be emitted on time when // the renderer process get destroyed because of navigation, we rely on the // renderer process to send "ELECTRON_BROWSER_CONTEXT_RELEASE" message to // guard this situation. - webContents.on('render-view-deleted' as any, listener) + webContents.on('render-view-deleted' as any, listener); } } -export default new ObjectsRegistry() +export default new ObjectsRegistry(); diff --git a/lib/browser/remote/server.ts b/lib/browser/remote/server.ts index 5dcf3b8848023..337cc034e8cf3 100644 --- a/lib/browser/remote/server.ts +++ b/lib/browser/remote/server.ts @@ -1,30 +1,30 @@ -'use strict' +'use strict'; -import * as electron from 'electron' -import { EventEmitter } from 'events' -import objectsRegistry from './objects-registry' -import { ipcMainInternal } from '../ipc-main-internal' -import * as guestViewManager from '@electron/internal/browser/guest-view-manager' -import { isPromise, isSerializableObject } from '@electron/internal/common/remote/type-utils' +import * as electron from 'electron'; +import { EventEmitter } from 'events'; +import objectsRegistry from './objects-registry'; +import { ipcMainInternal } from '../ipc-main-internal'; +import * as guestViewManager from '@electron/internal/browser/guest-view-manager'; +import { isPromise, isSerializableObject } from '@electron/internal/common/type-utils'; -const v8Util = process.electronBinding('v8_util') -const eventBinding = process.electronBinding('event') -const features = process.electronBinding('features') +const v8Util = process.electronBinding('v8_util'); +const eventBinding = process.electronBinding('event'); +const features = process.electronBinding('features'); if (!features.isRemoteModuleEnabled()) { - throw new Error('remote module is disabled') + throw new Error('remote module is disabled'); } -const hasProp = {}.hasOwnProperty +const hasProp = {}.hasOwnProperty; // The internal properties of Function. const FUNCTION_PROPERTIES = [ 'length', 'name', 'arguments', 'caller', 'prototype' -] +]; // The remote functions in renderer processes. // id => Function -const rendererFunctions = v8Util.createDoubleIDWeakMap() +const rendererFunctions = v8Util.createDoubleIDWeakMap(); type ObjectMember = { name: string, @@ -36,28 +36,28 @@ type ObjectMember = { // Return the description of object's members: const getObjectMembers = function (object: any): ObjectMember[] { - let names = Object.getOwnPropertyNames(object) + let names = Object.getOwnPropertyNames(object); // For Function, we should not override following properties even though they // are "own" properties. if (typeof object === 'function') { names = names.filter((name) => { - return !FUNCTION_PROPERTIES.includes(name) - }) + return !FUNCTION_PROPERTIES.includes(name); + }); } // Map properties to descriptors. return names.map((name) => { - const descriptor = Object.getOwnPropertyDescriptor(object, name)! - let type: ObjectMember['type'] - let writable = false + const descriptor = Object.getOwnPropertyDescriptor(object, name)!; + let type: ObjectMember['type']; + let writable = false; if (descriptor.get === undefined && typeof object[name] === 'function') { - type = 'method' + type = 'method'; } else { - if (descriptor.set || descriptor.writable) writable = true - type = 'get' + if (descriptor.set || descriptor.writable) writable = true; + type = 'get'; } - return { name, enumerable: descriptor.enumerable, writable, type } - }) -} + return { name, enumerable: descriptor.enumerable, writable, type }; + }); +}; type ObjProtoDescriptor = { members: ObjectMember[], @@ -66,13 +66,13 @@ type ObjProtoDescriptor = { // Return the description of object's prototype. const getObjectPrototype = function (object: any): ObjProtoDescriptor { - const proto = Object.getPrototypeOf(object) - if (proto === null || proto === Object.prototype) return null + const proto = Object.getPrototypeOf(object); + if (proto === null || proto === Object.prototype) return null; return { members: getObjectMembers(proto), proto: getObjectPrototype(proto) - } -} + }; +}; type MetaType = { type: 'number', @@ -119,25 +119,25 @@ type MetaType = { // Convert a real value into meta data. const valueToMeta = function (sender: electron.WebContents, contextId: string, value: any, optimizeSimpleObject = false): MetaType { // Determine the type of value. - let type: MetaType['type'] = typeof value + let type: MetaType['type'] = typeof value; if (type === 'object') { // Recognize certain types of objects. if (value instanceof Buffer) { - type = 'buffer' + type = 'buffer'; } else if (Array.isArray(value)) { - type = 'array' + type = 'array'; } else if (value instanceof Error) { - type = 'error' + type = 'error'; } else if (isSerializableObject(value)) { - type = 'value' + type = 'value'; } else if (isPromise(value)) { - type = 'promise' + type = 'promise'; } else if (hasProp.call(value, 'callee') && value.length != null) { // Treat the arguments object as array. - type = 'array' + type = 'array'; } else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) { // Treat simple objects as value. - type = 'value' + type = 'value'; } } @@ -146,7 +146,7 @@ const valueToMeta = function (sender: electron.WebContents, contextId: string, v return { type, members: value.map((el: any) => valueToMeta(sender, contextId, el, optimizeSimpleObject)) - } + }; } else if (type === 'object' || type === 'function') { return { type, @@ -157,20 +157,20 @@ const valueToMeta = function (sender: electron.WebContents, contextId: string, v id: objectsRegistry.add(sender, contextId, value), members: getObjectMembers(value), proto: getObjectPrototype(value) - } + }; } else if (type === 'buffer') { - return { type, value } + return { type, value }; } else if (type === 'promise') { // Add default handler to prevent unhandled rejections in main process // Instead they should appear in the renderer process - value.then(function () {}, function () {}) + value.then(function () {}, function () {}); return { type, then: valueToMeta(sender, contextId, function (onFulfilled: Function, onRejected: Function) { - value.then(onFulfilled, onRejected) + value.then(onFulfilled, onRejected); }) - } + }; } else if (type === 'error') { return { type, @@ -179,42 +179,42 @@ const valueToMeta = function (sender: electron.WebContents, contextId: string, v name, value: valueToMeta(sender, contextId, value[name]) })) - } + }; } else { return { type: 'value', value - } + }; } -} +}; const throwRPCError = function (message: string) { - const error = new Error(message) as Error & {code: string, errno: number} - error.code = 'EBADRPC' - error.errno = -72 - throw error -} + const error = new Error(message) as Error & {code: string, errno: number}; + error.code = 'EBADRPC'; + error.errno = -72; + throw error; +}; const removeRemoteListenersAndLogWarning = (sender: any, callIntoRenderer: (...args: any[]) => void) => { - const location = v8Util.getHiddenValue(callIntoRenderer, 'location') + const location = v8Util.getHiddenValue(callIntoRenderer, 'location'); let message = `Attempting to call a function in a renderer window that has been closed or released.` + - `\nFunction provided here: ${location}` + `\nFunction provided here: ${location}`; if (sender instanceof EventEmitter) { const remoteEvents = sender.eventNames().filter((eventName) => { - return sender.listeners(eventName).includes(callIntoRenderer) - }) + return sender.listeners(eventName).includes(callIntoRenderer); + }); if (remoteEvents.length > 0) { - message += `\nRemote event names: ${remoteEvents.join(', ')}` + message += `\nRemote event names: ${remoteEvents.join(', ')}`; remoteEvents.forEach((eventName) => { - sender.removeListener(eventName as any, callIntoRenderer) - }) + sender.removeListener(eventName as any, callIntoRenderer); + }); } } - console.warn(message) -} + console.warn(message); +}; type MetaTypeFromRenderer = { type: 'value', @@ -249,317 +249,317 @@ const fakeConstructor = (constructor: Function, name: string) => new Proxy(Object, { get (target, prop, receiver) { if (prop === 'name') { - return name + return name; } else { - return Reflect.get(target, prop, receiver) + return Reflect.get(target, prop, receiver); } } - }) + }); // Convert array of meta data from renderer into array of real values. const unwrapArgs = function (sender: electron.WebContents, frameId: number, contextId: string, args: any[]) { const metaToValue = function (meta: MetaTypeFromRenderer): any { switch (meta.type) { case 'value': - return meta.value + return meta.value; case 'remote-object': - return objectsRegistry.get(meta.id) + return objectsRegistry.get(meta.id); case 'array': - return unwrapArgs(sender, frameId, contextId, meta.value) + return unwrapArgs(sender, frameId, contextId, meta.value); case 'buffer': - return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength) + return Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength); case 'promise': return Promise.resolve({ then: metaToValue(meta.then) - }) + }); case 'object': { const ret: any = meta.name !== 'Object' ? Object.create({ constructor: fakeConstructor(Object, meta.name) - }) : {} + }) : {}; for (const { name, value } of meta.members) { - ret[name] = metaToValue(value) + ret[name] = metaToValue(value); } - return ret + return ret; } case 'function-with-return-value': - const returnValue = metaToValue(meta.value) + const returnValue = metaToValue(meta.value); return function () { - return returnValue - } + return returnValue; + }; case 'function': { // Merge contextId and meta.id, since meta.id can be the same in // different webContents. - const objectId = [contextId, meta.id] + const objectId = [contextId, meta.id]; // Cache the callbacks in renderer. if (rendererFunctions.has(objectId)) { - return rendererFunctions.get(objectId) + return rendererFunctions.get(objectId); } const callIntoRenderer = function (this: any, ...args: any[]) { - let succeed = false + let succeed = false; if (!sender.isDestroyed()) { - succeed = (sender as any)._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args)) + succeed = (sender as any)._sendToFrameInternal(frameId, 'ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args)); } if (!succeed) { - removeRemoteListenersAndLogWarning(this, callIntoRenderer) + removeRemoteListenersAndLogWarning(this, callIntoRenderer); } - } - v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location) - Object.defineProperty(callIntoRenderer, 'length', { value: meta.length }) + }; + v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location); + Object.defineProperty(callIntoRenderer, 'length', { value: meta.length }); - v8Util.setRemoteCallbackFreer(callIntoRenderer, contextId, meta.id, sender) - rendererFunctions.set(objectId, callIntoRenderer) - return callIntoRenderer + v8Util.setRemoteCallbackFreer(callIntoRenderer, frameId, contextId, meta.id, sender); + rendererFunctions.set(objectId, callIntoRenderer); + return callIntoRenderer; } default: - throw new TypeError(`Unknown type: ${(meta as any).type}`) + throw new TypeError(`Unknown type: ${(meta as any).type}`); } - } - return args.map(metaToValue) -} + }; + return args.map(metaToValue); +}; const isRemoteModuleEnabledImpl = function (contents: electron.WebContents) { - const webPreferences = (contents as any).getLastWebPreferences() || {} - return !!webPreferences.enableRemoteModule -} + const webPreferences = (contents as any).getLastWebPreferences() || {}; + return !!webPreferences.enableRemoteModule; +}; -const isRemoteModuleEnabledCache = new WeakMap() +const isRemoteModuleEnabledCache = new WeakMap(); const isRemoteModuleEnabled = function (contents: electron.WebContents) { if (!isRemoteModuleEnabledCache.has(contents)) { - isRemoteModuleEnabledCache.set(contents, isRemoteModuleEnabledImpl(contents)) + isRemoteModuleEnabledCache.set(contents, isRemoteModuleEnabledImpl(contents)); } - return isRemoteModuleEnabledCache.get(contents) -} + return isRemoteModuleEnabledCache.get(contents); +}; const handleRemoteCommand = function (channel: string, handler: (event: ElectronInternal.IpcMainInternalEvent, contextId: string, ...args: any[]) => void) { ipcMainInternal.on(channel, (event, contextId: string, ...args: any[]) => { - let returnValue + let returnValue; if (!isRemoteModuleEnabled(event.sender)) { - event.returnValue = null - return + event.returnValue = null; + return; } try { - returnValue = handler(event, contextId, ...args) + returnValue = handler(event, contextId, ...args); } catch (error) { returnValue = { type: 'exception', value: valueToMeta(event.sender, contextId, error) - } + }; } if (returnValue !== undefined) { - event.returnValue = returnValue + event.returnValue = returnValue; } - }) -} + }); +}; const emitCustomEvent = function (contents: electron.WebContents, eventName: string, ...args: any[]) { - const event = eventBinding.createWithSender(contents) + const event = eventBinding.createWithSender(contents); - electron.app.emit(eventName, event, contents, ...args) - contents.emit(eventName, event, ...args) + electron.app.emit(eventName, event, contents, ...args); + contents.emit(eventName, event, ...args); - return event -} + return event; +}; const logStack = function (contents: electron.WebContents, code: string, stack: string | undefined) { if (stack) { - console.warn(`WebContents (${contents.id}): ${code}`, stack) + console.warn(`WebContents (${contents.id}): ${code}`, stack); } -} +}; handleRemoteCommand('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', function (event, contextId, passedContextId, id) { - const objectId = [passedContextId, id] + const objectId = [passedContextId, id]; if (!rendererFunctions.has(objectId)) { // Do nothing if the error has already been reported before. - return + return; } - removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId)) -}) + removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId)); +}); handleRemoteCommand('ELECTRON_BROWSER_REQUIRE', function (event, contextId, moduleName, stack) { - logStack(event.sender, `remote.require('${moduleName}')`, stack) - const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName) + logStack(event.sender, `remote.require('${moduleName}')`, stack); + const customEvent = emitCustomEvent(event.sender, 'remote-require', moduleName); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { - throw new Error(`Blocked remote.require('${moduleName}')`) + throw new Error(`Blocked remote.require('${moduleName}')`); } else { - customEvent.returnValue = process.mainModule!.require(moduleName) + customEvent.returnValue = process.mainModule!.require(moduleName); } } - return valueToMeta(event.sender, contextId, customEvent.returnValue) -}) + return valueToMeta(event.sender, contextId, customEvent.returnValue); +}); handleRemoteCommand('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, moduleName, stack) { - logStack(event.sender, `remote.getBuiltin('${moduleName}')`, stack) - const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName) + logStack(event.sender, `remote.getBuiltin('${moduleName}')`, stack); + const customEvent = emitCustomEvent(event.sender, 'remote-get-builtin', moduleName); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { - throw new Error(`Blocked remote.getBuiltin('${moduleName}')`) + throw new Error(`Blocked remote.getBuiltin('${moduleName}')`); } else { - customEvent.returnValue = (electron as any)[moduleName] + customEvent.returnValue = (electron as any)[moduleName]; } } - return valueToMeta(event.sender, contextId, customEvent.returnValue) -}) + return valueToMeta(event.sender, contextId, customEvent.returnValue); +}); handleRemoteCommand('ELECTRON_BROWSER_GLOBAL', function (event, contextId, globalName, stack) { - logStack(event.sender, `remote.getGlobal('${globalName}')`, stack) - const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName) + logStack(event.sender, `remote.getGlobal('${globalName}')`, stack); + const customEvent = emitCustomEvent(event.sender, 'remote-get-global', globalName); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { - throw new Error(`Blocked remote.getGlobal('${globalName}')`) + throw new Error(`Blocked remote.getGlobal('${globalName}')`); } else { - customEvent.returnValue = (global as any)[globalName] + customEvent.returnValue = (global as any)[globalName]; } } - return valueToMeta(event.sender, contextId, customEvent.returnValue) -}) + return valueToMeta(event.sender, contextId, customEvent.returnValue); +}); handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId, stack) { - logStack(event.sender, 'remote.getCurrentWindow()', stack) - const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window') + logStack(event.sender, 'remote.getCurrentWindow()', stack); + const customEvent = emitCustomEvent(event.sender, 'remote-get-current-window'); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { - throw new Error('Blocked remote.getCurrentWindow()') + throw new Error('Blocked remote.getCurrentWindow()'); } else { - customEvent.returnValue = event.sender.getOwnerBrowserWindow() + customEvent.returnValue = event.sender.getOwnerBrowserWindow(); } } - return valueToMeta(event.sender, contextId, customEvent.returnValue) -}) + return valueToMeta(event.sender, contextId, customEvent.returnValue); +}); handleRemoteCommand('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId, stack) { - logStack(event.sender, 'remote.getCurrentWebContents()', stack) - const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents') + logStack(event.sender, 'remote.getCurrentWebContents()', stack); + const customEvent = emitCustomEvent(event.sender, 'remote-get-current-web-contents'); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { - throw new Error('Blocked remote.getCurrentWebContents()') + throw new Error('Blocked remote.getCurrentWebContents()'); } else { - customEvent.returnValue = event.sender + customEvent.returnValue = event.sender; } } - return valueToMeta(event.sender, contextId, customEvent.returnValue) -}) + return valueToMeta(event.sender, contextId, customEvent.returnValue); +}); handleRemoteCommand('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) - const constructor = objectsRegistry.get(id) + args = unwrapArgs(event.sender, event.frameId, contextId, args); + const constructor = objectsRegistry.get(id); if (constructor == null) { - throwRPCError(`Cannot call constructor on missing remote object ${id}`) + throwRPCError(`Cannot call constructor on missing remote object ${id}`); } - return valueToMeta(event.sender, contextId, new constructor(...args)) -}) + return valueToMeta(event.sender, contextId, new constructor(...args)); +}); handleRemoteCommand('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) - const func = objectsRegistry.get(id) + args = unwrapArgs(event.sender, event.frameId, contextId, args); + const func = objectsRegistry.get(id); if (func == null) { - throwRPCError(`Cannot call function on missing remote object ${id}`) + throwRPCError(`Cannot call function on missing remote object ${id}`); } try { - return valueToMeta(event.sender, contextId, func(...args), true) + return valueToMeta(event.sender, contextId, func(...args), true); } catch (error) { const err = new Error(`Could not call remote function '${func.name || 'anonymous'}'. Check that the function signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`); - (err as any).cause = error - throw err + (err as any).cause = error; + throw err; } -}) +}); handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) - const object = objectsRegistry.get(id) + args = unwrapArgs(event.sender, event.frameId, contextId, args); + const object = objectsRegistry.get(id); if (object == null) { - throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`) + throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`); } - return valueToMeta(event.sender, contextId, new object[method](...args)) -}) + return valueToMeta(event.sender, contextId, new object[method](...args)); +}); handleRemoteCommand('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) - const object = objectsRegistry.get(id) + args = unwrapArgs(event.sender, event.frameId, contextId, args); + const object = objectsRegistry.get(id); if (object == null) { - throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`) + throwRPCError(`Cannot call method '${method}' on missing remote object ${id}`); } try { - return valueToMeta(event.sender, contextId, object[method](...args), true) + return valueToMeta(event.sender, contextId, object[method](...args), true); } catch (error) { const err = new Error(`Could not call remote method '${method}'. Check that the method signature is correct. Underlying error: ${error.message}\nUnderlying stack: ${error.stack}\n`); - (err as any).cause = error - throw err + (err as any).cause = error; + throw err; } -}) +}); handleRemoteCommand('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) { - args = unwrapArgs(event.sender, event.frameId, contextId, args) - const obj = objectsRegistry.get(id) + args = unwrapArgs(event.sender, event.frameId, contextId, args); + const obj = objectsRegistry.get(id); if (obj == null) { - throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`) + throwRPCError(`Cannot set property '${name}' on missing remote object ${id}`); } - obj[name] = args[0] - return null -}) + obj[name] = args[0]; + return null; +}); handleRemoteCommand('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, id, name) { - const obj = objectsRegistry.get(id) + const obj = objectsRegistry.get(id); if (obj == null) { - throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`) + throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`); } - return valueToMeta(event.sender, contextId, obj[name]) -}) + return valueToMeta(event.sender, contextId, obj[name]); +}); handleRemoteCommand('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id, rendererSideRefCount) { - objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount) -}) + objectsRegistry.remove(event.sender, contextId, id, rendererSideRefCount); +}); handleRemoteCommand('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => { - objectsRegistry.clear(event.sender, contextId) -}) + objectsRegistry.clear(event.sender, contextId); +}); handleRemoteCommand('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId, stack) { - logStack(event.sender, 'remote.getGuestWebContents()', stack) - const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender) + logStack(event.sender, 'remote.getGuestWebContents()', stack); + const guest = guestViewManager.getGuestForWebContents(guestInstanceId, event.sender); - const customEvent = emitCustomEvent(event.sender, 'remote-get-guest-web-contents', guest) + const customEvent = emitCustomEvent(event.sender, 'remote-get-guest-web-contents', guest); if (customEvent.returnValue === undefined) { if (customEvent.defaultPrevented) { - throw new Error(`Blocked remote.getGuestWebContents()`) + throw new Error(`Blocked remote.getGuestWebContents()`); } else { - customEvent.returnValue = guest + customEvent.returnValue = guest; } } - return valueToMeta(event.sender, contextId, customEvent.returnValue) -}) + return valueToMeta(event.sender, contextId, customEvent.returnValue); +}); module.exports = { isRemoteModuleEnabled -} +}; diff --git a/lib/browser/rpc-server.js b/lib/browser/rpc-server.js index 2dd61e05b489f..10133095a7ebc 100644 --- a/lib/browser/rpc-server.js +++ b/lib/browser/rpc-server.js @@ -1,122 +1,126 @@ -'use strict' +'use strict'; -const electron = require('electron') -const fs = require('fs') +const electron = require('electron'); +const fs = require('fs'); -const eventBinding = process.electronBinding('event') -const clipboard = process.electronBinding('clipboard') -const features = process.electronBinding('features') +const eventBinding = process.electronBinding('event'); +const clipboard = process.electronBinding('clipboard'); +const features = process.electronBinding('features'); -const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init') -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal') -const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils') -const guestViewManager = require('@electron/internal/browser/guest-view-manager') -const clipboardUtils = require('@electron/internal/common/clipboard-utils') +const { crashReporterInit } = require('@electron/internal/browser/crash-reporter-init'); +const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); +const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils'); +const guestViewManager = require('@electron/internal/browser/guest-view-manager'); +const typeUtils = require('@electron/internal/common/type-utils'); const emitCustomEvent = function (contents, eventName, ...args) { - const event = eventBinding.createWithSender(contents) + const event = eventBinding.createWithSender(contents); - electron.app.emit(eventName, event, contents, ...args) - contents.emit(eventName, event, ...args) + electron.app.emit(eventName, event, contents, ...args); + contents.emit(eventName, event, ...args); - return event -} + return event; +}; const logStack = function (contents, code, stack) { if (stack) { - console.warn(`WebContents (${contents.id}): ${code}`, stack) + console.warn(`WebContents (${contents.id}): ${code}`, stack); } -} +}; // Implements window.close() ipcMainInternal.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) { - const window = event.sender.getOwnerBrowserWindow() + const window = event.sender.getOwnerBrowserWindow(); if (window) { - window.close() + window.close(); } - event.returnValue = null -}) + event.returnValue = null; +}); ipcMainUtils.handleSync('ELECTRON_CRASH_REPORTER_INIT', function (event, options) { - return crashReporterInit(options) -}) + return crashReporterInit(options); +}); ipcMainInternal.handle('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES', function (event) { - return event.sender.getLastWebPreferences() -}) + return event.sender.getLastWebPreferences(); +}); // Methods not listed in this set are called directly in the renderer process. const allowedClipboardMethods = (() => { switch (process.platform) { case 'darwin': - return new Set(['readFindText', 'writeFindText']) + return new Set(['readFindText', 'writeFindText']); case 'linux': - return new Set(Object.keys(clipboard)) + return new Set(Object.keys(clipboard)); default: - return new Set() + return new Set(); } -})() +})(); ipcMainUtils.handleSync('ELECTRON_BROWSER_CLIPBOARD', function (event, method, ...args) { if (!allowedClipboardMethods.has(method)) { - throw new Error(`Invalid method: ${method}`) + throw new Error(`Invalid method: ${method}`); } - return clipboardUtils.serialize(electron.clipboard[method](...clipboardUtils.deserialize(args))) -}) + return typeUtils.serialize(electron.clipboard[method](...typeUtils.deserialize(args))); +}); if (features.isDesktopCapturerEnabled()) { - const desktopCapturer = require('@electron/internal/browser/desktop-capturer') + const desktopCapturer = require('@electron/internal/browser/desktop-capturer'); ipcMainInternal.handle('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, options, stack) { - logStack(event.sender, 'desktopCapturer.getSources()', stack) - const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources') + logStack(event.sender, 'desktopCapturer.getSources()', stack); + const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources'); if (customEvent.defaultPrevented) { - console.error('Blocked desktopCapturer.getSources()') - return [] + console.error('Blocked desktopCapturer.getSources()'); + return []; } - return desktopCapturer.getSources(event, options) - }) + return desktopCapturer.getSources(event, options); + }); } const isRemoteModuleEnabled = features.isRemoteModuleEnabled() ? require('@electron/internal/browser/remote/server').isRemoteModuleEnabled - : () => false + : () => false; const getPreloadScript = async function (preloadPath) { - let preloadSrc = null - let preloadError = null + let preloadSrc = null; + let preloadError = null; try { - preloadSrc = (await fs.promises.readFile(preloadPath)).toString() + preloadSrc = (await fs.promises.readFile(preloadPath)).toString(); } catch (error) { - preloadError = error + preloadError = error; } - return { preloadPath, preloadSrc, preloadError } -} + return { preloadPath, preloadSrc, preloadError }; +}; if (features.isExtensionsEnabled()) { - ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => []) + ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => []); } else { - const { getContentScripts } = require('@electron/internal/browser/chrome-extension') - ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts()) + const { getContentScripts } = require('@electron/internal/browser/chrome-extension'); + ipcMainUtils.handleSync('ELECTRON_GET_CONTENT_SCRIPTS', () => getContentScripts()); } ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) { - const preloadPaths = event.sender._getPreloadPaths() + const preloadPaths = event.sender._getPreloadPaths(); - let contentScripts = [] + let contentScripts = []; if (!features.isExtensionsEnabled()) { - const { getContentScripts } = require('@electron/internal/browser/chrome-extension') - contentScripts = getContentScripts() + const { getContentScripts } = require('@electron/internal/browser/chrome-extension'); + contentScripts = getContentScripts(); } + const webPreferences = event.sender.getLastWebPreferences() || {}; + return { contentScripts, preloadScripts: await Promise.all(preloadPaths.map(path => getPreloadScript(path))), isRemoteModuleEnabled: isRemoteModuleEnabled(event.sender), isWebViewTagEnabled: guestViewManager.isWebViewTagEnabled(event.sender), + guestInstanceId: webPreferences.guestInstanceId, + openerId: webPreferences.openerId, process: { arch: process.arch, platform: process.platform, @@ -125,9 +129,9 @@ ipcMainUtils.handleSync('ELECTRON_BROWSER_SANDBOX_LOAD', async function (event) versions: process.versions, execPath: process.helperExecPath } - } -}) + }; +}); ipcMainInternal.on('ELECTRON_BROWSER_PRELOAD_ERROR', function (event, preloadPath, error) { - event.sender.emit('preload-error', event, preloadPath, error) -}) + event.sender.emit('preload-error', event, preloadPath, error); +}); diff --git a/lib/browser/utils.ts b/lib/browser/utils.ts index 91e3831e9b337..fd2b35d58afc3 100644 --- a/lib/browser/utils.ts +++ b/lib/browser/utils.ts @@ -1,4 +1,4 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'events'; /** * Creates a lazy instance of modules that can't be required before the @@ -16,23 +16,23 @@ export function createLazyInstance ( holder: Object, isEventEmitter: Boolean ) { - let lazyModule: Object - const module: any = {} - for (const method in (holder as any).prototype) { + let lazyModule: Object; + const module: any = {}; + for (const method in (holder as any).prototype) { // eslint-disable-line guard-for-in module[method] = (...args: any) => { // create new instance of module at runtime if none exists if (!lazyModule) { - lazyModule = creator() - if (isEventEmitter) EventEmitter.call(lazyModule as any) + lazyModule = creator(); + if (isEventEmitter) EventEmitter.call(lazyModule as any); } // check for properties on the prototype chain that aren't functions if (typeof (lazyModule as any)[method] !== 'function') { - return (lazyModule as any)[method] + return (lazyModule as any)[method]; } - return (lazyModule as any)[method](...args) - } + return (lazyModule as any)[method](...args); + }; } - return module + return module; } diff --git a/lib/common/api/clipboard.js b/lib/common/api/clipboard.js index 99b4644935ea1..a7c49cd88488d 100644 --- a/lib/common/api/clipboard.js +++ b/lib/common/api/clipboard.js @@ -1,29 +1,29 @@ -'use strict' +'use strict'; -const clipboard = process.electronBinding('clipboard') +const clipboard = process.electronBinding('clipboard'); if (process.type === 'renderer') { - const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') - const clipboardUtils = require('@electron/internal/common/clipboard-utils') + const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils'); + const typeUtils = require('@electron/internal/common/type-utils'); const makeRemoteMethod = function (method) { return (...args) => { - args = clipboardUtils.serialize(args) - const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args) - return clipboardUtils.deserialize(result) - } - } + args = typeUtils.serialize(args); + const result = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_CLIPBOARD', method, ...args); + return typeUtils.deserialize(result); + }; + }; if (process.platform === 'linux') { // On Linux we could not access clipboard in renderer process. for (const method of Object.keys(clipboard)) { - clipboard[method] = makeRemoteMethod(method) + clipboard[method] = makeRemoteMethod(method); } } else if (process.platform === 'darwin') { // Read/write to find pasteboard over IPC since only main process is notified of changes - clipboard.readFindText = makeRemoteMethod('readFindText') - clipboard.writeFindText = makeRemoteMethod('writeFindText') + clipboard.readFindText = makeRemoteMethod('readFindText'); + clipboard.writeFindText = makeRemoteMethod('writeFindText'); } } -module.exports = clipboard +module.exports = clipboard; diff --git a/lib/common/api/deprecate.ts b/lib/common/api/deprecate.ts index ceeb54cf08c1d..bda12bee44a93 100644 --- a/lib/common/api/deprecate.ts +++ b/lib/common/api/deprecate.ts @@ -1,95 +1,95 @@ -let deprecationHandler: ElectronInternal.DeprecationHandler | null = null +let deprecationHandler: ElectronInternal.DeprecationHandler | null = null; function warnOnce (oldName: string, newName?: string) { - let warned = false + let warned = false; const msg = newName ? `'${oldName}' is deprecated and will be removed. Please use '${newName}' instead.` - : `'${oldName}' is deprecated and will be removed.` + : `'${oldName}' is deprecated and will be removed.`; return () => { if (!warned && !process.noDeprecation) { - warned = true - deprecate.log(msg) + warned = true; + deprecate.log(msg); } - } + }; } const deprecate: ElectronInternal.DeprecationUtil = { warnOnce, - setHandler: (handler) => { deprecationHandler = handler }, + setHandler: (handler) => { deprecationHandler = handler; }, getHandler: () => deprecationHandler, warn: (oldName, newName) => { if (!process.noDeprecation) { - deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`) + deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`); } }, log: (message) => { if (typeof deprecationHandler === 'function') { - deprecationHandler(message) + deprecationHandler(message); } else if (process.throwDeprecation) { - throw new Error(message) + throw new Error(message); } else if (process.traceDeprecation) { - return console.trace(message) + return console.trace(message); } else { - return console.warn(`(electron) ${message}`) + return console.warn(`(electron) ${message}`); } }, // remove a function with no replacement removeFunction: (fn, removedName) => { - if (!fn) { throw Error(`'${removedName} function' is invalid or does not exist.`) } + if (!fn) { throw Error(`'${removedName} function' is invalid or does not exist.`); } // wrap the deprecated function to warn user - const warn = warnOnce(`${fn.name} function`) + const warn = warnOnce(`${fn.name} function`); return function (this: any) { - warn() - fn.apply(this, arguments) - } + warn(); + fn.apply(this, arguments); + }; }, // change the name of a function renameFunction: (fn, newName) => { - const warn = warnOnce(`${fn.name} function`, `${newName} function`) + const warn = warnOnce(`${fn.name} function`, `${newName} function`); return function (this: any) { - warn() - return fn.apply(this, arguments) - } + warn(); + return fn.apply(this, arguments); + }; }, moveAPI: (fn: Function, oldUsage: string, newUsage: string) => { - const warn = warnOnce(oldUsage, newUsage) + const warn = warnOnce(oldUsage, newUsage); return function (this: any) { - warn() - return fn.apply(this, arguments) - } + warn(); + return fn.apply(this, arguments); + }; }, // change the name of an event event: (emitter, oldName, newName) => { const warn = newName.startsWith('-') /* internal event */ ? warnOnce(`${oldName} event`) - : warnOnce(`${oldName} event`, `${newName} event`) + : warnOnce(`${oldName} event`, `${newName} event`); return emitter.on(newName, function (this: NodeJS.EventEmitter, ...args) { if (this.listenerCount(oldName) !== 0) { - warn() - this.emit(oldName, ...args) + warn(); + this.emit(oldName, ...args); } - }) + }); }, // deprecate a getter/setter function pair in favor of a property fnToProperty: (prototype: any, prop: string, getter: string, setter?: string) => { const withWarnOnce = function (obj: any, key: any, oldName: string, newName: string) { - const warn = warnOnce(oldName, newName) - const method = obj[key] + const warn = warnOnce(oldName, newName); + const method = obj[key]; return function (this: any, ...args: any) { - warn() - return method.apply(this, args) - } - } + warn(); + return method.apply(this, args); + }; + }; - prototype[getter.substr(1)] = withWarnOnce(prototype, getter, `${getter.substr(1)} function`, `${prop} property`) + prototype[getter.substr(1)] = withWarnOnce(prototype, getter, `${getter.substr(1)} function`, `${prop} property`); if (setter) { - prototype[setter.substr(1)] = withWarnOnce(prototype, setter, `${setter.substr(1)} function`, `${prop} property`) + prototype[setter.substr(1)] = withWarnOnce(prototype, setter, `${setter.substr(1)} function`, `${prop} property`); } }, @@ -97,107 +97,107 @@ const deprecate: ElectronInternal.DeprecationUtil = { removeProperty: (o, removedName) => { // if the property's already been removed, warn about it if (!(removedName in o)) { - deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`) + deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`); } // wrap the deprecated property in an accessor to warn - const warn = warnOnce(removedName) - let val = o[removedName] + const warn = warnOnce(removedName); + let val = o[removedName]; return Object.defineProperty(o, removedName, { configurable: true, get: () => { - warn() - return val + warn(); + return val; }, set: newVal => { - warn() - val = newVal + warn(); + val = newVal; } - }) + }); }, // deprecate a callback-based function in favor of one returning a Promise promisify: any>(fn: T): T => { - const fnName = fn.name || 'function' - const oldName = `${fnName} with callbacks` - const newName = `${fnName} with Promises` - const warn = warnOnce(oldName, newName) + const fnName = fn.name || 'function'; + const oldName = `${fnName} with callbacks`; + const newName = `${fnName} with Promises`; + const warn = warnOnce(oldName, newName); return function (this: any, ...params: any[]) { - let cb: Function | undefined + let cb: Function | undefined; if (params.length > 0 && typeof params[params.length - 1] === 'function') { - cb = params.pop() + cb = params.pop(); } - const promise = fn.apply(this, params) - if (!cb) return promise - if (process.enablePromiseAPIs) warn() + const promise = fn.apply(this, params); + if (!cb) return promise; + if (process.enablePromiseAPIs) warn(); return promise .then((res: any) => { process.nextTick(() => { - cb!.length === 2 ? cb!(null, res) : cb!(res) - }) - return res + cb!.length === 2 ? cb!(null, res) : cb!(res); + }); + return res; }, (err: Error) => { process.nextTick(() => { - cb!.length === 2 ? cb!(err) : cb!() - }) - throw err - }) - } as T + cb!.length === 2 ? cb!(err) : cb!(); + }); + throw err; + }); + } as T; }, // convertPromiseValue: Temporarily disabled until it's used // deprecate a callback-based function in favor of one returning a Promise promisifyMultiArg: any>(fn: T /* convertPromiseValue: (v: any) => any */): T => { - const fnName = fn.name || 'function' - const oldName = `${fnName} with callbacks` - const newName = `${fnName} with Promises` - const warn = warnOnce(oldName, newName) + const fnName = fn.name || 'function'; + const oldName = `${fnName} with callbacks`; + const newName = `${fnName} with Promises`; + const warn = warnOnce(oldName, newName); return function (this: any, ...params) { - let cb: Function | undefined + let cb: Function | undefined; if (params.length > 0 && typeof params[params.length - 1] === 'function') { - cb = params.pop() + cb = params.pop(); } - const promise = fn.apply(this, params) - if (!cb) return promise - if (process.enablePromiseAPIs) warn() + const promise = fn.apply(this, params); + if (!cb) return promise; + if (process.enablePromiseAPIs) warn(); return promise .then((res: any) => { process.nextTick(() => { // eslint-disable-next-line standard/no-callback-literal - cb!.length > 2 ? cb!(null, ...res) : cb!(...res) - }) + cb!.length > 2 ? cb!(null, ...res) : cb!(...res); + }); }, (err: Error) => { - process.nextTick(() => cb!(err)) - }) - } as T + process.nextTick(() => cb!(err)); + }); + } as T; }, // change the name of a property renameProperty: (o, oldName, newName) => { - const warn = warnOnce(oldName, newName) + const warn = warnOnce(oldName, newName); // if the new property isn't there yet, // inject it and warn about it if ((oldName in o) && !(newName in o)) { - warn() - o[newName] = (o as any)[oldName] + warn(); + o[newName] = (o as any)[oldName]; } // wrap the deprecated property in an accessor to warn // and redirect to the new property return Object.defineProperty(o, oldName, { get: () => { - warn() - return o[newName] + warn(); + return o[newName]; }, set: value => { - warn() - o[newName] = value + warn(); + o[newName] = value; } - }) + }); } -} +}; -export default deprecate +export default deprecate; diff --git a/lib/common/api/module-list.ts b/lib/common/api/module-list.ts index b329e7e469ee1..a5493e552dfa8 100644 --- a/lib/common/api/module-list.ts +++ b/lib/common/api/module-list.ts @@ -5,4 +5,4 @@ export const commonModuleList: ElectronInternal.ModuleEntry[] = [ { name: 'shell', loader: () => require('./shell') }, // The internal modules, invisible unless you know their names. { name: 'deprecate', loader: () => require('./deprecate'), private: true } -] +]; diff --git a/lib/common/api/native-image.js b/lib/common/api/native-image.js index 7c7737c7f1b87..e81cc977fe7d4 100644 --- a/lib/common/api/native-image.js +++ b/lib/common/api/native-image.js @@ -1,8 +1,8 @@ -'use strict' +'use strict'; -const { deprecate } = require('electron') -const { NativeImage, nativeImage } = process.electronBinding('native_image') +const { deprecate } = require('electron'); +const { NativeImage, nativeImage } = process.electronBinding('native_image'); -deprecate.fnToProperty(NativeImage.prototype, 'isMacTemplateImage', '_isTemplateImage', '_setTemplateImage') +deprecate.fnToProperty(NativeImage.prototype, 'isMacTemplateImage', '_isTemplateImage', '_setTemplateImage'); -module.exports = nativeImage +module.exports = nativeImage; diff --git a/lib/common/api/shell.js b/lib/common/api/shell.js index 9e9b673523588..c9f7b6c0e4e23 100644 --- a/lib/common/api/shell.js +++ b/lib/common/api/shell.js @@ -1,3 +1,3 @@ -'use strict' +'use strict'; -module.exports = process.electronBinding('shell') +module.exports = process.electronBinding('shell'); diff --git a/lib/common/asar.js b/lib/common/asar.js index 67b7b813f8515..cca6e5be71c7c 100644 --- a/lib/common/asar.js +++ b/lib/common/asar.js @@ -1,80 +1,82 @@ 'use strict'; (function () { - const asar = process._linkedBinding('atom_common_asar') - const v8Util = process._linkedBinding('atom_common_v8_util') - const { Buffer } = require('buffer') - const Module = require('module') - const path = require('path') - const util = require('util') + const asar = process._linkedBinding('atom_common_asar'); + const v8Util = process._linkedBinding('atom_common_v8_util'); + const { Buffer } = require('buffer'); + const Module = require('module'); + const path = require('path'); + const util = require('util'); + + const Promise = global.Promise; const envNoAsar = process.env.ELECTRON_NO_ASAR && process.type !== 'browser' && - process.type !== 'renderer' - const isAsarDisabled = () => process.noAsar || envNoAsar + process.type !== 'renderer'; + const isAsarDisabled = () => process.noAsar || envNoAsar; - const internalBinding = process.internalBinding - delete process.internalBinding + const internalBinding = process.internalBinding; + delete process.internalBinding; /** * @param {!Function} functionToCall * @param {!Array|undefined} args */ const nextTick = (functionToCall, args = []) => { - process.nextTick(() => functionToCall(...args)) - } + process.nextTick(() => functionToCall(...args)); + }; // Cache asar archive objects. - const cachedArchives = new Map() + const cachedArchives = new Map(); const getOrCreateArchive = archivePath => { - const isCached = cachedArchives.has(archivePath) + const isCached = cachedArchives.has(archivePath); if (isCached) { - return cachedArchives.get(archivePath) + return cachedArchives.get(archivePath); } - const newArchive = asar.createArchive(archivePath) - if (!newArchive) return null + const newArchive = asar.createArchive(archivePath); + if (!newArchive) return null; - cachedArchives.set(archivePath, newArchive) - return newArchive - } + cachedArchives.set(archivePath, newArchive); + return newArchive; + }; // Separate asar package's path from full path. const splitPath = archivePathOrBuffer => { // Shortcut for disabled asar. - if (isAsarDisabled()) return { isAsar: false } + if (isAsarDisabled()) return { isAsar: false }; // Check for a bad argument type. - let archivePath = archivePathOrBuffer + let archivePath = archivePathOrBuffer; if (Buffer.isBuffer(archivePathOrBuffer)) { - archivePath = archivePathOrBuffer.toString() + archivePath = archivePathOrBuffer.toString(); } - if (typeof archivePath !== 'string') return { isAsar: false } + if (typeof archivePath !== 'string') return { isAsar: false }; - return asar.splitPath(path.normalize(archivePath)) - } + return asar.splitPath(path.normalize(archivePath)); + }; // Convert asar archive's Stats object to fs's Stats object. - let nextInode = 0 + let nextInode = 0; - const uid = process.getuid != null ? process.getuid() : 0 - const gid = process.getgid != null ? process.getgid() : 0 + const uid = process.getuid != null ? process.getuid() : 0; + const gid = process.getgid != null ? process.getgid() : 0; - const fakeTime = new Date() - const msec = (date) => (date || fakeTime).getTime() + const fakeTime = new Date(); + const msec = (date) => (date || fakeTime).getTime(); const asarStatsToFsStats = function (stats) { - const { Stats, constants } = require('fs') + const { Stats, constants } = require('fs'); - let mode = constants.S_IROTH ^ constants.S_IRGRP ^ constants.S_IRUSR ^ constants.S_IWUSR + let mode = constants.S_IROTH ^ constants.S_IRGRP ^ constants.S_IRUSR ^ constants.S_IWUSR; if (stats.isFile) { - mode ^= constants.S_IFREG + mode ^= constants.S_IFREG; } else if (stats.isDirectory) { - mode ^= constants.S_IFDIR + mode ^= constants.S_IFDIR; } else if (stats.isLink) { - mode ^= constants.S_IFLNK + mode ^= constants.S_IFLNK; } return new Stats( @@ -92,648 +94,648 @@ msec(stats.mtime), // mtim_msec msec(stats.ctime), // ctim_msec msec(stats.birthtime) // birthtim_msec - ) - } + ); + }; const AsarError = { NOT_FOUND: 'NOT_FOUND', NOT_DIR: 'NOT_DIR', NO_ACCESS: 'NO_ACCESS', INVALID_ARCHIVE: 'INVALID_ARCHIVE' - } + }; const createError = (errorType, { asarPath, filePath } = {}) => { - let error + let error; switch (errorType) { case AsarError.NOT_FOUND: - error = new Error(`ENOENT, ${filePath} not found in ${asarPath}`) - error.code = 'ENOENT' - error.errno = -2 - break + error = new Error(`ENOENT, ${filePath} not found in ${asarPath}`); + error.code = 'ENOENT'; + error.errno = -2; + break; case AsarError.NOT_DIR: - error = new Error('ENOTDIR, not a directory') - error.code = 'ENOTDIR' - error.errno = -20 - break + error = new Error('ENOTDIR, not a directory'); + error.code = 'ENOTDIR'; + error.errno = -20; + break; case AsarError.NO_ACCESS: - error = new Error(`EACCES: permission denied, access '${filePath}'`) - error.code = 'EACCES' - error.errno = -13 - break + error = new Error(`EACCES: permission denied, access '${filePath}'`); + error.code = 'EACCES'; + error.errno = -13; + break; case AsarError.INVALID_ARCHIVE: - error = new Error(`Invalid package ${asarPath}`) - break + error = new Error(`Invalid package ${asarPath}`); + break; default: - throw new Error(`Invalid error type "${errorType}" passed to createError.`) + throw new Error(`Invalid error type "${errorType}" passed to createError.`); } - return error - } + return error; + }; const overrideAPISync = function (module, name, pathArgumentIndex, fromAsync) { - if (pathArgumentIndex == null) pathArgumentIndex = 0 - const old = module[name] + if (pathArgumentIndex == null) pathArgumentIndex = 0; + const old = module[name]; const func = function () { - const pathArgument = arguments[pathArgumentIndex] - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return old.apply(this, arguments) + const pathArgument = arguments[pathArgumentIndex]; + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return old.apply(this, arguments); - const archive = getOrCreateArchive(asarPath) - if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + const archive = getOrCreateArchive(asarPath); + if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); - const newPath = archive.copyFileOut(filePath) - if (!newPath) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + const newPath = archive.copyFileOut(filePath); + if (!newPath) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); - arguments[pathArgumentIndex] = newPath - return old.apply(this, arguments) - } + arguments[pathArgumentIndex] = newPath; + return old.apply(this, arguments); + }; if (fromAsync) { - return func + return func; } - module[name] = func - } + module[name] = func; + }; const overrideAPI = function (module, name, pathArgumentIndex) { - if (pathArgumentIndex == null) pathArgumentIndex = 0 - const old = module[name] + if (pathArgumentIndex == null) pathArgumentIndex = 0; + const old = module[name]; module[name] = function () { - const pathArgument = arguments[pathArgumentIndex] - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return old.apply(this, arguments) + const pathArgument = arguments[pathArgumentIndex]; + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return old.apply(this, arguments); - const callback = arguments[arguments.length - 1] + const callback = arguments[arguments.length - 1]; if (typeof callback !== 'function') { - return overrideAPISync(module, name, pathArgumentIndex, true).apply(this, arguments) + return overrideAPISync(module, name, pathArgumentIndex, true).apply(this, arguments); } - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const newPath = archive.copyFileOut(filePath) + const newPath = archive.copyFileOut(filePath); if (!newPath) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } - arguments[pathArgumentIndex] = newPath - return old.apply(this, arguments) - } + arguments[pathArgumentIndex] = newPath; + return old.apply(this, arguments); + }; if (old[util.promisify.custom]) { - module[name][util.promisify.custom] = makePromiseFunction(old[util.promisify.custom], pathArgumentIndex) + module[name][util.promisify.custom] = makePromiseFunction(old[util.promisify.custom], pathArgumentIndex); } if (module.promises && module.promises[name]) { - module.promises[name] = makePromiseFunction(module.promises[name], pathArgumentIndex) + module.promises[name] = makePromiseFunction(module.promises[name], pathArgumentIndex); } - } + }; const makePromiseFunction = function (orig, pathArgumentIndex) { return function (...args) { - const pathArgument = args[pathArgumentIndex] - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return orig.apply(this, args) + const pathArgument = args[pathArgumentIndex]; + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return orig.apply(this, args); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - return Promise.reject(createError(AsarError.INVALID_ARCHIVE, { asarPath })) + return Promise.reject(createError(AsarError.INVALID_ARCHIVE, { asarPath })); } - const newPath = archive.copyFileOut(filePath) + const newPath = archive.copyFileOut(filePath); if (!newPath) { - return Promise.reject(createError(AsarError.NOT_FOUND, { asarPath, filePath })) + return Promise.reject(createError(AsarError.NOT_FOUND, { asarPath, filePath })); } - args[pathArgumentIndex] = newPath - return orig.apply(this, args) - } - } + args[pathArgumentIndex] = newPath; + return orig.apply(this, args); + }; + }; // Override fs APIs. exports.wrapFsWithAsar = fs => { - const logFDs = {} + const logFDs = {}; const logASARAccess = (asarPath, filePath, offset) => { - if (!process.env.ELECTRON_LOG_ASAR_READS) return + if (!process.env.ELECTRON_LOG_ASAR_READS) return; if (!logFDs[asarPath]) { - const path = require('path') - const logFilename = `${path.basename(asarPath, '.asar')}-access-log.txt` - const logPath = path.join(require('os').tmpdir(), logFilename) - logFDs[asarPath] = fs.openSync(logPath, 'a') + const path = require('path'); + const logFilename = `${path.basename(asarPath, '.asar')}-access-log.txt`; + const logPath = path.join(require('os').tmpdir(), logFilename); + logFDs[asarPath] = fs.openSync(logPath, 'a'); } - fs.writeSync(logFDs[asarPath], `${offset}: ${filePath}\n`) - } + fs.writeSync(logFDs[asarPath], `${offset}: ${filePath}\n`); + }; - const { lstatSync } = fs + const { lstatSync } = fs; fs.lstatSync = (pathArgument, options) => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return lstatSync(pathArgument, options) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return lstatSync(pathArgument, options); - const archive = getOrCreateArchive(asarPath) - if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + const archive = getOrCreateArchive(asarPath); + if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); - const stats = archive.stat(filePath) - if (!stats) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + const stats = archive.stat(filePath); + if (!stats) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); - return asarStatsToFsStats(stats) - } + return asarStatsToFsStats(stats); + }; - const { lstat } = fs + const { lstat } = fs; fs.lstat = function (pathArgument, options, callback) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); if (typeof options === 'function') { - callback = options - options = {} + callback = options; + options = {}; } - if (!isAsar) return lstat(pathArgument, options, callback) + if (!isAsar) return lstat(pathArgument, options, callback); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const stats = archive.stat(filePath) + const stats = archive.stat(filePath); if (!stats) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } - const fsStats = asarStatsToFsStats(stats) - nextTick(callback, [null, fsStats]) - } + const fsStats = asarStatsToFsStats(stats); + nextTick(callback, [null, fsStats]); + }; - fs.promises.lstat = util.promisify(fs.lstat) + fs.promises.lstat = util.promisify(fs.lstat); - const { statSync } = fs + const { statSync } = fs; fs.statSync = (pathArgument, options) => { - const { isAsar } = splitPath(pathArgument) - if (!isAsar) return statSync(pathArgument, options) + const { isAsar } = splitPath(pathArgument); + if (!isAsar) return statSync(pathArgument, options); // Do not distinguish links for now. - return fs.lstatSync(pathArgument, options) - } + return fs.lstatSync(pathArgument, options); + }; - const { stat } = fs + const { stat } = fs; fs.stat = (pathArgument, options, callback) => { - const { isAsar } = splitPath(pathArgument) + const { isAsar } = splitPath(pathArgument); if (typeof options === 'function') { - callback = options - options = {} + callback = options; + options = {}; } - if (!isAsar) return stat(pathArgument, options, callback) + if (!isAsar) return stat(pathArgument, options, callback); // Do not distinguish links for now. - process.nextTick(() => fs.lstat(pathArgument, options, callback)) - } + process.nextTick(() => fs.lstat(pathArgument, options, callback)); + }; - fs.promises.stat = util.promisify(fs.stat) + fs.promises.stat = util.promisify(fs.stat); const wrapRealpathSync = function (realpathSync) { return function (pathArgument, options) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return realpathSync.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return realpathSync.apply(this, arguments); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); } - const fileRealPath = archive.realpath(filePath) + const fileRealPath = archive.realpath(filePath); if (fileRealPath === false) { - throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); } - return path.join(realpathSync(asarPath, options), fileRealPath) - } - } + return path.join(realpathSync(asarPath, options), fileRealPath); + }; + }; - const { realpathSync } = fs - fs.realpathSync = wrapRealpathSync(realpathSync) - fs.realpathSync.native = wrapRealpathSync(realpathSync.native) + const { realpathSync } = fs; + fs.realpathSync = wrapRealpathSync(realpathSync); + fs.realpathSync.native = wrapRealpathSync(realpathSync.native); const wrapRealpath = function (realpath) { return function (pathArgument, options, callback) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return realpath.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return realpath.apply(this, arguments); if (arguments.length < 3) { - callback = options - options = {} + callback = options; + options = {}; } - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const fileRealPath = archive.realpath(filePath) + const fileRealPath = archive.realpath(filePath); if (fileRealPath === false) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } realpath(asarPath, options, (error, archiveRealPath) => { if (error === null) { - const fullPath = path.join(archiveRealPath, fileRealPath) - callback(null, fullPath) + const fullPath = path.join(archiveRealPath, fileRealPath); + callback(null, fullPath); } else { - callback(error) + callback(error); } - }) - } - } + }); + }; + }; - const { realpath } = fs - fs.realpath = wrapRealpath(realpath) - fs.realpath.native = wrapRealpath(realpath.native) + const { realpath } = fs; + fs.realpath = wrapRealpath(realpath); + fs.realpath.native = wrapRealpath(realpath.native); - fs.promises.realpath = util.promisify(fs.realpath.native) + fs.promises.realpath = util.promisify(fs.realpath.native); - const { exists } = fs + const { exists } = fs; fs.exists = (pathArgument, callback) => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return exists(pathArgument, callback) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return exists(pathArgument, callback); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const pathExists = (archive.stat(filePath) !== false) - nextTick(callback, [pathExists]) - } + const pathExists = (archive.stat(filePath) !== false); + nextTick(callback, [pathExists]); + }; fs.exists[util.promisify.custom] = pathArgument => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return exists[util.promisify.custom](pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return exists[util.promisify.custom](pathArgument); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - return Promise.reject(error) + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + return Promise.reject(error); } - return Promise.resolve(archive.stat(filePath) !== false) - } + return Promise.resolve(archive.stat(filePath) !== false); + }; - const { existsSync } = fs + const { existsSync } = fs; fs.existsSync = pathArgument => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return existsSync(pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return existsSync(pathArgument); - const archive = getOrCreateArchive(asarPath) - if (!archive) return false + const archive = getOrCreateArchive(asarPath); + if (!archive) return false; - return archive.stat(filePath) !== false - } + return archive.stat(filePath) !== false; + }; - const { access } = fs + const { access } = fs; fs.access = function (pathArgument, mode, callback) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return access.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return access.apply(this, arguments); if (typeof mode === 'function') { - callback = mode - mode = fs.constants.F_OK + callback = mode; + mode = fs.constants.F_OK; } - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const info = archive.getFileInfo(filePath) + const info = archive.getFileInfo(filePath); if (!info) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } if (info.unpacked) { - const realPath = archive.copyFileOut(filePath) - return fs.access(realPath, mode, callback) + const realPath = archive.copyFileOut(filePath); + return fs.access(realPath, mode, callback); } - const stats = archive.stat(filePath) + const stats = archive.stat(filePath); if (!stats) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } if (mode & fs.constants.W_OK) { - const error = createError(AsarError.NO_ACCESS, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NO_ACCESS, { asarPath, filePath }); + nextTick(callback, [error]); + return; } - nextTick(callback) - } + nextTick(callback); + }; - fs.promises.access = util.promisify(fs.access) + fs.promises.access = util.promisify(fs.access); - const { accessSync } = fs + const { accessSync } = fs; fs.accessSync = function (pathArgument, mode) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return accessSync.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return accessSync.apply(this, arguments); - if (mode == null) mode = fs.constants.F_OK + if (mode == null) mode = fs.constants.F_OK; - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); } - const info = archive.getFileInfo(filePath) + const info = archive.getFileInfo(filePath); if (!info) { - throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); } if (info.unpacked) { - const realPath = archive.copyFileOut(filePath) - return fs.accessSync(realPath, mode) + const realPath = archive.copyFileOut(filePath); + return fs.accessSync(realPath, mode); } - const stats = archive.stat(filePath) + const stats = archive.stat(filePath); if (!stats) { - throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); } if (mode & fs.constants.W_OK) { - throw createError(AsarError.NO_ACCESS, { asarPath, filePath }) + throw createError(AsarError.NO_ACCESS, { asarPath, filePath }); } - } + }; - const { readFile } = fs + const { readFile } = fs; fs.readFile = function (pathArgument, options, callback) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return readFile.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return readFile.apply(this, arguments); if (typeof options === 'function') { - callback = options - options = { encoding: null } + callback = options; + options = { encoding: null }; } else if (typeof options === 'string') { - options = { encoding: options } + options = { encoding: options }; } else if (options === null || options === undefined) { - options = { encoding: null } + options = { encoding: null }; } else if (typeof options !== 'object') { - throw new TypeError('Bad arguments') + throw new TypeError('Bad arguments'); } - const { encoding } = options - const archive = getOrCreateArchive(asarPath) + const { encoding } = options; + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const info = archive.getFileInfo(filePath) + const info = archive.getFileInfo(filePath); if (!info) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } if (info.size === 0) { - nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]) - return + nextTick(callback, [null, encoding ? '' : Buffer.alloc(0)]); + return; } if (info.unpacked) { - const realPath = archive.copyFileOut(filePath) - return fs.readFile(realPath, options, callback) + const realPath = archive.copyFileOut(filePath); + return fs.readFile(realPath, options, callback); } - const buffer = Buffer.alloc(info.size) - const fd = archive.getFd() + const buffer = Buffer.alloc(info.size); + const fd = archive.getFd(); if (!(fd >= 0)) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } - logASARAccess(asarPath, filePath, info.offset) + logASARAccess(asarPath, filePath, info.offset); fs.read(fd, buffer, 0, info.size, info.offset, error => { - callback(error, encoding ? buffer.toString(encoding) : buffer) - }) - } + callback(error, encoding ? buffer.toString(encoding) : buffer); + }); + }; - fs.promises.readFile = util.promisify(fs.readFile) + fs.promises.readFile = util.promisify(fs.readFile); - const { readFileSync } = fs + const { readFileSync } = fs; fs.readFileSync = function (pathArgument, options) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return readFileSync.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return readFileSync.apply(this, arguments); - const archive = getOrCreateArchive(asarPath) - if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + const archive = getOrCreateArchive(asarPath); + if (!archive) throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); - const info = archive.getFileInfo(filePath) - if (!info) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + const info = archive.getFileInfo(filePath); + if (!info) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); - if (info.size === 0) return (options) ? '' : Buffer.alloc(0) + if (info.size === 0) return (options) ? '' : Buffer.alloc(0); if (info.unpacked) { - const realPath = archive.copyFileOut(filePath) - return fs.readFileSync(realPath, options) + const realPath = archive.copyFileOut(filePath); + return fs.readFileSync(realPath, options); } if (!options) { - options = { encoding: null } + options = { encoding: null }; } else if (typeof options === 'string') { - options = { encoding: options } + options = { encoding: options }; } else if (typeof options !== 'object') { - throw new TypeError('Bad arguments') + throw new TypeError('Bad arguments'); } - const { encoding } = options - const buffer = Buffer.alloc(info.size) - const fd = archive.getFd() - if (!(fd >= 0)) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + const { encoding } = options; + const buffer = Buffer.alloc(info.size); + const fd = archive.getFd(); + if (!(fd >= 0)) throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); - logASARAccess(asarPath, filePath, info.offset) - fs.readSync(fd, buffer, 0, info.size, info.offset) - return (encoding) ? buffer.toString(encoding) : buffer - } + logASARAccess(asarPath, filePath, info.offset); + fs.readSync(fd, buffer, 0, info.size, info.offset); + return (encoding) ? buffer.toString(encoding) : buffer; + }; - const { readdir } = fs + const { readdir } = fs; fs.readdir = function (pathArgument, options, callback) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); if (typeof options === 'function') { - callback = options - options = {} + callback = options; + options = {}; } - if (!isAsar) return readdir.apply(this, arguments) + if (!isAsar) return readdir.apply(this, arguments); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.INVALID_ARCHIVE, { asarPath }); + nextTick(callback, [error]); + return; } - const files = archive.readdir(filePath) + const files = archive.readdir(filePath); if (!files) { - const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_FOUND, { asarPath, filePath }); + nextTick(callback, [error]); + return; } - nextTick(callback, [null, files]) - } + nextTick(callback, [null, files]); + }; - fs.promises.readdir = util.promisify(fs.readdir) + fs.promises.readdir = util.promisify(fs.readdir); - const { readdirSync } = fs + const { readdirSync } = fs; fs.readdirSync = function (pathArgument, options) { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return readdirSync.apply(this, arguments) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return readdirSync.apply(this, arguments); - const archive = getOrCreateArchive(asarPath) + const archive = getOrCreateArchive(asarPath); if (!archive) { - throw createError(AsarError.INVALID_ARCHIVE, { asarPath }) + throw createError(AsarError.INVALID_ARCHIVE, { asarPath }); } - const files = archive.readdir(filePath) + const files = archive.readdir(filePath); if (!files) { - throw createError(AsarError.NOT_FOUND, { asarPath, filePath }) + throw createError(AsarError.NOT_FOUND, { asarPath, filePath }); } - return files - } + return files; + }; - const { internalModuleReadJSON } = internalBinding('fs') + const { internalModuleReadJSON } = internalBinding('fs'); internalBinding('fs').internalModuleReadJSON = pathArgument => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return internalModuleReadJSON(pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return internalModuleReadJSON(pathArgument); - const archive = getOrCreateArchive(asarPath) - if (!archive) return + const archive = getOrCreateArchive(asarPath); + if (!archive) return; - const info = archive.getFileInfo(filePath) - if (!info) return - if (info.size === 0) return '' + const info = archive.getFileInfo(filePath); + if (!info) return; + if (info.size === 0) return ''; if (info.unpacked) { - const realPath = archive.copyFileOut(filePath) - return fs.readFileSync(realPath, { encoding: 'utf8' }) + const realPath = archive.copyFileOut(filePath); + return fs.readFileSync(realPath, { encoding: 'utf8' }); } - const buffer = Buffer.alloc(info.size) - const fd = archive.getFd() - if (!(fd >= 0)) return + const buffer = Buffer.alloc(info.size); + const fd = archive.getFd(); + if (!(fd >= 0)) return; - logASARAccess(asarPath, filePath, info.offset) - fs.readSync(fd, buffer, 0, info.size, info.offset) - return buffer.toString('utf8') - } + logASARAccess(asarPath, filePath, info.offset); + fs.readSync(fd, buffer, 0, info.size, info.offset); + return buffer.toString('utf8'); + }; - const { internalModuleStat } = internalBinding('fs') + const { internalModuleStat } = internalBinding('fs'); internalBinding('fs').internalModuleStat = pathArgument => { - const { isAsar, asarPath, filePath } = splitPath(pathArgument) - if (!isAsar) return internalModuleStat(pathArgument) + const { isAsar, asarPath, filePath } = splitPath(pathArgument); + if (!isAsar) return internalModuleStat(pathArgument); // -ENOENT - const archive = getOrCreateArchive(asarPath) - if (!archive) return -34 + const archive = getOrCreateArchive(asarPath); + if (!archive) return -34; // -ENOENT - const stats = archive.stat(filePath) - if (!stats) return -34 + const stats = archive.stat(filePath); + if (!stats) return -34; - return (stats.isDirectory) ? 1 : 0 - } + return (stats.isDirectory) ? 1 : 0; + }; // Calling mkdir for directory inside asar archive should throw ENOTDIR // error, but on Windows it throws ENOENT. if (process.platform === 'win32') { - const { mkdir } = fs + const { mkdir } = fs; fs.mkdir = (pathArgument, options, callback) => { if (typeof options === 'function') { - callback = options - options = {} + callback = options; + options = {}; } - const { isAsar, filePath } = splitPath(pathArgument) + const { isAsar, filePath } = splitPath(pathArgument); if (isAsar && filePath.length > 0) { - const error = createError(AsarError.NOT_DIR) - nextTick(callback, [error]) - return + const error = createError(AsarError.NOT_DIR); + nextTick(callback, [error]); + return; } - mkdir(pathArgument, options, callback) - } + mkdir(pathArgument, options, callback); + }; - fs.promises.mkdir = util.promisify(fs.mkdir) + fs.promises.mkdir = util.promisify(fs.mkdir); - const { mkdirSync } = fs + const { mkdirSync } = fs; fs.mkdirSync = function (pathArgument, options) { - const { isAsar, filePath } = splitPath(pathArgument) - if (isAsar && filePath.length) throw createError(AsarError.NOT_DIR) - return mkdirSync(pathArgument, options) - } + const { isAsar, filePath } = splitPath(pathArgument); + if (isAsar && filePath.length) throw createError(AsarError.NOT_DIR); + return mkdirSync(pathArgument, options); + }; } function invokeWithNoAsar (func) { return function () { - const processNoAsarOriginalValue = process.noAsar - process.noAsar = true + const processNoAsarOriginalValue = process.noAsar; + process.noAsar = true; try { - return func.apply(this, arguments) + return func.apply(this, arguments); } finally { - process.noAsar = processNoAsarOriginalValue + process.noAsar = processNoAsarOriginalValue; } - } + }; } // Strictly implementing the flags of fs.copyFile is hard, just do a simple // implementation for now. Doing 2 copies won't spend much time more as OS // has filesystem caching. - overrideAPI(fs, 'copyFile') - overrideAPISync(fs, 'copyFileSync') + overrideAPI(fs, 'copyFile'); + overrideAPISync(fs, 'copyFileSync'); - overrideAPI(fs, 'open') - overrideAPISync(process, 'dlopen', 1) - overrideAPISync(Module._extensions, '.node', 1) - overrideAPISync(fs, 'openSync') + overrideAPI(fs, 'open'); + overrideAPISync(process, 'dlopen', 1); + overrideAPISync(Module._extensions, '.node', 1); + overrideAPISync(fs, 'openSync'); // Lazily override the child_process APIs only when child_process is fetched the first time - const originalModuleLoad = Module._load + const originalModuleLoad = Module._load; Module._load = (request, ...args) => { - const loadResult = originalModuleLoad(request, ...args) + const loadResult = originalModuleLoad(request, ...args); if (request === 'child_process') { if (!v8Util.getHiddenValue(loadResult, 'asar-ready')) { - v8Util.setHiddenValue(loadResult, 'asar-ready', true) + v8Util.setHiddenValue(loadResult, 'asar-ready', true); // Just to make it obvious what we are dealing with here - const childProcess = loadResult + const childProcess = loadResult; // Executing a command string containing a path to an asar // archive confuses `childProcess.execFile`, which is internally // called by `childProcess.{exec,execSync}`, causing // Electron to consider the full command as a single path // to an archive. - const { exec, execSync } = childProcess - childProcess.exec = invokeWithNoAsar(exec) - childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom]) - childProcess.execSync = invokeWithNoAsar(execSync) + const { exec, execSync } = childProcess; + childProcess.exec = invokeWithNoAsar(exec); + childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom]); + childProcess.execSync = invokeWithNoAsar(execSync); - overrideAPI(childProcess, 'execFile') - overrideAPISync(childProcess, 'execFileSync') + overrideAPI(childProcess, 'execFile'); + overrideAPISync(childProcess, 'execFileSync'); } } - return loadResult - } - } -})() + return loadResult; + }; + }; +})(); diff --git a/lib/common/asar_init.js b/lib/common/asar_init.js index 0d6d145b1104e..ae00669bf5f8f 100644 --- a/lib/common/asar_init.js +++ b/lib/common/asar_init.js @@ -1,6 +1,6 @@ -'use strict' +'use strict'; /* global require */ // Monkey-patch the fs module. -require('electron/js2c/asar').wrapFsWithAsar(require('fs')) +require('electron/js2c/asar').wrapFsWithAsar(require('fs')); diff --git a/lib/common/clipboard-utils.ts b/lib/common/clipboard-utils.ts deleted file mode 100644 index 166bf74be5625..0000000000000 --- a/lib/common/clipboard-utils.ts +++ /dev/null @@ -1,39 +0,0 @@ -const { nativeImage, NativeImage } = process.electronBinding('native_image') - -const objectMap = function (source: Object, mapper: (value: any) => any) { - const sourceEntries = Object.entries(source) - const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)]) - return Object.fromEntries(targetEntries) -} - -export function serialize (value: any): any { - if (value instanceof NativeImage) { - return { - buffer: value.toBitmap(), - size: value.getSize(), - __ELECTRON_SERIALIZED_NativeImage__: true - } - } else if (Array.isArray(value)) { - return value.map(serialize) - } else if (value instanceof Buffer) { - return value - } else if (value instanceof Object) { - return objectMap(value, serialize) - } else { - return value - } -} - -export function deserialize (value: any): any { - if (value && value.__ELECTRON_SERIALIZED_NativeImage__) { - return nativeImage.createFromBitmap(value.buffer, value.size) - } else if (Array.isArray(value)) { - return value.map(deserialize) - } else if (value instanceof Buffer) { - return value - } else if (value instanceof Object) { - return objectMap(value, deserialize) - } else { - return value - } -} diff --git a/lib/common/crash-reporter.js b/lib/common/crash-reporter.js index 9219af0807d58..36df08928cf62 100644 --- a/lib/common/crash-reporter.js +++ b/lib/common/crash-reporter.js @@ -1,19 +1,19 @@ -'use strict' +'use strict'; -const binding = process.electronBinding('crash_reporter') +const binding = process.electronBinding('crash_reporter'); class CrashReporter { - contructor () { - this.productName = null - this.crashesDirectory = null + constructor () { + this.productName = null; + this.crashesDirectory = null; } init (options) { - throw new Error('Not implemented') + throw new Error('Not implemented'); } start (options) { - if (options == null) options = {} + if (options == null) options = {}; const { productName, @@ -22,72 +22,77 @@ class CrashReporter { ignoreSystemCrashHandler = false, submitURL, uploadToServer = true - } = options + } = options; - if (companyName == null) throw new Error('companyName is a required option to crashReporter.start') - if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start') + if (companyName == null) throw new Error('companyName is a required option to crashReporter.start'); + if (submitURL == null) throw new Error('submitURL is a required option to crashReporter.start'); const ret = this.init({ submitURL, productName - }) + }); - this.productName = ret.productName - this.crashesDirectory = ret.crashesDirectory + this.productName = ret.productName; + this.crashesDirectory = ret.crashesDirectory; - if (extra._productName == null) extra._productName = ret.productName - if (extra._companyName == null) extra._companyName = companyName - if (extra._version == null) extra._version = ret.appVersion + if (extra._productName == null) extra._productName = ret.productName; + if (extra._companyName == null) extra._companyName = companyName; + if (extra._version == null) extra._version = ret.appVersion; - binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra) + binding.start(ret.productName, companyName, submitURL, ret.crashesDirectory, uploadToServer, ignoreSystemCrashHandler, extra); } getLastCrashReport () { const reports = this.getUploadedReports() .sort((a, b) => { - const ats = (a && a.date) ? new Date(a.date).getTime() : 0 - const bts = (b && b.date) ? new Date(b.date).getTime() : 0 - return bts - ats - }) + const ats = (a && a.date) ? new Date(a.date).getTime() : 0; + const bts = (b && b.date) ? new Date(b.date).getTime() : 0; + return bts - ats; + }); - return (reports.length > 0) ? reports[0] : null + return (reports.length > 0) ? reports[0] : null; } getUploadedReports () { - return binding.getUploadedReports(this.getCrashesDirectory()) + const crashDir = this.getCrashesDirectory(); + if (!crashDir) { + throw new Error('crashReporter has not been started'); + } + + return binding.getUploadedReports(crashDir); } getCrashesDirectory () { - return this.crashesDirectory + return this.crashesDirectory; } getUploadToServer () { if (process.type === 'browser') { - return binding.getUploadToServer() + return binding.getUploadToServer(); } else { - throw new Error('getUploadToServer can only be called from the main process') + throw new Error('getUploadToServer can only be called from the main process'); } } setUploadToServer (uploadToServer) { if (process.type === 'browser') { - return binding.setUploadToServer(uploadToServer) + return binding.setUploadToServer(uploadToServer); } else { - throw new Error('setUploadToServer can only be called from the main process') + throw new Error('setUploadToServer can only be called from the main process'); } } addExtraParameter (key, value) { - binding.addExtraParameter(key, value) + binding.addExtraParameter(key, value); } removeExtraParameter (key) { - binding.removeExtraParameter(key) + binding.removeExtraParameter(key); } getParameters () { - return binding.getParameters() + return binding.getParameters(); } } -module.exports = CrashReporter +module.exports = CrashReporter; diff --git a/lib/common/define-properties.ts b/lib/common/define-properties.ts index 7f2ddf5a46aed..add72cd846656 100644 --- a/lib/common/define-properties.ts +++ b/lib/common/define-properties.ts @@ -1,17 +1,17 @@ const handleESModule = (loader: ElectronInternal.ModuleLoader) => () => { - const value = loader() - if (value.__esModule && value.default) return value.default - return value -} + const value = loader(); + if (value.__esModule && value.default) return value.default; + return value; +}; // Attaches properties to |targetExports|. export function defineProperties (targetExports: Object, moduleList: ElectronInternal.ModuleEntry[]) { - const descriptors: PropertyDescriptorMap = {} + const descriptors: PropertyDescriptorMap = {}; for (const module of moduleList) { descriptors[module.name] = { enumerable: !module.private, get: handleESModule(module.loader) - } + }; } - return Object.defineProperties(targetExports, descriptors) + return Object.defineProperties(targetExports, descriptors); } diff --git a/lib/common/electron-binding-setup.ts b/lib/common/electron-binding-setup.ts index fcb79d3126b2b..f03f2beb440f6 100644 --- a/lib/common/electron-binding-setup.ts +++ b/lib/common/electron-binding-setup.ts @@ -1,13 +1,13 @@ export function electronBindingSetup (binding: typeof process['_linkedBinding'], processType: typeof process['type']): typeof process['electronBinding'] { return function electronBinding (name: string) { try { - return binding(`atom_${processType}_${name}`) + return binding(`atom_${processType}_${name}`); } catch (error) { if (/No such module/.test(error.message)) { - return binding(`atom_common_${name}`) + return binding(`atom_common_${name}`); } else { - throw error + throw error; } } - } + }; } diff --git a/lib/common/init.ts b/lib/common/init.ts index 425efac85d9c7..cabdfd8b4a11f 100644 --- a/lib/common/init.ts +++ b/lib/common/init.ts @@ -1,10 +1,10 @@ -import * as util from 'util' +import * as util from 'util'; -import { electronBindingSetup } from '@electron/internal/common/electron-binding-setup' +import { electronBindingSetup } from '@electron/internal/common/electron-binding-setup'; -const timers = require('timers') +const timers = require('timers'); -process.electronBinding = electronBindingSetup(process._linkedBinding, process.type) +process.electronBinding = electronBindingSetup(process._linkedBinding, process.type); type AnyFn = (...args: any[]) => any @@ -17,11 +17,11 @@ type AnyFn = (...args: any[]) => any const wrapWithActivateUvLoop = function (func: T): T { return wrap(func, function (func) { return function (this: any, ...args: any[]) { - process.activateUvLoop() - return func.apply(this, args) - } - }) as T -} + process.activateUvLoop(); + return func.apply(this, args); + }; + }) as T; +}; /** * Casts to any below for func are due to Typescript not supporting symbols @@ -30,41 +30,41 @@ const wrapWithActivateUvLoop = function (func: T): T { * Refs: https://github.com/Microsoft/TypeScript/issues/1863 */ function wrap (func: T, wrapper: (fn: AnyFn) => T) { - const wrapped = wrapper(func) + const wrapped = wrapper(func); if ((func as any)[util.promisify.custom]) { - (wrapped as any)[util.promisify.custom] = wrapper((func as any)[util.promisify.custom]) + (wrapped as any)[util.promisify.custom] = wrapper((func as any)[util.promisify.custom]); } - return wrapped + return wrapped; } -process.nextTick = wrapWithActivateUvLoop(process.nextTick) +process.nextTick = wrapWithActivateUvLoop(process.nextTick); -global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate) -global.clearImmediate = timers.clearImmediate +global.setImmediate = timers.setImmediate = wrapWithActivateUvLoop(timers.setImmediate); +global.clearImmediate = timers.clearImmediate; // setTimeout needs to update the polling timeout of the event loop, when // called under Chromium's event loop the node's event loop won't get a chance // to update the timeout, so we have to force the node's event loop to // recalculate the timeout in browser process. -timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout) -timers.setInterval = wrapWithActivateUvLoop(timers.setInterval) +timers.setTimeout = wrapWithActivateUvLoop(timers.setTimeout); +timers.setInterval = wrapWithActivateUvLoop(timers.setInterval); // Only override the global setTimeout/setInterval impls in the browser process if (process.type === 'browser') { - global.setTimeout = timers.setTimeout - global.setInterval = timers.setInterval + global.setTimeout = timers.setTimeout; + global.setInterval = timers.setInterval; } if (process.platform === 'win32') { // Always returns EOF for stdin stream. - const { Readable } = require('stream') - const stdin = new Readable() - stdin.push(null) + const { Readable } = require('stream'); + const stdin = new Readable(); + stdin.push(null); Object.defineProperty(process, 'stdin', { configurable: false, enumerable: true, get () { - return stdin + return stdin; } - }) + }); } diff --git a/lib/common/parse-features-string.js b/lib/common/parse-features-string.js index 1ef51092fdd3f..90c6f7ad140a5 100644 --- a/lib/common/parse-features-string.js +++ b/lib/common/parse-features-string.js @@ -1,21 +1,21 @@ -'use strict' +'use strict'; // parses a feature string that has the format used in window.open() // - `features` input string // - `emit` function(key, value) - called for each parsed KV module.exports = function parseFeaturesString (features, emit) { - features = `${features}`.trim() + features = `${features}`.trim(); // split the string by ',' features.split(/\s*,\s*/).forEach((feature) => { // expected form is either a key by itself or a key/value pair in the form of // 'key=value' - let [key, value] = feature.split(/\s*=\s*/) - if (!key) return + let [key, value] = feature.split(/\s*=\s*/); + if (!key) return; // interpret the value as a boolean, if possible - value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value + value = (value === 'yes' || value === '1') ? true : (value === 'no' || value === '0') ? false : value; // emit the parsed pair - emit(key, value) - }) -} + emit(key, value); + }); +}; diff --git a/lib/common/remote/type-utils.ts b/lib/common/remote/type-utils.ts deleted file mode 100644 index 58a57765fa341..0000000000000 --- a/lib/common/remote/type-utils.ts +++ /dev/null @@ -1,25 +0,0 @@ -export function isPromise (val: any) { - return ( - val && - val.then && - val.then instanceof Function && - val.constructor && - val.constructor.reject && - val.constructor.reject instanceof Function && - val.constructor.resolve && - val.constructor.resolve instanceof Function - ) -} - -const serializableTypes = [ - Boolean, - Number, - String, - Date, - RegExp, - ArrayBuffer -] - -export function isSerializableObject (value: any) { - return value === null || ArrayBuffer.isView(value) || serializableTypes.some(type => value instanceof type) -} diff --git a/lib/common/reset-search-paths.ts b/lib/common/reset-search-paths.ts index 30937d5a0dc56..143dc34d1e448 100644 --- a/lib/common/reset-search-paths.ts +++ b/lib/common/reset-search-paths.ts @@ -1,42 +1,42 @@ -import * as path from 'path' +import * as path from 'path'; -const Module = require('module') +const Module = require('module'); // Clear Node's global search paths. -Module.globalPaths.length = 0 +Module.globalPaths.length = 0; // Prevent Node from adding paths outside this app to search paths. -const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep -const originalNodeModulePaths = Module._nodeModulePaths +const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep; +const originalNodeModulePaths = Module._nodeModulePaths; Module._nodeModulePaths = function (from: string) { - const paths: string[] = originalNodeModulePaths(from) - const fromPath = path.resolve(from) + path.sep + const paths: string[] = originalNodeModulePaths(from); + const fromPath = path.resolve(from) + path.sep; // If "from" is outside the app then we do nothing. if (fromPath.startsWith(resourcesPathWithTrailingSlash)) { return paths.filter(function (candidate) { - return candidate.startsWith(resourcesPathWithTrailingSlash) - }) + return candidate.startsWith(resourcesPathWithTrailingSlash); + }); } else { - return paths + return paths; } -} +}; // Make a fake Electron module that we will insert into the module cache -const electronModule = new Module('electron', null) -electronModule.id = 'electron' -electronModule.loaded = true -electronModule.filename = 'electron' +const electronModule = new Module('electron', null); +electronModule.id = 'electron'; +electronModule.loaded = true; +electronModule.filename = 'electron'; Object.defineProperty(electronModule, 'exports', { get: () => require('electron') -}) +}); -Module._cache['electron'] = electronModule +Module._cache['electron'] = electronModule; -const originalResolveFilename = Module._resolveFilename +const originalResolveFilename = Module._resolveFilename; Module._resolveFilename = function (request: string, parent: NodeModule, isMain: boolean) { if (request === 'electron') { - return 'electron' + return 'electron'; } else { - return originalResolveFilename(request, parent, isMain) + return originalResolveFilename(request, parent, isMain); } -} +}; diff --git a/lib/common/type-utils.ts b/lib/common/type-utils.ts new file mode 100644 index 0000000000000..4d244b40ae65b --- /dev/null +++ b/lib/common/type-utils.ts @@ -0,0 +1,66 @@ +const { nativeImage, NativeImage } = process.electronBinding('native_image'); + +export function isPromise (val: any) { + return ( + val && + val.then && + val.then instanceof Function && + val.constructor && + val.constructor.reject && + val.constructor.reject instanceof Function && + val.constructor.resolve && + val.constructor.resolve instanceof Function + ); +} + +const serializableTypes = [ + Boolean, + Number, + String, + Date, + Error, + RegExp, + ArrayBuffer +]; + +export function isSerializableObject (value: any) { + return value === null || ArrayBuffer.isView(value) || serializableTypes.some(type => value instanceof type); +} + +const objectMap = function (source: Object, mapper: (value: any) => any) { + const sourceEntries = Object.entries(source); + const targetEntries = sourceEntries.map(([key, val]) => [key, mapper(val)]); + return Object.fromEntries(targetEntries); +}; + +export function serialize (value: any): any { + if (value instanceof NativeImage) { + return { + buffer: value.toBitmap(), + size: value.getSize(), + __ELECTRON_SERIALIZED_NativeImage__: true + }; + } else if (Array.isArray(value)) { + return value.map(serialize); + } else if (isSerializableObject(value)) { + return value; + } else if (value instanceof Object) { + return objectMap(value, serialize); + } else { + return value; + } +} + +export function deserialize (value: any): any { + if (value && value.__ELECTRON_SERIALIZED_NativeImage__) { + return nativeImage.createFromBitmap(value.buffer, value.size); + } else if (Array.isArray(value)) { + return value.map(deserialize); + } else if (isSerializableObject(value)) { + return value; + } else if (value instanceof Object) { + return objectMap(value, deserialize); + } else { + return value; + } +} diff --git a/lib/common/web-view-methods.ts b/lib/common/web-view-methods.ts index f3b6c3c0de319..449ee9d3915c4 100644 --- a/lib/common/web-view-methods.ts +++ b/lib/common/web-view-methods.ts @@ -48,11 +48,18 @@ export const syncMethods = new Set([ 'getZoomLevel', 'setZoomFactor', 'setZoomLevel' -]) +]); + +export const properties = new Set([ + 'audioMuted', + 'userAgent', + 'zoomLevel', + 'zoomFactor', + 'frameRate' +]); export const asyncMethods = new Set([ 'loadURL', - 'capturePage', 'executeJavaScript', 'insertCSS', 'insertText', @@ -63,4 +70,4 @@ export const asyncMethods = new Set([ 'setVisualZoomLevelLimits', 'print', 'printToPDF' -]) +]); diff --git a/lib/common/webpack-globals-provider.ts b/lib/common/webpack-globals-provider.ts new file mode 100644 index 0000000000000..8271118eea1c9 --- /dev/null +++ b/lib/common/webpack-globals-provider.ts @@ -0,0 +1,8 @@ +// Captures original globals into a scope to ensure that userland modifications do +// not impact Electron. Note that users doing: +// +// global.Promise.resolve = myFn +// +// Will mutate this captured one as well and that is OK. + +export const Promise = global.Promise; diff --git a/lib/content_script/init.js b/lib/content_script/init.js index 720fa88be67dd..1762b5b4d019d 100644 --- a/lib/content_script/init.js +++ b/lib/content_script/init.js @@ -1,37 +1,37 @@ -'use strict' +'use strict'; /* global nodeProcess, isolatedWorld, worldId */ -const { EventEmitter } = require('events') +const { EventEmitter } = require('events'); -process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer') +process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer'); -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); // The `lib/renderer/ipc-renderer-internal.js` module looks for the ipc object in the // "ipc-internal" hidden value -v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal')) +v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal')); // The process object created by webpack is not an event emitter, fix it so // the API is more compatible with non-sandboxed renderers. for (const prop of Object.keys(EventEmitter.prototype)) { if (process.hasOwnProperty(prop)) { - delete process[prop] + delete process[prop]; } } -Object.setPrototypeOf(process, EventEmitter.prototype) +Object.setPrototypeOf(process, EventEmitter.prototype); -const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args') +const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args'); if (isolatedWorldArgs) { - const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs - const { windowSetup } = require('@electron/internal/renderer/window-setup') - windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen) + const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs; + const { windowSetup } = require('@electron/internal/renderer/window-setup'); + windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen); } -const extensionId = v8Util.getHiddenValue(isolatedWorld, `extension-${worldId}`) +const extensionId = v8Util.getHiddenValue(isolatedWorld, `extension-${worldId}`); if (extensionId) { - const chromeAPI = require('@electron/internal/renderer/chrome-api') - chromeAPI.injectTo(extensionId, window) + const chromeAPI = require('@electron/internal/renderer/chrome-api'); + chromeAPI.injectTo(extensionId, window); } diff --git a/lib/isolated_renderer/init.js b/lib/isolated_renderer/init.js index e22d75934c1c3..60e00eafc2f87 100644 --- a/lib/isolated_renderer/init.js +++ b/lib/isolated_renderer/init.js @@ -1,27 +1,15 @@ -'use strict' +'use strict'; /* global nodeProcess, isolatedWorld */ -process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer') +process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(nodeProcess._linkedBinding, 'renderer'); -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); -// The `lib/renderer/ipc-renderer-internal.js` module looks for the ipc object in the -// "ipc-internal" hidden value -v8Util.setHiddenValue(global, 'ipc-internal', v8Util.getHiddenValue(isolatedWorld, 'ipc-internal')) - -const webViewImpl = v8Util.getHiddenValue(isolatedWorld, 'web-view-impl') +const webViewImpl = v8Util.getHiddenValue(isolatedWorld, 'web-view-impl'); if (webViewImpl) { // Must setup the WebView element in main world. - const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element') - setupWebView(v8Util, webViewImpl) -} - -const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-args') - -if (isolatedWorldArgs) { - const { guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs - const { windowSetup } = require('@electron/internal/renderer/window-setup') - windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen) + const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element'); + setupWebView(v8Util, webViewImpl); } diff --git a/lib/renderer/api/context-bridge.ts b/lib/renderer/api/context-bridge.ts index b435be60d7be1..9269d6985cdb3 100644 --- a/lib/renderer/api/context-bridge.ts +++ b/lib/renderer/api/context-bridge.ts @@ -1,20 +1,32 @@ -const { hasSwitch } = process.electronBinding('command_line') -const binding = process.electronBinding('context_bridge') +const { hasSwitch } = process.electronBinding('command_line'); +const binding = process.electronBinding('context_bridge'); -const contextIsolationEnabled = hasSwitch('context-isolation') +const contextIsolationEnabled = hasSwitch('context-isolation'); const checkContextIsolationEnabled = () => { - if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled') -} + if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled'); +}; const contextBridge = { exposeInMainWorld: (key: string, api: Record) => { - checkContextIsolationEnabled() - return binding.exposeAPIInMainWorld(key, api) - }, - debugGC: () => binding._debugGCMaps({}) -} + checkContextIsolationEnabled(); + return binding.exposeAPIInMainWorld(key, api); + } +}; -if (!binding._debugGCMaps) delete contextBridge.debugGC +export default contextBridge; -export default contextBridge +export const internalContextBridge = { + contextIsolationEnabled, + overrideGlobalValueFromIsolatedWorld: (keys: string[], value: any) => { + return binding._overrideGlobalValueFromIsolatedWorld(keys, value, false); + }, + overrideGlobalValueWithDynamicPropsFromIsolatedWorld: (keys: string[], value: any) => { + return binding._overrideGlobalValueFromIsolatedWorld(keys, value, true); + }, + overrideGlobalPropertyFromIsolatedWorld: (keys: string[], getter: Function, setter?: Function) => { + return binding._overrideGlobalPropertyFromIsolatedWorld(keys, getter, setter || null); + }, + isInMainWorld: () => binding._isCalledFromMainWorld() as boolean, + isInIsolatedWorld: () => binding._isCalledFromIsolatedWorld() as boolean +}; diff --git a/lib/renderer/api/crash-reporter.js b/lib/renderer/api/crash-reporter.js index 5931befb3fee1..48212ab170fab 100644 --- a/lib/renderer/api/crash-reporter.js +++ b/lib/renderer/api/crash-reporter.js @@ -1,12 +1,12 @@ -'use strict' +'use strict'; -const CrashReporter = require('@electron/internal/common/crash-reporter') -const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') +const CrashReporter = require('@electron/internal/common/crash-reporter'); +const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils'); class CrashReporterRenderer extends CrashReporter { init (options) { - return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options) + return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options); } } -module.exports = new CrashReporterRenderer() +module.exports = new CrashReporterRenderer(); diff --git a/lib/renderer/api/desktop-capturer.ts b/lib/renderer/api/desktop-capturer.ts index 538f6be1322b2..30838f070d230 100644 --- a/lib/renderer/api/desktop-capturer.ts +++ b/lib/renderer/api/desktop-capturer.ts @@ -1,39 +1,39 @@ -import { nativeImage } from 'electron' -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { nativeImage } from 'electron'; +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; -const { hasSwitch } = process.electronBinding('command_line') +const { hasSwitch } = process.electronBinding('command_line'); // |options.types| can't be empty and must be an array function isValid (options: Electron.SourcesOptions) { - const types = options ? options.types : undefined - return Array.isArray(types) + const types = options ? options.types : undefined; + return Array.isArray(types); } -const enableStacks = hasSwitch('enable-api-filtering-logging') +const enableStacks = hasSwitch('enable-api-filtering-logging'); function getCurrentStack () { - const target = {} + const target = {}; if (enableStacks) { - Error.captureStackTrace(target, getCurrentStack) + Error.captureStackTrace(target, getCurrentStack); } - return (target as any).stack + return (target as any).stack; } export async function getSources (options: Electron.SourcesOptions) { - if (!isValid(options)) throw new Error('Invalid options') + if (!isValid(options)) throw new Error('Invalid options'); - const captureWindow = options.types.includes('window') - const captureScreen = options.types.includes('screen') + const captureWindow = options.types.includes('window'); + const captureScreen = options.types.includes('screen'); - const { thumbnailSize = { width: 150, height: 150 } } = options - const { fetchWindowIcons = false } = options + const { thumbnailSize = { width: 150, height: 150 } } = options; + const { fetchWindowIcons = false } = options; const sources = await ipcRendererInternal.invoke('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', { captureWindow, captureScreen, thumbnailSize, fetchWindowIcons - } as ElectronInternal.GetSourcesOptions, getCurrentStack()) + } as ElectronInternal.GetSourcesOptions, getCurrentStack()); return sources.map(source => ({ id: source.id, @@ -41,5 +41,5 @@ export async function getSources (options: Electron.SourcesOptions) { thumbnail: nativeImage.createFromDataURL(source.thumbnail), display_id: source.display_id, appIcon: source.appIcon ? nativeImage.createFromDataURL(source.appIcon) : null - })) + })); } diff --git a/lib/renderer/api/exports/electron.ts b/lib/renderer/api/exports/electron.ts index 47a1dd09e1188..13ea2fc57a20a 100644 --- a/lib/renderer/api/exports/electron.ts +++ b/lib/renderer/api/exports/electron.ts @@ -1,6 +1,8 @@ -import { defineProperties } from '@electron/internal/common/define-properties' -import { commonModuleList } from '@electron/internal/common/api/module-list' -import { rendererModuleList } from '@electron/internal/renderer/api/module-list' +import { defineProperties } from '@electron/internal/common/define-properties'; +import { commonModuleList } from '@electron/internal/common/api/module-list'; +import { rendererModuleList } from '@electron/internal/renderer/api/module-list'; -defineProperties(exports, commonModuleList) -defineProperties(exports, rendererModuleList) +module.exports = {}; + +defineProperties(module.exports, commonModuleList); +defineProperties(module.exports, rendererModuleList); diff --git a/lib/renderer/api/ipc-renderer.ts b/lib/renderer/api/ipc-renderer.ts index 2a5085a825794..a7f675e335dc8 100644 --- a/lib/renderer/api/ipc-renderer.ts +++ b/lib/renderer/api/ipc-renderer.ts @@ -1,32 +1,34 @@ -const { ipc } = process.electronBinding('ipc') -const v8Util = process.electronBinding('v8_util') +const { ipc } = process.electronBinding('ipc'); +const v8Util = process.electronBinding('v8_util'); // Created by init.js. -const ipcRenderer = v8Util.getHiddenValue(global, 'ipc') -const internal = false +const ipcRenderer = v8Util.getHiddenValue(global, 'ipc'); +const internal = false; -ipcRenderer.send = function (channel, ...args) { - return ipc.send(internal, channel, args) -} +if (!ipcRenderer.send) { + ipcRenderer.send = function (channel, ...args) { + return ipc.send(internal, channel, args); + }; -ipcRenderer.sendSync = function (channel, ...args) { - return ipc.sendSync(internal, channel, args)[0] -} + ipcRenderer.sendSync = function (channel, ...args) { + return ipc.sendSync(internal, channel, args)[0]; + }; -ipcRenderer.sendToHost = function (channel, ...args) { - return ipc.sendToHost(channel, args) -} + ipcRenderer.sendToHost = function (channel, ...args) { + return ipc.sendToHost(channel, args); + }; -ipcRenderer.sendTo = function (webContentsId, channel, ...args) { - return ipc.sendTo(internal, false, webContentsId, channel, args) -} + ipcRenderer.sendTo = function (webContentsId, channel, ...args) { + return ipc.sendTo(internal, false, webContentsId, channel, args); + }; -ipcRenderer.invoke = async function (channel, ...args) { - const { error, result } = await ipc.invoke(internal, channel, args) - if (error) { - throw new Error(`Error invoking remote method '${channel}': ${error}`) - } - return result + ipcRenderer.invoke = async function (channel, ...args) { + const { error, result } = await ipc.invoke(internal, channel, args); + if (error) { + throw new Error(`Error invoking remote method '${channel}': ${error}`); + } + return result; + }; } -export default ipcRenderer +export default ipcRenderer; diff --git a/lib/renderer/api/module-list.ts b/lib/renderer/api/module-list.ts index f35f66f6ae449..d11e1020b520c 100644 --- a/lib/renderer/api/module-list.ts +++ b/lib/renderer/api/module-list.ts @@ -1,7 +1,7 @@ -const features = process.electronBinding('features') -const v8Util = process.electronBinding('v8_util') +const features = process.electronBinding('features'); +const v8Util = process.electronBinding('v8_util'); -const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule') +const enableRemoteModule = v8Util.getHiddenValue(global, 'enableRemoteModule'); // Renderer side modules, please sort alphabetically. export const rendererModuleList: ElectronInternal.ModuleEntry[] = [ @@ -9,12 +9,12 @@ export const rendererModuleList: ElectronInternal.ModuleEntry[] = [ { name: 'crashReporter', loader: () => require('./crash-reporter') }, { name: 'ipcRenderer', loader: () => require('./ipc-renderer') }, { name: 'webFrame', loader: () => require('./web-frame') } -] +]; if (features.isDesktopCapturerEnabled()) { - rendererModuleList.push({ name: 'desktopCapturer', loader: () => require('./desktop-capturer') }) + rendererModuleList.push({ name: 'desktopCapturer', loader: () => require('./desktop-capturer') }); } if (features.isRemoteModuleEnabled() && enableRemoteModule) { - rendererModuleList.push({ name: 'remote', loader: () => require('./remote') }) + rendererModuleList.push({ name: 'remote', loader: () => require('./remote') }); } diff --git a/lib/renderer/api/remote.js b/lib/renderer/api/remote.js index 53087896d9087..acb99e61718e2 100644 --- a/lib/renderer/api/remote.js +++ b/lib/renderer/api/remote.js @@ -1,26 +1,26 @@ -'use strict' +'use strict'; -const v8Util = process.electronBinding('v8_util') -const { hasSwitch } = process.electronBinding('command_line') +const v8Util = process.electronBinding('v8_util'); +const { hasSwitch } = process.electronBinding('command_line'); -const { CallbacksRegistry } = require('@electron/internal/renderer/remote/callbacks-registry') -const { isPromise, isSerializableObject } = require('@electron/internal/common/remote/type-utils') -const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal') +const { CallbacksRegistry } = require('@electron/internal/renderer/remote/callbacks-registry'); +const { isPromise, isSerializableObject } = require('@electron/internal/common/type-utils'); +const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); -const callbacksRegistry = new CallbacksRegistry() -const remoteObjectCache = v8Util.createIDWeakMap() +const callbacksRegistry = new CallbacksRegistry(); +const remoteObjectCache = v8Util.createIDWeakMap(); // An unique ID that can represent current context. -const contextId = v8Util.getHiddenValue(global, 'contextId') +const contextId = v8Util.getHiddenValue(global, 'contextId'); // Notify the main process when current context is going to be released. // Note that when the renderer process is destroyed, the message may not be // sent, we also listen to the "render-view-deleted" event in the main process // to guard that situation. process.on('exit', () => { - const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE' - ipcRendererInternal.send(command, contextId) -}) + const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'; + ipcRendererInternal.send(command, contextId); +}); // Convert the arguments object into an array of meta data. function wrapArgs (args, visited = new Set()) { @@ -30,187 +30,182 @@ function wrapArgs (args, visited = new Set()) { return { type: 'value', value: null - } + }; } if (Array.isArray(value)) { - visited.add(value) + visited.add(value); const meta = { type: 'array', value: wrapArgs(value, visited) - } - visited.delete(value) - return meta + }; + visited.delete(value); + return meta; } else if (value instanceof Buffer) { return { type: 'buffer', value - } + }; } else if (isSerializableObject(value)) { return { type: 'value', value - } + }; } else if (typeof value === 'object') { if (isPromise(value)) { return { type: 'promise', then: valueToMeta(function (onFulfilled, onRejected) { - value.then(onFulfilled, onRejected) + value.then(onFulfilled, onRejected); }) - } + }; } else if (v8Util.getHiddenValue(value, 'atomId')) { return { type: 'remote-object', id: v8Util.getHiddenValue(value, 'atomId') - } - } else if (value instanceof Error) { - return { - type: 'value', - value - } + }; } const meta = { type: 'object', name: value.constructor ? value.constructor.name : '', members: [] - } - visited.add(value) - for (const prop in value) { + }; + visited.add(value); + for (const prop in value) { // eslint-disable-line guard-for-in meta.members.push({ name: prop, value: valueToMeta(value[prop]) - }) + }); } - visited.delete(value) - return meta + visited.delete(value); + return meta; } else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) { return { type: 'function-with-return-value', value: valueToMeta(value()) - } + }; } else if (typeof value === 'function') { return { type: 'function', id: callbacksRegistry.add(value), location: v8Util.getHiddenValue(value, 'location'), length: value.length - } + }; } else { return { type: 'value', value - } + }; } - } - return args.map(valueToMeta) + }; + return args.map(valueToMeta); } // Populate object's members from descriptors. // The |ref| will be kept referenced by |members|. // This matches |getObjectMemebers| in rpc-server. function setObjectMembers (ref, object, metaId, members) { - if (!Array.isArray(members)) return + if (!Array.isArray(members)) return; for (const member of members) { - if (object.hasOwnProperty(member.name)) continue + if (object.hasOwnProperty(member.name)) continue; - const descriptor = { enumerable: member.enumerable } + const descriptor = { enumerable: member.enumerable }; if (member.type === 'method') { const remoteMemberFunction = function (...args) { - let command + let command; if (this && this.constructor === remoteMemberFunction) { - command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR' + command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR'; } else { - command = 'ELECTRON_BROWSER_MEMBER_CALL' + command = 'ELECTRON_BROWSER_MEMBER_CALL'; } - const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args)) - return metaToValue(ret) - } + const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args)); + return metaToValue(ret); + }; - let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name) + let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name); descriptor.get = () => { - descriptorFunction.ref = ref // The member should reference its object. - return descriptorFunction - } + descriptorFunction.ref = ref; // The member should reference its object. + return descriptorFunction; + }; // Enable monkey-patch the method descriptor.set = (value) => { - descriptorFunction = value - return value - } - descriptor.configurable = true + descriptorFunction = value; + return value; + }; + descriptor.configurable = true; } else if (member.type === 'get') { descriptor.get = () => { - const command = 'ELECTRON_BROWSER_MEMBER_GET' - const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name) - return metaToValue(meta) - } + const command = 'ELECTRON_BROWSER_MEMBER_GET'; + const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name); + return metaToValue(meta); + }; if (member.writable) { descriptor.set = (value) => { - const args = wrapArgs([value]) - const command = 'ELECTRON_BROWSER_MEMBER_SET' - const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args) - if (meta != null) metaToValue(meta) - return value - } + const args = wrapArgs([value]); + const command = 'ELECTRON_BROWSER_MEMBER_SET'; + const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args); + if (meta != null) metaToValue(meta); + return value; + }; } } - Object.defineProperty(object, member.name, descriptor) + Object.defineProperty(object, member.name, descriptor); } } // Populate object's prototype from descriptor. // This matches |getObjectPrototype| in rpc-server. function setObjectPrototype (ref, object, metaId, descriptor) { - if (descriptor === null) return - const proto = {} - setObjectMembers(ref, proto, metaId, descriptor.members) - setObjectPrototype(ref, proto, metaId, descriptor.proto) - Object.setPrototypeOf(object, proto) + if (descriptor === null) return; + const proto = {}; + setObjectMembers(ref, proto, metaId, descriptor.members); + setObjectPrototype(ref, proto, metaId, descriptor.proto); + Object.setPrototypeOf(object, proto); } // Wrap function in Proxy for accessing remote properties function proxyFunctionProperties (remoteMemberFunction, metaId, name) { - let loaded = false + let loaded = false; // Lazily load function properties const loadRemoteProperties = () => { - if (loaded) return - loaded = true - const command = 'ELECTRON_BROWSER_MEMBER_GET' - const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name) - setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members) - } + if (loaded) return; + loaded = true; + const command = 'ELECTRON_BROWSER_MEMBER_GET'; + const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name); + setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members); + }; return new Proxy(remoteMemberFunction, { set: (target, property, value, receiver) => { - if (property !== 'ref') loadRemoteProperties() - target[property] = value - return true + if (property !== 'ref') loadRemoteProperties(); + target[property] = value; + return true; }, get: (target, property, receiver) => { - if (!target.hasOwnProperty(property)) loadRemoteProperties() - const value = target[property] + if (!target.hasOwnProperty(property)) loadRemoteProperties(); + const value = target[property]; if (property === 'toString' && typeof value === 'function') { - return value.bind(target) + return value.bind(target); } - return value + return value; }, ownKeys: (target) => { - loadRemoteProperties() - return Object.getOwnPropertyNames(target) + loadRemoteProperties(); + return Object.getOwnPropertyNames(target); }, getOwnPropertyDescriptor: (target, property) => { - const descriptor = Object.getOwnPropertyDescriptor(target, property) - if (descriptor) return descriptor - loadRemoteProperties() - return Object.getOwnPropertyDescriptor(target, property) + const descriptor = Object.getOwnPropertyDescriptor(target, property); + if (descriptor) return descriptor; + loadRemoteProperties(); + return Object.getOwnPropertyDescriptor(target, property); } - }) + }); } // Convert meta data from browser into real value. @@ -221,150 +216,150 @@ function metaToValue (meta) { buffer: () => Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength), promise: () => Promise.resolve({ then: metaToValue(meta.then) }), error: () => metaToError(meta), - exception: () => { throw metaToError(meta.value) } - } + exception: () => { throw metaToError(meta.value); } + }; - if (meta.type in types) { - return types[meta.type]() + if (Object.prototype.hasOwnProperty.call(types, meta.type)) { + return types[meta.type](); } else { - let ret + let ret; if (remoteObjectCache.has(meta.id)) { - v8Util.addRemoteObjectRef(contextId, meta.id) - return remoteObjectCache.get(meta.id) + v8Util.addRemoteObjectRef(contextId, meta.id); + return remoteObjectCache.get(meta.id); } // A shadow class to represent the remote function object. if (meta.type === 'function') { const remoteFunction = function (...args) { - let command + let command; if (this && this.constructor === remoteFunction) { - command = 'ELECTRON_BROWSER_CONSTRUCTOR' + command = 'ELECTRON_BROWSER_CONSTRUCTOR'; } else { - command = 'ELECTRON_BROWSER_FUNCTION_CALL' + command = 'ELECTRON_BROWSER_FUNCTION_CALL'; } - const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args)) - return metaToValue(obj) - } - ret = remoteFunction + const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args)); + return metaToValue(obj); + }; + ret = remoteFunction; } else { - ret = {} + ret = {}; } - setObjectMembers(ret, ret, meta.id, meta.members) - setObjectPrototype(ret, ret, meta.id, meta.proto) - Object.defineProperty(ret.constructor, 'name', { value: meta.name }) + setObjectMembers(ret, ret, meta.id, meta.members); + setObjectPrototype(ret, ret, meta.id, meta.proto); + Object.defineProperty(ret.constructor, 'name', { value: meta.name }); // Track delegate obj's lifetime & tell browser to clean up when object is GCed. - v8Util.setRemoteObjectFreer(ret, contextId, meta.id) - v8Util.setHiddenValue(ret, 'atomId', meta.id) - v8Util.addRemoteObjectRef(contextId, meta.id) - remoteObjectCache.set(meta.id, ret) - return ret + v8Util.setRemoteObjectFreer(ret, contextId, meta.id); + v8Util.setHiddenValue(ret, 'atomId', meta.id); + v8Util.addRemoteObjectRef(contextId, meta.id); + remoteObjectCache.set(meta.id, ret); + return ret; } } function metaToError (meta) { - const obj = meta.value + const obj = meta.value; for (const { name, value } of meta.members) { - obj[name] = metaToValue(value) + obj[name] = metaToValue(value); } - return obj + return obj; } function handleMessage (channel, handler) { ipcRendererInternal.on(channel, (event, passedContextId, id, ...args) => { if (passedContextId === contextId) { - handler(id, ...args) + handler(id, ...args); } else { // Message sent to an un-exist context, notify the error to main process. - ipcRendererInternal.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id) + ipcRendererInternal.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id); } - }) + }); } -const enableStacks = hasSwitch('enable-api-filtering-logging') +const enableStacks = hasSwitch('enable-api-filtering-logging'); function getCurrentStack () { - const target = {} + const target = {}; if (enableStacks) { - Error.captureStackTrace(target, getCurrentStack) + Error.captureStackTrace(target, getCurrentStack); } - return target.stack + return target.stack; } // Browser calls a callback in renderer. handleMessage('ELECTRON_RENDERER_CALLBACK', (id, args) => { - callbacksRegistry.apply(id, metaToValue(args)) -}) + callbacksRegistry.apply(id, metaToValue(args)); +}); // A callback in browser is released. handleMessage('ELECTRON_RENDERER_RELEASE_CALLBACK', (id) => { - callbacksRegistry.remove(id) -}) + callbacksRegistry.remove(id); +}); exports.require = (module) => { - const command = 'ELECTRON_BROWSER_REQUIRE' - const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack()) - return metaToValue(meta) -} + const command = 'ELECTRON_BROWSER_REQUIRE'; + const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack()); + return metaToValue(meta); +}; // Alias to remote.require('electron').xxx. exports.getBuiltin = (module) => { - const command = 'ELECTRON_BROWSER_GET_BUILTIN' - const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack()) - return metaToValue(meta) -} + const command = 'ELECTRON_BROWSER_GET_BUILTIN'; + const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack()); + return metaToValue(meta); +}; exports.getCurrentWindow = () => { - const command = 'ELECTRON_BROWSER_CURRENT_WINDOW' - const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack()) - return metaToValue(meta) -} + const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'; + const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack()); + return metaToValue(meta); +}; // Get current WebContents object. exports.getCurrentWebContents = () => { - const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS' - const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack()) - return metaToValue(meta) -} + const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'; + const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack()); + return metaToValue(meta); +}; // Get a global object in browser. exports.getGlobal = (name) => { - const command = 'ELECTRON_BROWSER_GLOBAL' - const meta = ipcRendererInternal.sendSync(command, contextId, name, getCurrentStack()) - return metaToValue(meta) -} + const command = 'ELECTRON_BROWSER_GLOBAL'; + const meta = ipcRendererInternal.sendSync(command, contextId, name, getCurrentStack()); + return metaToValue(meta); +}; // Get the process object in browser. Object.defineProperty(exports, 'process', { get: () => exports.getGlobal('process') -}) +}); // Create a function that will return the specified value when called in browser. exports.createFunctionWithReturnValue = (returnValue) => { - const func = () => returnValue - v8Util.setHiddenValue(func, 'returnValue', true) - return func -} + const func = () => returnValue; + v8Util.setHiddenValue(func, 'returnValue', true); + return func; +}; // Get the guest WebContents from guestInstanceId. exports.getGuestWebContents = (guestInstanceId) => { - const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS' - const meta = ipcRendererInternal.sendSync(command, contextId, guestInstanceId, getCurrentStack()) - return metaToValue(meta) -} + const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS'; + const meta = ipcRendererInternal.sendSync(command, contextId, guestInstanceId, getCurrentStack()); + return metaToValue(meta); +}; const addBuiltinProperty = (name) => { Object.defineProperty(exports, name, { get: () => exports.getBuiltin(name) - }) -} + }); +}; -const { commonModuleList } = require('@electron/internal/common/api/module-list') -const browserModules = commonModuleList.concat(require('@electron/internal/browser/api/module-keys')) +const { commonModuleList } = require('@electron/internal/common/api/module-list'); +const browserModules = commonModuleList.concat(require('@electron/internal/browser/api/module-keys')); // And add a helper receiver for each one. browserModules .filter((m) => !m.private) .map((m) => m.name) - .forEach(addBuiltinProperty) + .forEach(addBuiltinProperty); diff --git a/lib/renderer/api/web-frame.ts b/lib/renderer/api/web-frame.ts index 7b34e87229d1e..2cd18446d4ff7 100644 --- a/lib/renderer/api/web-frame.ts +++ b/lib/renderer/api/web-frame.ts @@ -1,49 +1,56 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'events'; +import { deprecate } from 'electron'; -const binding = process.electronBinding('web_frame') +const binding = process.electronBinding('web_frame'); + +const setLayoutZoomLevelLimitsWarning = deprecate.warnOnce('setLayoutZoomLevelLimits'); class WebFrame extends EventEmitter { constructor (public context: Window) { - super() + super(); // Lots of webview would subscribe to webFrame's events. - this.setMaxListeners(0) + this.setMaxListeners(0); } findFrameByRoutingId (...args: Array) { - return getWebFrame(binding._findFrameByRoutingId(this.context, ...args)) + return getWebFrame(binding._findFrameByRoutingId(this.context, ...args)); } getFrameForSelector (...args: Array) { - return getWebFrame(binding._getFrameForSelector(this.context, ...args)) + return getWebFrame(binding._getFrameForSelector(this.context, ...args)); } findFrameByName (...args: Array) { - return getWebFrame(binding._findFrameByName(this.context, ...args)) + return getWebFrame(binding._findFrameByName(this.context, ...args)); } get opener () { - return getWebFrame(binding._getOpener(this.context)) + return getWebFrame(binding._getOpener(this.context)); } get parent () { - return getWebFrame(binding._getParent(this.context)) + return getWebFrame(binding._getParent(this.context)); } get top () { - return getWebFrame(binding._getTop(this.context)) + return getWebFrame(binding._getTop(this.context)); } get firstChild () { - return getWebFrame(binding._getFirstChild(this.context)) + return getWebFrame(binding._getFirstChild(this.context)); } get nextSibling () { - return getWebFrame(binding._getNextSibling(this.context)) + return getWebFrame(binding._getNextSibling(this.context)); } get routingId () { - return binding._getRoutingId(this.context) + return binding._getRoutingId(this.context); + } + + setLayoutZoomLevelLimits () { + setLayoutZoomLevelLimitsWarning(); } } @@ -53,17 +60,17 @@ for (const name in binding) { // TODO(felixrieseberg): Once we can type web_frame natives, we could // use a neat `keyof` here (WebFrame as any).prototype[name] = function (...args: Array) { - return binding[name](this.context, ...args) - } + return binding[name](this.context, ...args); + }; } } // Helper to return WebFrame or null depending on context. // TODO(zcbenz): Consider returning same WebFrame for the same frame. function getWebFrame (context: Window) { - return context ? new WebFrame(context) : null + return context ? new WebFrame(context) : null; } -const _webFrame = new WebFrame(window) +const _webFrame = new WebFrame(window); -export default _webFrame +export default _webFrame; diff --git a/lib/renderer/chrome-api.ts b/lib/renderer/chrome-api.ts index 8c5538378c13d..cdd9db6395279 100644 --- a/lib/renderer/chrome-api.ts +++ b/lib/renderer/chrome-api.ts @@ -1,14 +1,14 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' -import * as url from 'url' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; +import * as url from 'url'; -import { Event } from '@electron/internal/renderer/extensions/event' +import { Event } from '@electron/internal/renderer/extensions/event'; class Tab { public id: number constructor (tabId: number) { - this.id = tabId + this.id = tabId; } } @@ -18,9 +18,9 @@ class MessageSender { public url: string constructor (tabId: number, extensionId: string) { - this.tab = tabId ? new Tab(tabId) : null - this.id = extensionId - this.url = `chrome-extension://${extensionId}` + this.tab = tabId ? new Tab(tabId) : null; + this.id = extensionId; + this.url = `chrome-extension://${extensionId}`; } } @@ -31,69 +31,69 @@ class Port { public sender: MessageSender constructor (public tabId: number, public portId: number, extensionId: string, public name: string) { - this.onDisconnect = new Event() - this.onMessage = new Event() - this.sender = new MessageSender(tabId, extensionId) + this.onDisconnect = new Event(); + this.onMessage = new Event(); + this.sender = new MessageSender(tabId, extensionId); ipcRendererInternal.once(`CHROME_PORT_DISCONNECT_${portId}`, () => { - this._onDisconnect() - }) + this._onDisconnect(); + }); ipcRendererInternal.on(`CHROME_PORT_POSTMESSAGE_${portId}`, ( _event: Electron.Event, message: any ) => { - const sendResponse = function () { console.error('sendResponse is not implemented') } - this.onMessage.emit(JSON.parse(message), this.sender, sendResponse) - }) + const sendResponse = function () { console.error('sendResponse is not implemented'); }; + this.onMessage.emit(JSON.parse(message), this.sender, sendResponse); + }); } disconnect () { - if (this.disconnected) return + if (this.disconnected) return; - ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`) - this._onDisconnect() + ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`); + this._onDisconnect(); } postMessage (message: any) { - ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, JSON.stringify(message)) + ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, JSON.stringify(message)); } _onDisconnect () { - this.disconnected = true - ipcRendererInternal.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`) - this.onDisconnect.emit() + this.disconnected = true; + ipcRendererInternal.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`); + this.onDisconnect.emit(); } } // Inject chrome API to the |context| export function injectTo (extensionId: string, context: any) { if (process.electronBinding('features').isExtensionsEnabled()) { - throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled') + throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled'); } - const chrome = context.chrome = context.chrome || {} + const chrome = context.chrome = context.chrome || {}; ipcRendererInternal.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, ( _event: Electron.Event, tabId: number, portId: number, connectInfo: { name: string } ) => { - chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name)) - }) + chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name)); + }); ipcRendererUtils.handle(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, ( _event: Electron.Event, tabId: number, message: string ) => { return new Promise(resolve => { - chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), resolve) - }) - }) + chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), resolve); + }); + }); ipcRendererInternal.on('CHROME_TABS_ONCREATED', (_event: Electron.Event, tabId: number) => { - chrome.tabs.onCreated.emit(new Tab(tabId)) - }) + chrome.tabs.onCreated.emit(new Tab(tabId)); + }); ipcRendererInternal.on('CHROME_TABS_ONREMOVED', (_event: Electron.Event, tabId: number) => { - chrome.tabs.onRemoved.emit(tabId) - }) + chrome.tabs.onRemoved.emit(tabId); + }); chrome.runtime = { id: extensionId, @@ -105,65 +105,69 @@ export function injectTo (extensionId: string, context: any) { slashes: true, hostname: extensionId, pathname: path - }) + }); }, // https://developer.chrome.com/extensions/runtime#method-getManifest getManifest: function () { - const manifest = ipcRendererUtils.invokeSync('CHROME_EXTENSION_MANIFEST', extensionId) - return manifest + const manifest = ipcRendererUtils.invokeSync('CHROME_EXTENSION_MANIFEST', extensionId); + return manifest; }, // https://developer.chrome.com/extensions/runtime#method-connect connect (...args: Array) { // Parse the optional args. - let targetExtensionId = extensionId - let connectInfo = { name: '' } + let targetExtensionId = extensionId; + let connectInfo = { name: '' }; if (args.length === 1) { - targetExtensionId = args[0] + if (typeof args[0] === 'string') { + targetExtensionId = args[0]; + } else { + connectInfo = args[0]; + } } else if (args.length === 2) { - [targetExtensionId, connectInfo] = args + [targetExtensionId, connectInfo] = args; } - const { tabId, portId } = ipcRendererUtils.invokeSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo) - return new Port(tabId, portId, extensionId, connectInfo.name) + const { tabId, portId } = ipcRendererUtils.invokeSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo); + return new Port(tabId, portId, extensionId, connectInfo.name); }, // https://developer.chrome.com/extensions/runtime#method-sendMessage sendMessage (...args: Array) { // Parse the optional args. - const targetExtensionId = extensionId - let message: string - let options: Object | undefined - let responseCallback: Chrome.Tabs.SendMessageCallback = () => {} + const targetExtensionId = extensionId; + let message: string; + let options: Object | undefined; + let responseCallback: Chrome.Tabs.SendMessageCallback = () => {}; if (typeof args[args.length - 1] === 'function') { - responseCallback = args.pop() + responseCallback = args.pop(); } if (args.length === 1) { - [message] = args + [message] = args; } else if (args.length === 2) { if (typeof args[0] === 'string') { - [extensionId, message] = args + [extensionId, message] = args; } else { - [message, options] = args + [message, options] = args; } } else { - [extensionId, message, options] = args + [extensionId, message, options] = args; } if (options) { - console.error('options are not supported') + console.error('options are not supported'); } - ipcRendererInternal.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback) + ipcRendererInternal.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback); }, onConnect: new Event(), onMessage: new Event(), onInstalled: new Event() - } + }; chrome.tabs = { // https://developer.chrome.com/extensions/tabs#method-executeScript @@ -173,7 +177,7 @@ export function injectTo (extensionId: string, context: any) { resultCallback: Chrome.Tabs.ExecuteScriptCallback = () => {} ) { ipcRendererInternal.invoke('CHROME_TABS_EXECUTE_SCRIPT', tabId, extensionId, details) - .then((result: any) => resultCallback([result])) + .then((result: any) => resultCallback([result])); }, // https://developer.chrome.com/extensions/tabs#method-sendMessage @@ -183,13 +187,13 @@ export function injectTo (extensionId: string, context: any) { _options: Chrome.Tabs.SendMessageDetails, responseCallback: Chrome.Tabs.SendMessageCallback = () => {} ) { - ipcRendererInternal.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback) + ipcRendererInternal.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback); }, onUpdated: new Event(), onCreated: new Event(), onRemoved: new Event() - } + }; chrome.extension = { getURL: chrome.runtime.getURL, @@ -197,9 +201,9 @@ export function injectTo (extensionId: string, context: any) { onConnect: chrome.runtime.onConnect, sendMessage: chrome.runtime.sendMessage, onMessage: chrome.runtime.onMessage - } + }; - chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId) + chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId); chrome.pageAction = { show () {}, @@ -209,14 +213,14 @@ export function injectTo (extensionId: string, context: any) { setIcon () {}, setPopup () {}, getPopup () {} - } + }; - chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId) - chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup() + chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId); + chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup(); // Electron has no concept of a browserAction but we should stub these APIs for compatibility chrome.browserAction = { setIcon () {}, setPopup () {} - } + }; } diff --git a/lib/renderer/content-scripts-injector.ts b/lib/renderer/content-scripts-injector.ts index d98237717958a..26deb6dcc4896 100644 --- a/lib/renderer/content-scripts-injector.ts +++ b/lib/renderer/content-scripts-injector.ts @@ -1,8 +1,8 @@ -import { webFrame } from 'electron' +import { webFrame } from 'electron'; -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); const IsolatedWorldIDs = { /** @@ -10,93 +10,93 @@ const IsolatedWorldIDs = { * atom_render_frame_observer.h */ ISOLATED_WORLD_EXTENSIONS: 1 << 20 -} +}; -let isolatedWorldIds = IsolatedWorldIDs.ISOLATED_WORLD_EXTENSIONS -const extensionWorldId: {[key: string]: number | undefined} = {} +let isolatedWorldIds = IsolatedWorldIDs.ISOLATED_WORLD_EXTENSIONS; +const extensionWorldId: {[key: string]: number | undefined} = {}; // https://cs.chromium.org/chromium/src/extensions/renderer/script_injection.cc?type=cs&sq=package:chromium&g=0&l=52 const getIsolatedWorldIdForInstance = () => { // TODO(samuelmaddock): allocate and cleanup IDs - return isolatedWorldIds++ -} + return isolatedWorldIds++; +}; const escapePattern = function (pattern: string) { - return pattern.replace(/[\\^$+?.()|[\]{}]/g, '\\$&') -} + return pattern.replace(/[\\^$+?.()|[\]{}]/g, '\\$&'); +}; // Check whether pattern matches. // https://developer.chrome.com/extensions/match_patterns const matchesPattern = function (pattern: string) { - if (pattern === '') return true - const regexp = new RegExp(`^${pattern.split('*').map(escapePattern).join('.*')}$`) - const url = `${location.protocol}//${location.host}${location.pathname}` - return url.match(regexp) -} + if (pattern === '') return true; + const regexp = new RegExp(`^${pattern.split('*').map(escapePattern).join('.*')}$`); + const url = `${location.protocol}//${location.host}${location.pathname}`; + return url.match(regexp); +}; // Run the code with chrome API integrated. const runContentScript = function (this: any, extensionId: string, url: string, code: string) { // Assign unique world ID to each extension const worldId = extensionWorldId[extensionId] || - (extensionWorldId[extensionId] = getIsolatedWorldIdForInstance()) + (extensionWorldId[extensionId] = getIsolatedWorldIdForInstance()); // store extension ID for content script to read in isolated world - v8Util.setHiddenValue(global, `extension-${worldId}`, extensionId) + v8Util.setHiddenValue(global, `extension-${worldId}`, extensionId); webFrame.setIsolatedWorldInfo(worldId, { name: `${extensionId} [${worldId}]` // TODO(samuelmaddock): read `content_security_policy` from extension manifest // csp: manifest.content_security_policy, - }) + }); - const sources = [{ code, url }] - return webFrame.executeJavaScriptInIsolatedWorld(worldId, sources) -} + const sources = [{ code, url }]; + return webFrame.executeJavaScriptInIsolatedWorld(worldId, sources); +}; const runAllContentScript = function (scripts: Array, extensionId: string) { for (const { url, code } of scripts) { - runContentScript.call(window, extensionId, url, code) + runContentScript.call(window, extensionId, url, code); } -} +}; const runStylesheet = function (this: any, url: string, code: string) { - webFrame.insertCSS(code) -} + webFrame.insertCSS(code); +}; const runAllStylesheet = function (css: Array) { for (const { url, code } of css) { - runStylesheet.call(window, url, code) + runStylesheet.call(window, url, code); } -} +}; // Run injected scripts. // https://developer.chrome.com/extensions/content_scripts const injectContentScript = function (extensionId: string, script: Electron.ContentScript) { - if (!process.isMainFrame && !script.allFrames) return - if (!script.matches.some(matchesPattern)) return + if (!process.isMainFrame && !script.allFrames) return; + if (!script.matches.some(matchesPattern)) return; if (script.js) { - const fire = runAllContentScript.bind(window, script.js, extensionId) + const fire = runAllContentScript.bind(window, script.js, extensionId); if (script.runAt === 'document_start') { - process.once('document-start', fire) + process.once('document-start', fire); } else if (script.runAt === 'document_end') { - process.once('document-end', fire) + process.once('document-end', fire); } else { - document.addEventListener('DOMContentLoaded', fire) + document.addEventListener('DOMContentLoaded', fire); } } if (script.css) { - const fire = runAllStylesheet.bind(window, script.css) + const fire = runAllStylesheet.bind(window, script.css); if (script.runAt === 'document_start') { - process.once('document-start', fire) + process.once('document-start', fire); } else if (script.runAt === 'document_end') { - process.once('document-end', fire) + process.once('document-end', fire); } else { - document.addEventListener('DOMContentLoaded', fire) + document.addEventListener('DOMContentLoaded', fire); } } -} +}; // Handle the request of chrome.tabs.executeJavaScript. ipcRendererUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', function ( @@ -105,15 +105,15 @@ ipcRendererUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', function ( url: string, code: string ) { - return runContentScript.call(window, extensionId, url, code) -}) + return runContentScript.call(window, extensionId, url, code); +}); module.exports = (entries: Electron.ContentScriptEntry[]) => { for (const entry of entries) { if (entry.contentScripts) { for (const script of entry.contentScripts) { - injectContentScript(entry.extensionId, script) + injectContentScript(entry.extensionId, script); } } } -} +}; diff --git a/lib/renderer/extensions/event.ts b/lib/renderer/extensions/event.ts index d1fc5fb723e5f..d88800c4f93c7 100644 --- a/lib/renderer/extensions/event.ts +++ b/lib/renderer/extensions/event.ts @@ -2,19 +2,19 @@ export class Event { private listeners: Function[] = [] addListener (callback: Function) { - this.listeners.push(callback) + this.listeners.push(callback); } removeListener (callback: Function) { - const index = this.listeners.indexOf(callback) + const index = this.listeners.indexOf(callback); if (index !== -1) { - this.listeners.splice(index, 1) + this.listeners.splice(index, 1); } } emit (...args: any[]) { for (const listener of this.listeners) { - listener(...args) + listener(...args); } } } diff --git a/lib/renderer/extensions/i18n.ts b/lib/renderer/extensions/i18n.ts index 7d3c718434986..4f67670d3ea1d 100644 --- a/lib/renderer/extensions/i18n.ts +++ b/lib/renderer/extensions/i18n.ts @@ -4,7 +4,7 @@ // Does not implement predefined messages: // https://developer.chrome.com/extensions/i18n#overview-predefined -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; interface Placeholder { content: string; @@ -13,48 +13,48 @@ interface Placeholder { const getMessages = (extensionId: number) => { try { - const data = ipcRendererUtils.invokeSync('CHROME_GET_MESSAGES', extensionId) - return JSON.parse(data) || {} + const data = ipcRendererUtils.invokeSync('CHROME_GET_MESSAGES', extensionId); + return JSON.parse(data) || {}; } catch { - return {} + return {}; } -} +}; const replaceNumberedSubstitutions = (message: string, substitutions: string[]) => { return message.replace(/\$(\d+)/, (_, number) => { - const index = parseInt(number, 10) - 1 - return substitutions[index] || '' - }) -} + const index = parseInt(number, 10) - 1; + return substitutions[index] || ''; + }); +}; const replacePlaceholders = (message: string, placeholders: Record, substitutions: string[] | string) => { - if (typeof substitutions === 'string') substitutions = [substitutions] - if (!Array.isArray(substitutions)) substitutions = [] + if (typeof substitutions === 'string') substitutions = [substitutions]; + if (!Array.isArray(substitutions)) substitutions = []; if (placeholders) { Object.keys(placeholders).forEach((name: string) => { - let { content } = placeholders[name] - const substitutionsArray = Array.isArray(substitutions) ? substitutions : [] - content = replaceNumberedSubstitutions(content, substitutionsArray) - message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content) - }) + let { content } = placeholders[name]; + const substitutionsArray = Array.isArray(substitutions) ? substitutions : []; + content = replaceNumberedSubstitutions(content, substitutionsArray); + message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content); + }); } - return replaceNumberedSubstitutions(message, substitutions) -} + return replaceNumberedSubstitutions(message, substitutions); +}; const getMessage = (extensionId: number, messageName: string, substitutions: string[]) => { - const messages = getMessages(extensionId) + const messages = getMessages(extensionId); if (messages.hasOwnProperty(messageName)) { - const { message, placeholders } = messages[messageName] - return replacePlaceholders(message, placeholders, substitutions) + const { message, placeholders } = messages[messageName]; + return replacePlaceholders(message, placeholders, substitutions); } -} +}; exports.setup = (extensionId: number) => { return { getMessage (messageName: string, substitutions: string[]) { - return getMessage(extensionId, messageName, substitutions) + return getMessage(extensionId, messageName, substitutions); } - } -} + }; +}; diff --git a/lib/renderer/extensions/storage.ts b/lib/renderer/extensions/storage.ts index f5ed8b69cdab1..6dbafb6e5bd81 100644 --- a/lib/renderer/extensions/storage.ts +++ b/lib/renderer/extensions/storage.ts @@ -1,86 +1,86 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; const getStorage = (storageType: string, extensionId: number, callback: Function) => { - if (typeof callback !== 'function') throw new TypeError('No callback provided') + if (typeof callback !== 'function') throw new TypeError('No callback provided'); ipcRendererInternal.invoke('CHROME_STORAGE_READ', storageType, extensionId) .then(data => { if (data !== null) { - callback(JSON.parse(data)) + callback(JSON.parse(data)); } else { // Disabled due to false positive in StandardJS // eslint-disable-next-line standard/no-callback-literal - callback({}) + callback({}); } - }) -} + }); +}; const setStorage = (storageType: string, extensionId: number, storage: Record, callback: Function) => { - const json = JSON.stringify(storage) + const json = JSON.stringify(storage); ipcRendererInternal.invoke('CHROME_STORAGE_WRITE', storageType, extensionId, json) .then(() => { - if (callback) callback() - }) -} + if (callback) callback(); + }); +}; const getStorageManager = (storageType: string, extensionId: number) => { return { get (keys: string[], callback: Function) { getStorage(storageType, extensionId, (storage: Record) => { - if (keys == null) return callback(storage) + if (keys == null) return callback(storage); - let defaults: Record = {} + let defaults: Record = {}; switch (typeof keys) { case 'string': - keys = [keys] - break + keys = [keys]; + break; case 'object': if (!Array.isArray(keys)) { - defaults = keys - keys = Object.keys(keys) + defaults = keys; + keys = Object.keys(keys); } - break + break; } // Disabled due to false positive in StandardJS // eslint-disable-next-line standard/no-callback-literal - if (keys.length === 0) return callback({}) + if (keys.length === 0) return callback({}); - const items: Record = {} + const items: Record = {}; keys.forEach((key: string) => { - let value = storage[key] - if (value == null) value = defaults[key] - items[key] = value - }) - callback(items) - }) + let value = storage[key]; + if (value == null) value = defaults[key]; + items[key] = value; + }); + callback(items); + }); }, set (items: Record, callback: Function) { getStorage(storageType, extensionId, (storage: Record) => { - Object.keys(items).forEach(name => { storage[name] = items[name] }) - setStorage(storageType, extensionId, storage, callback) - }) + Object.keys(items).forEach(name => { storage[name] = items[name]; }); + setStorage(storageType, extensionId, storage, callback); + }); }, remove (keys: string[], callback: Function) { getStorage(storageType, extensionId, (storage: Record) => { - if (!Array.isArray(keys)) keys = [keys] + if (!Array.isArray(keys)) keys = [keys]; keys.forEach((key: string) => { - delete storage[key] - }) + delete storage[key]; + }); - setStorage(storageType, extensionId, storage, callback) - }) + setStorage(storageType, extensionId, storage, callback); + }); }, clear (callback: Function) { - setStorage(storageType, extensionId, {}, callback) + setStorage(storageType, extensionId, {}, callback); } - } -} + }; +}; export const setup = (extensionId: number) => ({ sync: getStorageManager('sync', extensionId), local: getStorageManager('local', extensionId) -}) +}); diff --git a/lib/renderer/extensions/web-navigation.ts b/lib/renderer/extensions/web-navigation.ts index fb39f11af0424..768eb342804b1 100644 --- a/lib/renderer/extensions/web-navigation.ts +++ b/lib/renderer/extensions/web-navigation.ts @@ -1,5 +1,5 @@ -import { Event } from '@electron/internal/renderer/extensions/event' -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { Event } from '@electron/internal/renderer/extensions/event'; +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; class WebNavigation { private onBeforeNavigate = new Event() @@ -7,13 +7,13 @@ class WebNavigation { constructor () { ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', (event: Electron.IpcRendererEvent, details: any) => { - this.onBeforeNavigate.emit(details) - }) + this.onBeforeNavigate.emit(details); + }); ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONCOMPLETED', (event: Electron.IpcRendererEvent, details: any) => { - this.onCompleted.emit(details) - }) + this.onCompleted.emit(details); + }); } } -export const setup = () => new WebNavigation() +export const setup = () => new WebNavigation(); diff --git a/lib/renderer/init.ts b/lib/renderer/init.ts index c189c2ab8d145..d94c3cd2e3e25 100644 --- a/lib/renderer/init.ts +++ b/lib/renderer/init.ts @@ -1,7 +1,7 @@ -import { EventEmitter } from 'events' -import * as path from 'path' +import { EventEmitter } from 'events'; +import * as path from 'path'; -const Module = require('module') +const Module = require('module'); // Make sure globals like "process" and "global" are always available in preload // scripts even after they are deleted in "loaded" script. @@ -23,42 +23,42 @@ Module.wrapper = [ // code to override "process" and "Buffer" with local variables. 'return function (exports, require, module, __filename, __dirname) { ', '\n}.call(this, exports, require, module, __filename, __dirname); });' -] +]; // We modified the original process.argv to let node.js load the // init.js, we need to restore it here. -process.argv.splice(1, 1) +process.argv.splice(1, 1); // Clear search paths. -require('../common/reset-search-paths') +require('../common/reset-search-paths'); // Import common settings. -require('@electron/internal/common/init') +require('@electron/internal/common/init'); // The global variable will be used by ipc for event dispatching -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); -const ipcEmitter = new EventEmitter() -const ipcInternalEmitter = new EventEmitter() -v8Util.setHiddenValue(global, 'ipc', ipcEmitter) -v8Util.setHiddenValue(global, 'ipc-internal', ipcInternalEmitter) +const ipcEmitter = new EventEmitter(); +const ipcInternalEmitter = new EventEmitter(); +v8Util.setHiddenValue(global, 'ipc', ipcEmitter); +v8Util.setHiddenValue(global, 'ipc-internal', ipcInternalEmitter); v8Util.setHiddenValue(global, 'ipcNative', { onMessage (internal: boolean, channel: string, args: any[], senderId: number) { - const sender = internal ? ipcInternalEmitter : ipcEmitter - sender.emit(channel, { sender, senderId }, ...args) + const sender = internal ? ipcInternalEmitter : ipcEmitter; + sender.emit(channel, { sender, senderId }, ...args); } -}) +}); // Use electron module after everything is ready. -const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal') -const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') -const { webFrameInit } = require('@electron/internal/renderer/web-frame-init') -webFrameInit() +const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); +const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils'); +const { webFrameInit } = require('@electron/internal/renderer/web-frame-init'); +webFrameInit(); // Process command line arguments. -const { hasSwitch, getSwitchValue } = process.electronBinding('command_line') +const { hasSwitch, getSwitchValue } = process.electronBinding('command_line'); const parseOption = function ( name: string, defaultValue: T, converter?: (value: string) => T @@ -69,104 +69,104 @@ const parseOption = function ( ? converter(getSwitchValue(name)) : getSwitchValue(name) ) - : defaultValue -} + : defaultValue; +}; -const contextIsolation = hasSwitch('context-isolation') -const nodeIntegration = hasSwitch('node-integration') -const webviewTag = hasSwitch('webview-tag') -const isHiddenPage = hasSwitch('hidden-page') -const usesNativeWindowOpen = hasSwitch('native-window-open') +const contextIsolation = hasSwitch('context-isolation'); +const nodeIntegration = hasSwitch('node-integration'); +const webviewTag = hasSwitch('webview-tag'); +const isHiddenPage = hasSwitch('hidden-page'); +const usesNativeWindowOpen = hasSwitch('native-window-open'); -const preloadScript = parseOption('preload', null) -const preloadScripts = parseOption('preload-scripts', [], value => value.split(path.delimiter)) as string[] -const appPath = parseOption('app-path', null) -const guestInstanceId = parseOption('guest-instance-id', null, value => parseInt(value)) -const openerId = parseOption('opener-id', null, value => parseInt(value)) +const preloadScript = parseOption('preload', null); +const preloadScripts = parseOption('preload-scripts', [], value => value.split(path.delimiter)) as string[]; +const appPath = parseOption('app-path', null); +const guestInstanceId = parseOption('guest-instance-id', null, value => parseInt(value)); +const openerId = parseOption('opener-id', null, value => parseInt(value)); // The arguments to be passed to isolated world. -const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } +const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen }; // The webContents preload script is loaded after the session preload scripts. if (preloadScript) { - preloadScripts.push(preloadScript) + preloadScripts.push(preloadScript); } switch (window.location.protocol) { case 'devtools:': { // Override some inspector APIs. - require('@electron/internal/renderer/inspector') - break + require('@electron/internal/renderer/inspector'); + break; } case 'chrome-extension:': { // Inject the chrome.* APIs that chrome extensions require if (!process.electronBinding('features').isExtensionsEnabled()) { - require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window) + require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window); } - break + break; } case 'chrome:': - break + break; default: { // Override default web functions. - const { windowSetup } = require('@electron/internal/renderer/window-setup') - windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen) + const { windowSetup } = require('@electron/internal/renderer/window-setup'); + windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen); // Inject content scripts. if (!process.electronBinding('features').isExtensionsEnabled()) { - const contentScripts = ipcRendererUtils.invokeSync('ELECTRON_GET_CONTENT_SCRIPTS') as Electron.ContentScriptEntry[] - require('@electron/internal/renderer/content-scripts-injector')(contentScripts) + const contentScripts = ipcRendererUtils.invokeSync('ELECTRON_GET_CONTENT_SCRIPTS') as Electron.ContentScriptEntry[]; + require('@electron/internal/renderer/content-scripts-injector')(contentScripts); } } } // Load webview tag implementation. if (process.isMainFrame) { - const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init') - webViewInit(contextIsolation, webviewTag, guestInstanceId) + const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init'); + webViewInit(contextIsolation, webviewTag, guestInstanceId); } // Pass the arguments to isolatedWorld. if (contextIsolation) { - v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs) + v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs); } if (nodeIntegration) { // Export node bindings to global. const { makeRequireFunction } = __non_webpack_require__('internal/modules/cjs/helpers') // eslint-disable-line - global.module = new Module('electron/js2c/renderer_init') - global.require = makeRequireFunction(global.module) + global.module = new Module('electron/js2c/renderer_init'); + global.require = makeRequireFunction(global.module); // Set the __filename to the path of html file if it is file: protocol. if (window.location.protocol === 'file:') { - const location = window.location - let pathname = location.pathname + const location = window.location; + let pathname = location.pathname; if (process.platform === 'win32') { - if (pathname[0] === '/') pathname = pathname.substr(1) + if (pathname[0] === '/') pathname = pathname.substr(1); - const isWindowsNetworkSharePath = location.hostname.length > 0 && process.resourcesPath.startsWith('\\') + const isWindowsNetworkSharePath = location.hostname.length > 0 && process.resourcesPath.startsWith('\\'); if (isWindowsNetworkSharePath) { - pathname = `//${location.host}/${pathname}` + pathname = `//${location.host}/${pathname}`; } } - global.__filename = path.normalize(decodeURIComponent(pathname)) - global.__dirname = path.dirname(global.__filename) + global.__filename = path.normalize(decodeURIComponent(pathname)); + global.__dirname = path.dirname(global.__filename); // Set module's filename so relative require can work as expected. - global.module.filename = global.__filename + global.module.filename = global.__filename; // Also search for module under the html file. - global.module.paths = Module._nodeModulePaths(global.__dirname) + global.module.paths = Module._nodeModulePaths(global.__dirname); } else { // For backwards compatibility we fake these two paths here - global.__filename = path.join(process.resourcesPath, 'electron.asar', 'renderer', 'init.js') - global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'renderer') + global.__filename = path.join(process.resourcesPath, 'electron.asar', 'renderer', 'init.js'); + global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'renderer'); if (appPath) { // Search for module under the app directory - global.module.paths = Module._nodeModulePaths(appPath) + global.module.paths = Module._nodeModulePaths(appPath); } } @@ -176,40 +176,42 @@ if (nodeIntegration) { // We do not want to add `uncaughtException` to our definitions // because we don't want anyone else (anywhere) to throw that kind // of error. - global.process.emit('uncaughtException' as any, error as any) - return true + global.process.emit('uncaughtException' as any, error as any); + return true; } else { - return false + return false; } - } + }; } else { // Delete Node's symbols after the Environment has been loaded in a // non context-isolated environment if (!contextIsolation) { process.once('loaded', function () { - delete global.process - delete global.Buffer - delete global.setImmediate - delete global.clearImmediate - delete global.global - }) + delete global.process; + delete global.Buffer; + delete global.setImmediate; + delete global.clearImmediate; + delete global.global; + delete global.root; + delete global.GLOBAL; + }); } } // Load the preload scripts. for (const preloadScript of preloadScripts) { try { - Module._load(preloadScript) + Module._load(preloadScript); } catch (error) { - console.error(`Unable to load preload script: ${preloadScript}`) - console.error(error) + console.error(`Unable to load preload script: ${preloadScript}`); + console.error(error); - ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, error) + ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, error); } } // Warn about security issues if (process.isMainFrame) { - const { securityWarnings } = require('@electron/internal/renderer/security-warnings') - securityWarnings(nodeIntegration) + const { securityWarnings } = require('@electron/internal/renderer/security-warnings'); + securityWarnings(nodeIntegration); } diff --git a/lib/renderer/inspector.ts b/lib/renderer/inspector.ts index 0f4927040cb25..ebb76cc8a2f52 100644 --- a/lib/renderer/inspector.ts +++ b/lib/renderer/inspector.ts @@ -1,61 +1,61 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; window.onload = function () { // Use menu API to show context menu. - window.InspectorFrontendHost!.showContextMenuAtPoint = createMenu + window.InspectorFrontendHost!.showContextMenuAtPoint = createMenu; // correct for Chromium returning undefined for filesystem - window.Persistence!.FileSystemWorkspaceBinding.completeURL = completeURL + window.Persistence!.FileSystemWorkspaceBinding.completeURL = completeURL; // Use dialog API to override file chooser dialog. - window.UI!.createFileSelectorElement = createFileSelectorElement -} + window.UI!.createFileSelectorElement = createFileSelectorElement; +}; // Extra / is needed as a result of MacOS requiring absolute paths function completeURL (project: string, path: string) { - project = 'file:///' - return `${project}${path}` + project = 'file:///'; + return `${project}${path}`; } // The DOM implementation expects (message?: string) => boolean (window.confirm as any) = function (message: string, title: string) { - return ipcRendererUtils.invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title) as boolean -} + return ipcRendererUtils.invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title) as boolean; +}; const useEditMenuItems = function (x: number, y: number, items: ContextMenuItem[]) { return items.length === 0 && document.elementsFromPoint(x, y).some(function (element) { return element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA' || - (element as HTMLElement).isContentEditable - }) -} + (element as HTMLElement).isContentEditable; + }); +}; const createMenu = function (x: number, y: number, items: ContextMenuItem[]) { - const isEditMenu = useEditMenuItems(x, y, items) + const isEditMenu = useEditMenuItems(x, y, items); ipcRendererInternal.invoke('ELECTRON_INSPECTOR_CONTEXT_MENU', items, isEditMenu).then(id => { if (typeof id === 'number') { - window.DevToolsAPI!.contextMenuItemSelected(id) + window.DevToolsAPI!.contextMenuItemSelected(id); } - window.DevToolsAPI!.contextMenuCleared() - }) -} + window.DevToolsAPI!.contextMenuCleared(); + }); +}; const showFileChooserDialog = function (callback: (blob: File) => void) { ipcRendererInternal.invoke<[ string, any ]>('ELECTRON_INSPECTOR_SELECT_FILE').then(([path, data]) => { if (path && data) { - callback(dataToHtml5FileObject(path, data)) + callback(dataToHtml5FileObject(path, data)); } - }) -} + }); +}; const dataToHtml5FileObject = function (path: string, data: any) { - return new File([data], path) -} + return new File([data], path); +}; const createFileSelectorElement = function (this: any, callback: () => void) { - const fileSelectorElement = document.createElement('span') - fileSelectorElement.style.display = 'none' - fileSelectorElement.click = showFileChooserDialog.bind(this, callback) - return fileSelectorElement -} + const fileSelectorElement = document.createElement('span'); + fileSelectorElement.style.display = 'none'; + fileSelectorElement.click = showFileChooserDialog.bind(this, callback); + return fileSelectorElement; +}; diff --git a/lib/renderer/ipc-renderer-internal-utils.ts b/lib/renderer/ipc-renderer-internal-utils.ts index 2a954c0f179be..28bd02e82dd38 100644 --- a/lib/renderer/ipc-renderer-internal-utils.ts +++ b/lib/renderer/ipc-renderer-internal-utils.ts @@ -1,24 +1,24 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; type IPCHandler = (event: Electron.IpcRendererEvent, ...args: any[]) => any export const handle = function (channel: string, handler: T) { ipcRendererInternal.on(channel, async (event, requestId, ...args) => { - const replyChannel = `${channel}_RESPONSE_${requestId}` + const replyChannel = `${channel}_RESPONSE_${requestId}`; try { - event.sender.send(replyChannel, null, await handler(event, ...args)) + event.sender.send(replyChannel, null, await handler(event, ...args)); } catch (error) { - event.sender.send(replyChannel, error) + event.sender.send(replyChannel, error); } - }) -} + }); +}; export function invokeSync (command: string, ...args: any[]): T { - const [ error, result ] = ipcRendererInternal.sendSync(command, ...args) + const [ error, result ] = ipcRendererInternal.sendSync(command, ...args); if (error) { - throw error + throw error; } else { - return result + return result; } } diff --git a/lib/renderer/ipc-renderer-internal.ts b/lib/renderer/ipc-renderer-internal.ts index 661f7059d6aa1..045e78c381d5c 100644 --- a/lib/renderer/ipc-renderer-internal.ts +++ b/lib/renderer/ipc-renderer-internal.ts @@ -1,30 +1,32 @@ -const { ipc } = process.electronBinding('ipc') -const v8Util = process.electronBinding('v8_util') +const { ipc } = process.electronBinding('ipc'); +const v8Util = process.electronBinding('v8_util'); // Created by init.js. -export const ipcRendererInternal = v8Util.getHiddenValue(global, 'ipc-internal') -const internal = true +export const ipcRendererInternal = v8Util.getHiddenValue(global, 'ipc-internal'); +const internal = true; -ipcRendererInternal.send = function (channel, ...args) { - return ipc.send(internal, channel, args) -} +if (!ipcRendererInternal.send) { + ipcRendererInternal.send = function (channel, ...args) { + return ipc.send(internal, channel, args); + }; -ipcRendererInternal.sendSync = function (channel, ...args) { - return ipc.sendSync(internal, channel, args)[0] -} + ipcRendererInternal.sendSync = function (channel, ...args) { + return ipc.sendSync(internal, channel, args)[0]; + }; -ipcRendererInternal.sendTo = function (webContentsId, channel, ...args) { - return ipc.sendTo(internal, false, webContentsId, channel, args) -} + ipcRendererInternal.sendTo = function (webContentsId, channel, ...args) { + return ipc.sendTo(internal, false, webContentsId, channel, args); + }; -ipcRendererInternal.sendToAll = function (webContentsId, channel, ...args) { - return ipc.sendTo(internal, true, webContentsId, channel, args) -} + ipcRendererInternal.sendToAll = function (webContentsId, channel, ...args) { + return ipc.sendTo(internal, true, webContentsId, channel, args); + }; -ipcRendererInternal.invoke = async function (channel: string, ...args: any[]) { - const { error, result } = await ipc.invoke(internal, channel, args) - if (error) { - throw new Error(`Error invoking remote method '${channel}': ${error}`) - } - return result + ipcRendererInternal.invoke = async function (channel: string, ...args: any[]) { + const { error, result } = await ipc.invoke(internal, channel, args); + if (error) { + throw new Error(`Error invoking remote method '${channel}': ${error}`); + } + return result; + }; } diff --git a/lib/renderer/remote/callbacks-registry.ts b/lib/renderer/remote/callbacks-registry.ts index f2f164fa6fa09..61d3d3a07ca99 100644 --- a/lib/renderer/remote/callbacks-registry.ts +++ b/lib/renderer/remote/callbacks-registry.ts @@ -1,4 +1,4 @@ -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); export class CallbacksRegistry { private nextId: number = 0 @@ -6,50 +6,50 @@ export class CallbacksRegistry { add (callback: Function) { // The callback is already added. - let id = v8Util.getHiddenValue(callback, 'callbackId') - if (id != null) return id + let id = v8Util.getHiddenValue(callback, 'callbackId'); + if (id != null) return id; - id = this.nextId += 1 + id = this.nextId += 1; // Capture the location of the function and put it in the ID string, // so that release errors can be tracked down easily. - const regexp = /at (.*)/gi - const stackString = (new Error()).stack - if (!stackString) return + const regexp = /at (.*)/gi; + const stackString = (new Error()).stack; + if (!stackString) return; - let filenameAndLine - let match + let filenameAndLine; + let match; while ((match = regexp.exec(stackString)) !== null) { - const location = match[1] - if (location.includes('(native)')) continue - if (location.includes('()')) continue - if (location.includes('electron/js2c')) continue - - const ref = /([^/^)]*)\)?$/gi.exec(location) - if (ref) filenameAndLine = ref![1] - break + const location = match[1]; + if (location.includes('(native)')) continue; + if (location.includes('()')) continue; + if (location.includes('electron/js2c')) continue; + + const ref = /([^/^)]*)\)?$/gi.exec(location); + if (ref) filenameAndLine = ref![1]; + break; } - this.callbacks.set(id, callback) - v8Util.setHiddenValue(callback, 'callbackId', id) - v8Util.setHiddenValue(callback, 'location', filenameAndLine) - return id + this.callbacks.set(id, callback); + v8Util.setHiddenValue(callback, 'callbackId', id); + v8Util.setHiddenValue(callback, 'location', filenameAndLine); + return id; } get (id: number) { - return this.callbacks.get(id) || function () {} + return this.callbacks.get(id) || function () {}; } apply (id: number, ...args: any[]) { - return this.get(id).apply(global, ...args) + return this.get(id).apply(global, ...args); } remove (id: number) { - const callback = this.callbacks.get(id) + const callback = this.callbacks.get(id); if (callback) { - v8Util.deleteHiddenValue(callback, 'callbackId') - this.callbacks.delete(id) + v8Util.deleteHiddenValue(callback, 'callbackId'); + this.callbacks.delete(id); } } } diff --git a/lib/renderer/security-warnings.ts b/lib/renderer/security-warnings.ts index b917f8af3b552..d7f0e3d14f0fc 100644 --- a/lib/renderer/security-warnings.ts +++ b/lib/renderer/security-warnings.ts @@ -1,9 +1,9 @@ -import { webFrame } from 'electron' -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { webFrame } from 'electron'; +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; -let shouldLog: boolean | null = null +let shouldLog: boolean | null = null; -const { platform, execPath, env } = process +const { platform, execPath, env } = process; /** * This method checks if a security message should be logged. @@ -15,37 +15,37 @@ const { platform, execPath, env } = process */ const shouldLogSecurityWarnings = function (): boolean { if (shouldLog !== null) { - return shouldLog + return shouldLog; } switch (platform) { case 'darwin': shouldLog = execPath.endsWith('MacOS/Electron') || - execPath.includes('Electron.app/Contents/Frameworks/') - break + execPath.includes('Electron.app/Contents/Frameworks/'); + break; case 'freebsd': case 'linux': - shouldLog = execPath.endsWith('/electron') - break + shouldLog = execPath.endsWith('/electron'); + break; case 'win32': - shouldLog = execPath.endsWith('\\electron.exe') - break + shouldLog = execPath.endsWith('\\electron.exe'); + break; default: - shouldLog = false + shouldLog = false; } if ((env && env.ELECTRON_DISABLE_SECURITY_WARNINGS) || (window && window.ELECTRON_DISABLE_SECURITY_WARNINGS)) { - shouldLog = false + shouldLog = false; } if ((env && env.ELECTRON_ENABLE_SECURITY_WARNINGS) || (window && window.ELECTRON_ENABLE_SECURITY_WARNINGS)) { - shouldLog = true + shouldLog = true; } - return shouldLog -} + return shouldLog; +}; /** * Checks if the current window is remote. @@ -54,9 +54,9 @@ const shouldLogSecurityWarnings = function (): boolean { */ const getIsRemoteProtocol = function () { if (window && window.location && window.location.protocol) { - return /^(http|ftp)s?/gi.test(window.location.protocol) + return /^(http|ftp)s?/gi.test(window.location.protocol); } -} +}; /** * Checks if the current window is from localhost. @@ -65,11 +65,11 @@ const getIsRemoteProtocol = function () { */ const isLocalhost = function () { if (!window || !window.location) { - return false + return false; } - return window.location.hostname === 'localhost' -} + return window.location.hostname === 'localhost'; +}; /** * Tries to determine whether a CSP without `unsafe-eval` is set. @@ -79,17 +79,17 @@ const isLocalhost = function () { const isUnsafeEvalEnabled = function () { return webFrame.executeJavaScript(`(${(() => { try { - new Function('') // eslint-disable-line no-new,no-new-func + new Function(''); // eslint-disable-line no-new,no-new-func } catch { - return false + return false; } - return true - }).toString()})()`, false) -} + return true; + }).toString()})()`, false); +}; const moreInformation = `\nFor more information and help, consult https://electronjs.org/docs/tutorial/security.\nThis warning will not show up -once the app is packaged.` +once the app is packaged.`; /** * #1 Only load secure content @@ -99,7 +99,7 @@ once the app is packaged.` */ const warnAboutInsecureResources = function () { if (!window || !window.performance || !window.performance.getEntriesByType) { - return + return; } const resources = window.performance @@ -107,20 +107,20 @@ const warnAboutInsecureResources = function () { .filter(({ name }) => /^(http|ftp):/gi.test(name || '')) .filter(({ name }) => new URL(name).hostname !== 'localhost') .map(({ name }) => `- ${name}`) - .join('\n') + .join('\n'); if (!resources || resources.length === 0) { - return + return; } const warning = `This renderer process loads resources using insecure protocols. This exposes users of this app to unnecessary security risks. Consider loading the following resources over HTTPS or FTPS. \n${resources} - \n${moreInformation}` + \n${moreInformation}`; console.warn('%cElectron Security Warning (Insecure Resources)', - 'font-weight: bold;', warning) -} + 'font-weight: bold;', warning); +}; /** * #2 on the checklist: Disable the Node.js integration in all renderers that @@ -129,17 +129,17 @@ const warnAboutInsecureResources = function () { * Logs a warning message about Node integration. */ const warnAboutNodeWithRemoteContent = function (nodeIntegration: boolean) { - if (!nodeIntegration || isLocalhost()) return + if (!nodeIntegration || isLocalhost()) return; if (getIsRemoteProtocol()) { const warning = `This renderer process has Node.js integration enabled and attempted to load remote content from '${window.location}'. This - exposes users of this app to severe security risks.\n${moreInformation}` + exposes users of this app to severe security risks.\n${moreInformation}`; console.warn('%cElectron Security Warning (Node.js Integration with Remote Content)', - 'font-weight: bold;', warning) + 'font-weight: bold;', warning); } -} +}; // Currently missing since it has ramifications and is still experimental: // #3 Enable context isolation in all renderers that display remote content @@ -153,14 +153,14 @@ const warnAboutNodeWithRemoteContent = function (nodeIntegration: boolean) { * Logs a warning message about disabled webSecurity. */ const warnAboutDisabledWebSecurity = function (webPreferences?: Electron.WebPreferences) { - if (!webPreferences || webPreferences.webSecurity !== false) return + if (!webPreferences || webPreferences.webSecurity !== false) return; const warning = `This renderer process has "webSecurity" disabled. This - exposes users of this app to severe security risks.\n${moreInformation}` + exposes users of this app to severe security risks.\n${moreInformation}`; console.warn('%cElectron Security Warning (Disabled webSecurity)', - 'font-weight: bold;', warning) -} + 'font-weight: bold;', warning); +}; /** * #6 on the checklist: Define a Content-Security-Policy and use restrictive @@ -170,16 +170,16 @@ const warnAboutDisabledWebSecurity = function (webPreferences?: Electron.WebPref */ const warnAboutInsecureCSP = function () { isUnsafeEvalEnabled().then((enabled) => { - if (!enabled) return + if (!enabled) return; const warning = `This renderer process has either no Content Security Policy set or a policy with "unsafe-eval" enabled. This exposes users of - this app to unnecessary security risks.\n${moreInformation}` + this app to unnecessary security risks.\n${moreInformation}`; console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)', - 'font-weight: bold;', warning) - }) -} + 'font-weight: bold;', warning); + }); +}; /** * #7 on the checklist: Do not set allowRunningInsecureContent to true @@ -187,15 +187,15 @@ const warnAboutInsecureCSP = function () { * Logs a warning message about disabled webSecurity. */ const warnAboutInsecureContentAllowed = function (webPreferences?: Electron.WebPreferences) { - if (!webPreferences || !webPreferences.allowRunningInsecureContent) return + if (!webPreferences || !webPreferences.allowRunningInsecureContent) return; const warning = `This renderer process has "allowRunningInsecureContent" enabled. This exposes users of this app to severe security risks.\n - ${moreInformation}` + ${moreInformation}`; console.warn('%cElectron Security Warning (allowRunningInsecureContent)', - 'font-weight: bold;', warning) -} + 'font-weight: bold;', warning); +}; /** * #8 on the checklist: Do not enable experimental features @@ -204,16 +204,16 @@ const warnAboutInsecureContentAllowed = function (webPreferences?: Electron.WebP */ const warnAboutExperimentalFeatures = function (webPreferences?: Electron.WebPreferences) { if (!webPreferences || (!webPreferences.experimentalFeatures)) { - return + return; } const warning = `This renderer process has "experimentalFeatures" enabled. This exposes users of this app to some security risk. If you do not need - this feature, you should disable it.\n${moreInformation}` + this feature, you should disable it.\n${moreInformation}`; console.warn('%cElectron Security Warning (experimentalFeatures)', - 'font-weight: bold;', warning) -} + 'font-weight: bold;', warning); +}; /** * #9 on the checklist: Do not use enableBlinkFeatures @@ -224,16 +224,16 @@ const warnAboutEnableBlinkFeatures = function (webPreferences?: Electron.WebPref if (!webPreferences || !webPreferences.hasOwnProperty('enableBlinkFeatures') || (webPreferences.enableBlinkFeatures && webPreferences.enableBlinkFeatures.length === 0)) { - return + return; } const warning = `This renderer process has additional "enableBlinkFeatures" enabled. This exposes users of this app to some security risk. If you do not - need this feature, you should disable it.\n${moreInformation}` + need this feature, you should disable it.\n${moreInformation}`; console.warn('%cElectron Security Warning (enableBlinkFeatures)', - 'font-weight: bold;', warning) -} + 'font-weight: bold;', warning); +}; /** * #10 on the checklist: Do Not Use allowpopups @@ -242,21 +242,21 @@ const warnAboutEnableBlinkFeatures = function (webPreferences?: Electron.WebPref */ const warnAboutAllowedPopups = function () { if (document && document.querySelectorAll) { - const domElements = document.querySelectorAll('[allowpopups]') + const domElements = document.querySelectorAll('[allowpopups]'); if (!domElements || domElements.length === 0) { - return + return; } const warning = `A has "allowpopups" set to true. This exposes users of this app to some security risk, since popups are just BrowserWindows. If you do not need this feature, you should disable it.\n - ${moreInformation}` + ${moreInformation}`; console.warn('%cElectron Security Warning (allowpopups)', - 'font-weight: bold;', warning) + 'font-weight: bold;', warning); } -} +}; // Currently missing since we can't easily programmatically check for it: // #11 Verify WebView Options Before Creation @@ -268,17 +268,17 @@ const warnAboutAllowedPopups = function () { // Logs a warning message about the remote module const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electron.WebPreferences) { - if (!webPreferences || !webPreferences.enableRemoteModule || isLocalhost()) return + if (!webPreferences || !webPreferences.enableRemoteModule || isLocalhost()) return; if (getIsRemoteProtocol()) { const warning = `This renderer process has "enableRemoteModule" enabled and attempted to load remote content from '${window.location}'. This - exposes users of this app to unnecessary security risks.\n${moreInformation}` + exposes users of this app to unnecessary security risks.\n${moreInformation}`; console.warn('%cElectron Security Warning (enableRemoteModule)', - 'font-weight: bold;', warning) + 'font-weight: bold;', warning); } -} +}; // Currently missing since we can't easily programmatically check for it: // #16 Filter the `remote` module @@ -286,31 +286,31 @@ const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electr const logSecurityWarnings = function ( webPreferences: Electron.WebPreferences | undefined, nodeIntegration: boolean ) { - warnAboutNodeWithRemoteContent(nodeIntegration) - warnAboutDisabledWebSecurity(webPreferences) - warnAboutInsecureResources() - warnAboutInsecureContentAllowed(webPreferences) - warnAboutExperimentalFeatures(webPreferences) - warnAboutEnableBlinkFeatures(webPreferences) - warnAboutInsecureCSP() - warnAboutAllowedPopups() - warnAboutRemoteModuleWithRemoteContent(webPreferences) -} + warnAboutNodeWithRemoteContent(nodeIntegration); + warnAboutDisabledWebSecurity(webPreferences); + warnAboutInsecureResources(); + warnAboutInsecureContentAllowed(webPreferences); + warnAboutExperimentalFeatures(webPreferences); + warnAboutEnableBlinkFeatures(webPreferences); + warnAboutInsecureCSP(); + warnAboutAllowedPopups(); + warnAboutRemoteModuleWithRemoteContent(webPreferences); +}; const getWebPreferences = async function () { try { - return ipcRendererInternal.invoke('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES') + return ipcRendererInternal.invoke('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES'); } catch (error) { - console.warn(`getLastWebPreferences() failed: ${error}`) + console.warn(`getLastWebPreferences() failed: ${error}`); } -} +}; export function securityWarnings (nodeIntegration: boolean) { const loadHandler = async function () { if (shouldLogSecurityWarnings()) { - const webPreferences = await getWebPreferences() - logSecurityWarnings(webPreferences, nodeIntegration) + const webPreferences = await getWebPreferences(); + logSecurityWarnings(webPreferences, nodeIntegration); } - } - window.addEventListener('load', loadHandler, { once: true }) + }; + window.addEventListener('load', loadHandler, { once: true }); } diff --git a/lib/renderer/web-frame-init.ts b/lib/renderer/web-frame-init.ts index 743dba3adb3cf..b4e8604b05b13 100644 --- a/lib/renderer/web-frame-init.ts +++ b/lib/renderer/web-frame-init.ts @@ -1,5 +1,5 @@ -import { webFrame, WebFrame } from 'electron' -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import { webFrame, WebFrame } from 'electron'; +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; // All keys of WebFrame that extend Function type WebFrameMethod = { @@ -15,6 +15,6 @@ export const webFrameInit = () => { // The TypeScript compiler cannot handle the sheer number of // call signatures here and simply gives up. Incorrect invocations // will be caught by "keyof WebFrameMethod" though. - return (webFrame[method] as any)(...args) - }) -} + return (webFrame[method] as any)(...args); + }); +}; diff --git a/lib/renderer/web-view/guest-view-internal.ts b/lib/renderer/web-view/guest-view-internal.ts index 852826da1582c..3ca3ae3f5f599 100644 --- a/lib/renderer/web-view/guest-view-internal.ts +++ b/lib/renderer/web-view/guest-view-internal.ts @@ -1,8 +1,8 @@ -import { webFrame, IpcMessageEvent } from 'electron' -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import { webFrame, IpcMessageEvent } from 'electron'; +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; -import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl' +import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl'; const WEB_VIEW_EVENTS: Record> = { 'load-commit': ['url', 'isMainFrame'], @@ -38,81 +38,86 @@ const WEB_VIEW_EVENTS: Record> = { 'found-in-page': ['result'], 'did-change-theme-color': ['themeColor'], 'update-target-url': ['url'] -} +}; const DEPRECATED_EVENTS: Record = { 'page-title-updated': 'page-title-set' -} +}; const dispatchEvent = function ( webView: WebViewImpl, eventName: string, eventKey: string, ...args: Array ) { if (DEPRECATED_EVENTS[eventName] != null) { - dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args) + dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args); } - const domEvent = new Event(eventName) as ElectronInternal.WebViewEvent + const domEvent = new Event(eventName) as ElectronInternal.WebViewEvent; WEB_VIEW_EVENTS[eventKey].forEach((prop, index) => { - (domEvent as any)[prop] = args[index] - }) + (domEvent as any)[prop] = args[index]; + }); - webView.dispatchEvent(domEvent) + webView.dispatchEvent(domEvent); if (eventName === 'load-commit') { - webView.onLoadCommit(domEvent) + webView.onLoadCommit(domEvent); } else if (eventName === 'focus-change') { - webView.onFocusChange() + webView.onFocusChange(); } -} +}; export function registerEvents (webView: WebViewImpl, viewInstanceId: number) { ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`, function () { - webView.guestInstanceId = undefined - webView.reset() - const domEvent = new Event('destroyed') - webView.dispatchEvent(domEvent) - }) + webView.guestInstanceId = undefined; + webView.reset(); + const domEvent = new Event('destroyed'); + webView.dispatchEvent(domEvent); + }); ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`, function (event, eventName, ...args) { - dispatchEvent(webView, eventName, eventName, ...args) - }) + dispatchEvent(webView, eventName, eventName, ...args); + }); ipcRendererInternal.on(`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 + const domEvent = new Event('ipc-message') as IpcMessageEvent; + domEvent.channel = channel; + domEvent.args = args; - webView.dispatchEvent(domEvent) - }) + webView.dispatchEvent(domEvent); + }); } export function deregisterEvents (viewInstanceId: number) { - ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`) - ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`) - ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`) + ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`); + ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`); + ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`); } export function createGuest (params: Record): Promise { - return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params) + return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params); } export function createGuestSync (params: Record): number { - return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params) + return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params); } export function attachGuest ( elementInstanceId: number, guestInstanceId: number, params: Record, contentWindow: Window ) { - const embedderFrameId = (webFrame as ElectronInternal.WebFrameInternal).getWebFrameId(contentWindow) + const embedderFrameId = (webFrame as ElectronInternal.WebFrameInternal).getWebFrameId(contentWindow); if (embedderFrameId < 0) { // this error should not happen. - throw new Error('Invalid embedder frame') + throw new Error('Invalid embedder frame'); } - ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params) + ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params); +} + +export function detachGuest (guestInstanceId: number) { + return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_DETACH_GUEST', guestInstanceId); } export const guestViewInternalModule = { deregisterEvents, createGuest, createGuestSync, - attachGuest -} + attachGuest, + detachGuest +}; diff --git a/lib/renderer/web-view/web-view-attributes.ts b/lib/renderer/web-view/web-view-attributes.ts index 4a6844acea05d..4ce18e9a9d296 100644 --- a/lib/renderer/web-view/web-view-attributes.ts +++ b/lib/renderer/web-view/web-view-attributes.ts @@ -1,15 +1,15 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl' -import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl'; +import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants'; // Helper function to resolve url set in attribute. -const a = document.createElement('a') +const a = document.createElement('a'); const resolveURL = function (url?: string | null) { - if (!url) return '' - a.href = url - return a.href -} + if (!url) return ''; + a.href = url; + return a.href; +}; interface MutationHandler { handleMutation (_oldValue: any, _newValue: any): any; @@ -22,40 +22,40 @@ class WebViewAttribute implements MutationHandler { public ignoreMutation = false; constructor (public name: string, public webViewImpl: WebViewImpl) { - this.name = name - this.value = (webViewImpl.webviewNode as Record)[name] || '' - this.webViewImpl = webViewImpl - this.defineProperty() + this.name = name; + this.value = (webViewImpl.webviewNode as Record)[name] || ''; + this.webViewImpl = webViewImpl; + this.defineProperty(); } // Retrieves and returns the attribute's value. public getValue () { - return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value + return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value; } // Sets the attribute's value. public setValue (value: any) { - this.webViewImpl.webviewNode.setAttribute(this.name, value || '') + this.webViewImpl.webviewNode.setAttribute(this.name, value || ''); } // Changes the attribute's value without triggering its mutation handler. public setValueIgnoreMutation (value: any) { - this.ignoreMutation = true - this.setValue(value) - this.ignoreMutation = false + this.ignoreMutation = true; + this.setValue(value); + this.ignoreMutation = false; } // Defines this attribute as a property on the webview node. public defineProperty () { return Object.defineProperty(this.webViewImpl.webviewNode, this.name, { get: () => { - return this.getValue() + return this.getValue(); }, set: (value) => { - return this.setValue(value) + return this.setValue(value); }, enumerable: true - }) + }); } // Called when the attribute's value changes. @@ -65,14 +65,14 @@ class WebViewAttribute implements MutationHandler { // An attribute that is treated as a Boolean. class BooleanAttribute extends WebViewAttribute { getValue () { - return this.webViewImpl.webviewNode.hasAttribute(this.name) + return this.webViewImpl.webviewNode.hasAttribute(this.name); } setValue (value: boolean) { if (value) { - this.webViewImpl.webviewNode.setAttribute(this.name, '') + this.webViewImpl.webviewNode.setAttribute(this.name, ''); } else { - this.webViewImpl.webviewNode.removeAttribute(this.name) + this.webViewImpl.webviewNode.removeAttribute(this.name); } } } @@ -82,21 +82,21 @@ class PartitionAttribute extends WebViewAttribute { public validPartitionId = true constructor (public webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, webViewImpl); } public handleMutation = (oldValue: any, newValue: any) => { - newValue = newValue || '' + newValue = newValue || ''; // The partition cannot change if the webview has already navigated. if (!this.webViewImpl.beforeFirstNavigation) { - console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_ALREADY_NAVIGATED) - this.setValueIgnoreMutation(oldValue) - return + console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_ALREADY_NAVIGATED); + this.setValueIgnoreMutation(oldValue); + return; } if (newValue === 'persist:') { - this.validPartitionId = false - console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE) + this.validPartitionId = false; + console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE); } } } @@ -106,26 +106,26 @@ class SrcAttribute extends WebViewAttribute { public observer!: MutationObserver; constructor (public webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC, webViewImpl) - this.setupMutationObserver() + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC, webViewImpl); + this.setupMutationObserver(); } public getValue () { if (this.webViewImpl.webviewNode.hasAttribute(this.name)) { - return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)) + return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)); } else { - return this.value + return this.value; } } public setValueIgnoreMutation (value: any) { - super.setValueIgnoreMutation(value) + super.setValueIgnoreMutation(value); // takeRecords() is needed to clear queued up src mutations. Without it, it // is possible for this change to get picked up asyncronously by src's // mutation observer |observer|, and then get handled even though we do not // want to handle this mutation. - this.observer.takeRecords() + this.observer.takeRecords(); } public handleMutation = (oldValue: any, newValue: any) => { @@ -136,10 +136,10 @@ class SrcAttribute extends WebViewAttribute { // src attribute changes normally initiate a navigation. We suppress // the next src attribute handler call to avoid reloading the page // on every guest-initiated navigation. - this.setValueIgnoreMutation(oldValue) - return + this.setValueIgnoreMutation(oldValue); + return; } - this.parse() + this.parse(); } // The purpose of this mutation observer is to catch assignment to the src @@ -149,144 +149,144 @@ class SrcAttribute extends WebViewAttribute { public setupMutationObserver () { this.observer = new MutationObserver((mutations) => { for (const mutation of mutations) { - const { oldValue } = mutation - const newValue = this.getValue() + const { oldValue } = mutation; + const newValue = this.getValue(); if (oldValue !== newValue) { - return + return; } - this.handleMutation(oldValue, newValue) + this.handleMutation(oldValue, newValue); } - }) + }); const params = { attributes: true, attributeOldValue: true, attributeFilter: [this.name] - } + }; - this.observer.observe(this.webViewImpl.webviewNode, params) + this.observer.observe(this.webViewImpl.webviewNode, params); } public parse () { if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) { - return + return; } if (this.webViewImpl.guestInstanceId == null) { if (this.webViewImpl.beforeFirstNavigation) { - this.webViewImpl.beforeFirstNavigation = false - this.webViewImpl.createGuest() + this.webViewImpl.beforeFirstNavigation = false; + this.webViewImpl.createGuest(); } - return + return; } // Navigate to |this.src|. - const opts: Record = {} + const opts: Record = {}; - const httpreferrer = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER].getValue() + const httpreferrer = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER].getValue(); if (httpreferrer) { - opts.httpReferrer = httpreferrer + opts.httpReferrer = httpreferrer; } - const useragent = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT].getValue() + const useragent = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT].getValue(); if (useragent) { - opts.userAgent = useragent + opts.userAgent = useragent; } - const guestInstanceId = this.webViewImpl.guestInstanceId - const method = 'loadURL' - const args = [this.getValue(), opts] + const guestInstanceId = this.webViewImpl.guestInstanceId; + const method = 'loadURL'; + const args = [this.getValue(), opts]; - ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', guestInstanceId, method, args) + ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', guestInstanceId, method, args); } } // Attribute specifies HTTP referrer. class HttpReferrerAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER, webViewImpl); } } // Attribute specifies user agent class UserAgentAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT, webViewImpl); } } // Attribute that set preload script. class PreloadAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD, webViewImpl); } public getValue () { if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) { - return this.value + return this.value; } - let preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)) - const protocol = preload.substr(0, 5) + let preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name)); + const protocol = preload.substr(0, 5); if (protocol !== 'file:') { - console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE) - preload = '' + console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE); + preload = ''; } - return preload + return preload; } } // Attribute that specifies the blink features to be enabled. class BlinkFeaturesAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, webViewImpl); } } // Attribute that specifies the blink features to be disabled. class DisableBlinkFeaturesAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl); } } // Attribute that specifies the web preferences to be enabled. class WebPreferencesAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES, webViewImpl); } } class EnableRemoteModuleAttribute extends WebViewAttribute { constructor (webViewImpl: WebViewImpl) { - super(WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl) + super(WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl); } public getValue () { - return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false' + return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false'; } public setValue (value: any) { - this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false') + this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false'); } } // Sets up all of the webview attributes. WebViewImpl.prototype.setupWebViewAttributes = function () { - this.attributes = {} - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION] = new PartitionAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC] = new SrcAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION, this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES, this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE] = new EnableRemoteModuleAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this) - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES] = new WebPreferencesAttribute(this) -} + this.attributes = {}; + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION] = new PartitionAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC] = new SrcAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION, this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES, this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE] = new EnableRemoteModuleAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this); + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES] = new WebPreferencesAttribute(this); +}; diff --git a/lib/renderer/web-view/web-view-element.ts b/lib/renderer/web-view/web-view-element.ts index cdb81308e22d2..dcf3dbc753400 100644 --- a/lib/renderer/web-view/web-view-element.ts +++ b/lib/renderer/web-view/web-view-element.ts @@ -8,12 +8,12 @@ // which runs in browserify environment instead of Node environment, all native // modules must be passed from outside, all included files must be plain JS. -import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants' -import { WebViewImpl as IWebViewImpl, webViewImplModule } from '@electron/internal/renderer/web-view/web-view-impl' +import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants'; +import { WebViewImpl as IWebViewImpl, webViewImplModule } from '@electron/internal/renderer/web-view/web-view-impl'; // Return a WebViewElement class that is defined in this context. const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => { - const { guestViewInternal, WebViewImpl } = webViewImpl + const { guestViewInternal, WebViewImpl } = webViewImpl; return class WebViewElement extends HTMLElement { public internalInstanceId?: number; @@ -33,45 +33,48 @@ const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES - ] + ]; } constructor () { - super() - v8Util.setHiddenValue(this, 'internal', new WebViewImpl(this)) + super(); + v8Util.setHiddenValue(this, 'internal', new WebViewImpl(this)); } connectedCallback () { - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal'); if (!internal) { - return + return; } if (!internal.elementAttached) { - guestViewInternal.registerEvents(internal, internal.viewInstanceId) - internal.elementAttached = true - internal.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].parse() + guestViewInternal.registerEvents(internal, internal.viewInstanceId); + internal.elementAttached = true; + internal.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].parse(); } } attributeChangedCallback (name: string, oldValue: any, newValue: any) { - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal'); if (internal) { - internal.handleWebviewAttributeMutation(name, oldValue, newValue) + internal.handleWebviewAttributeMutation(name, oldValue, newValue); } } disconnectedCallback () { - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal'); if (!internal) { - return + return; } - guestViewInternal.deregisterEvents(internal.viewInstanceId) - internal.elementAttached = false - this.internalInstanceId = 0 - internal.reset() + guestViewInternal.deregisterEvents(internal.viewInstanceId); + if (internal.guestInstanceId) { + guestViewInternal.detachGuest(internal.guestInstanceId); + } + internal.elementAttached = false; + this.internalInstanceId = 0; + internal.reset(); } - } -} + }; +}; // Register custom element. const registerWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => { @@ -79,40 +82,40 @@ const registerWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeo // eslint-disable-next-line const WebViewElement = defineWebViewElement(v8Util, webViewImpl) as unknown as typeof ElectronInternal.WebViewElement - webViewImpl.setupMethods(WebViewElement) + webViewImpl.setupMethods(WebViewElement); // The customElements.define has to be called in a special scope. - const webFrame = webViewImpl.webFrame as ElectronInternal.WebFrameInternal + const webFrame = webViewImpl.webFrame as ElectronInternal.WebFrameInternal; webFrame.allowGuestViewElementDefinition(window, () => { window.customElements.define('webview', WebViewElement); - (window as any).WebView = WebViewElement + (window as any).WebView = WebViewElement; // Delete the callbacks so developers cannot call them and produce unexpected // behavior. - delete WebViewElement.prototype.connectedCallback - delete WebViewElement.prototype.disconnectedCallback - delete WebViewElement.prototype.attributeChangedCallback + delete WebViewElement.prototype.connectedCallback; + delete WebViewElement.prototype.disconnectedCallback; + delete WebViewElement.prototype.attributeChangedCallback; // Now that |observedAttributes| has been retrieved, we can hide it from // user code as well. // TypeScript is concerned that we're deleting a read-only attribute - delete (WebViewElement as any).observedAttributes - }) -} + delete (WebViewElement as any).observedAttributes; + }); +}; // Prepare to register the element. export const setupWebView = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => { - const useCapture = true + const useCapture = true; const listener = (event: Event) => { if (document.readyState === 'loading') { - return + return; } - webViewImpl.setupAttributes() - registerWebViewElement(v8Util, webViewImpl) + webViewImpl.setupAttributes(); + registerWebViewElement(v8Util, webViewImpl); - window.removeEventListener(event.type, listener, useCapture) - } + window.removeEventListener(event.type, listener, useCapture); + }; - window.addEventListener('readystatechange', listener, useCapture) -} + window.addEventListener('readystatechange', listener, useCapture); +}; diff --git a/lib/renderer/web-view/web-view-impl.ts b/lib/renderer/web-view/web-view-impl.ts index 2ec4db66cc75e..04d7b90eda522 100644 --- a/lib/renderer/web-view/web-view-impl.ts +++ b/lib/renderer/web-view/web-view-impl.ts @@ -1,20 +1,21 @@ -import * as electron from 'electron' +import * as electron from 'electron'; -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' -import * as guestViewInternal from '@electron/internal/renderer/web-view/guest-view-internal' -import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants' -import { syncMethods, asyncMethods } from '@electron/internal/common/web-view-methods' -const { webFrame } = electron +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; +import * as guestViewInternal from '@electron/internal/renderer/web-view/guest-view-internal'; +import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants'; +import { syncMethods, asyncMethods, properties } from '@electron/internal/common/web-view-methods'; +import { deserialize } from '@electron/internal/common/type-utils'; +const { webFrame } = electron; -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); // ID generator. -let nextId = 0 +let nextId = 0; const getNextId = function () { - return ++nextId -} + return ++nextId; +}; // Represents the internal state of the WebView node. export class WebViewImpl { @@ -37,29 +38,29 @@ export class WebViewImpl { constructor (public webviewNode: HTMLElement) { // Create internal iframe element. - this.internalElement = this.createInternalElement() - const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' }) - shadowRoot.innerHTML = '' - this.setupWebViewAttributes() - this.viewInstanceId = getNextId() - shadowRoot.appendChild(this.internalElement) + this.internalElement = this.createInternalElement(); + const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' }); + shadowRoot.innerHTML = ''; + this.setupWebViewAttributes(); + this.viewInstanceId = getNextId(); + shadowRoot.appendChild(this.internalElement); // Provide access to contentWindow. Object.defineProperty(this.webviewNode, 'contentWindow', { get: () => { - return this.internalElement.contentWindow + return this.internalElement.contentWindow; }, enumerable: true - }) + }); } createInternalElement () { - const iframeElement = document.createElement('iframe') - iframeElement.style.flex = '1 1 auto' - iframeElement.style.width = '100%' - iframeElement.style.border = '0' - v8Util.setHiddenValue(iframeElement, 'internal', this) - return iframeElement + const iframeElement = document.createElement('iframe'); + iframeElement.style.flex = '1 1 auto'; + iframeElement.style.width = '100%'; + iframeElement.style.border = '0'; + v8Util.setHiddenValue(iframeElement, 'internal', this); + return iframeElement; } // Resets some state upon reattaching element to the DOM. @@ -71,20 +72,20 @@ export class WebViewImpl { // heard back from createGuest yet. We will not reset the flag in this case so // that we don't end up allocating a second guest. if (this.guestInstanceId) { - this.guestInstanceId = void 0 + this.guestInstanceId = void 0; } - this.beforeFirstNavigation = true - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].validPartitionId = true + this.beforeFirstNavigation = true; + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].validPartitionId = true; // Since attachment swaps a local frame for a remote frame, we need our // internal iframe element to be local again before we can reattach. - const newFrame = this.createInternalElement() - const oldFrame = this.internalElement - this.internalElement = newFrame + const newFrame = this.createInternalElement(); + const oldFrame = this.internalElement; + this.internalElement = newFrame; if (oldFrame && oldFrame.parentNode) { - oldFrame.parentNode.replaceChild(newFrame, oldFrame) + oldFrame.parentNode.replaceChild(newFrame, oldFrame); } } @@ -95,175 +96,204 @@ export class WebViewImpl { // details. handleWebviewAttributeMutation (attributeName: string, oldValue: any, newValue: any) { if (!this.attributes[attributeName] || this.attributes[attributeName].ignoreMutation) { - return + return; } // Let the changed attribute handle its own mutation - this.attributes[attributeName].handleMutation(oldValue, newValue) + this.attributes[attributeName].handleMutation(oldValue, newValue); } onElementResize () { - const resizeEvent = new Event('resize') as ElectronInternal.WebFrameResizeEvent - resizeEvent.newWidth = this.webviewNode.clientWidth - resizeEvent.newHeight = this.webviewNode.clientHeight - this.dispatchEvent(resizeEvent) + const resizeEvent = new Event('resize') as ElectronInternal.WebFrameResizeEvent; + resizeEvent.newWidth = this.webviewNode.clientWidth; + resizeEvent.newHeight = this.webviewNode.clientHeight; + this.dispatchEvent(resizeEvent); } createGuest () { guestViewInternal.createGuest(this.buildParams()).then(guestInstanceId => { - this.attachGuestInstance(guestInstanceId) - }) + this.attachGuestInstance(guestInstanceId); + }); } createGuestSync () { - this.beforeFirstNavigation = false - this.attachGuestInstance(guestViewInternal.createGuestSync(this.buildParams())) + this.beforeFirstNavigation = false; + this.attachGuestInstance(guestViewInternal.createGuestSync(this.buildParams())); } dispatchEvent (webViewEvent: Electron.Event) { - this.webviewNode.dispatchEvent(webViewEvent) + this.webviewNode.dispatchEvent(webViewEvent); } // Adds an 'on' property on the webview, which can be used to set/unset // an event handler. setupEventProperty (eventName: string) { - const propertyName = `on${eventName.toLowerCase()}` + const propertyName = `on${eventName.toLowerCase()}`; return Object.defineProperty(this.webviewNode, propertyName, { get: () => { - return this.on[propertyName] + return this.on[propertyName]; }, set: (value) => { if (this.on[propertyName]) { - this.webviewNode.removeEventListener(eventName, this.on[propertyName]) + this.webviewNode.removeEventListener(eventName, this.on[propertyName]); } - this.on[propertyName] = value + this.on[propertyName] = value; if (value) { - return this.webviewNode.addEventListener(eventName, value) + return this.webviewNode.addEventListener(eventName, value); } }, enumerable: true - }) + }); } // Updates state upon loadcommit. onLoadCommit (webViewEvent: ElectronInternal.WebViewEvent) { - const oldValue = this.webviewNode.getAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC) - const newValue = webViewEvent.url + const oldValue = this.webviewNode.getAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC); + const newValue = webViewEvent.url; if (webViewEvent.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. - this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue) + this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue); } } // Emits focus/blur events. onFocusChange () { - const hasFocus = document.activeElement === this.webviewNode + const hasFocus = document.activeElement === this.webviewNode; if (hasFocus !== this.hasFocus) { - this.hasFocus = hasFocus - this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur')) + this.hasFocus = hasFocus; + this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur')); } } onAttach (storagePartitionId: number) { - return this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].setValue(storagePartitionId) + return this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].setValue(storagePartitionId); } buildParams () { const params: Record = { instanceId: this.viewInstanceId, userAgentOverride: this.userAgentOverride - } + }; for (const attributeName in this.attributes) { if (this.attributes.hasOwnProperty(attributeName)) { - params[attributeName] = this.attributes[attributeName].getValue() + params[attributeName] = this.attributes[attributeName].getValue(); } } - return params + return params; } attachGuestInstance (guestInstanceId: number) { if (!this.elementAttached) { // The element could be detached before we got response from browser. - return + return; } - this.internalInstanceId = getNextId() - this.guestInstanceId = guestInstanceId + this.internalInstanceId = getNextId(); + this.guestInstanceId = guestInstanceId; guestViewInternal.attachGuest( this.internalInstanceId, this.guestInstanceId, this.buildParams(), this.internalElement.contentWindow! - ) + ); // ResizeObserver is a browser global not recognized by "standard". /* globals ResizeObserver */ // TODO(zcbenz): Should we deprecate the "resize" event? Wait, it is not // even documented. - this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this)) - this.resizeObserver.observe(this.internalElement) + this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this)); + this.resizeObserver.observe(this.internalElement); } } export const setupAttributes = () => { - require('@electron/internal/renderer/web-view/web-view-attributes') -} + require('@electron/internal/renderer/web-view/web-view-attributes'); +}; // I wish eslint wasn't so stupid, but it is // eslint-disable-next-line export const setupMethods = (WebViewElement: typeof ElectronInternal.WebViewElement) => { WebViewElement.prototype.getWebContentsId = function () { - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal'); if (!internal.guestInstanceId) { - throw new Error('The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.') + throw new Error('The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.'); } - return internal.guestInstanceId - } + return internal.guestInstanceId; + }; // WebContents associated with this webview. WebViewElement.prototype.getWebContents = function () { - const remote = electron.remote as Electron.RemoteInternal + const remote = electron.remote as Electron.RemoteInternal; if (!remote) { - throw new Error('getGuestWebContents requires remote, which is not enabled') + throw new Error('getGuestWebContents requires remote, which is not enabled'); } - const internal = v8Util.getHiddenValue(this, 'internal') + const internal = v8Util.getHiddenValue(this, 'internal'); if (!internal.guestInstanceId) { - internal.createGuestSync() + internal.createGuestSync(); } - return remote.getGuestWebContents(internal.guestInstanceId!) - } + return remote.getGuestWebContents(internal.guestInstanceId!); + }; + + WebViewElement.prototype.getWebContents = electron.deprecate.moveAPI( + WebViewElement.prototype.getWebContents, + 'webview.getWebContents()', + 'remote.webContents.fromId(webview.getWebContentsId())' + ) as any; // Focusing the webview should move page focus to the underlying iframe. WebViewElement.prototype.focus = function () { - this.contentWindow.focus() - } + this.contentWindow.focus(); + }; // Forward proto.foo* method calls to WebViewImpl.foo*. const createBlockHandler = function (method: string) { return function (this: ElectronInternal.WebViewElement, ...args: Array) { - return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args) - } - } + return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args); + }; + }; for (const method of syncMethods) { - (WebViewElement.prototype as Record)[method] = createBlockHandler(method) + (WebViewElement.prototype as Record)[method] = createBlockHandler(method); } const createNonBlockHandler = function (method: string) { return function (this: ElectronInternal.WebViewElement, ...args: Array) { - return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args) - } - } + return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args); + }; + }; for (const method of asyncMethods) { - (WebViewElement.prototype as Record)[method] = createNonBlockHandler(method) + (WebViewElement.prototype as Record)[method] = createNonBlockHandler(method); } -} + + WebViewElement.prototype.capturePage = async function (...args) { + return deserialize(await ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', this.getWebContentsId(), args)); + }; + + const createPropertyGetter = function (property: string) { + return function (this: ElectronInternal.WebViewElement) { + return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_GET', this.getWebContentsId(), property); + }; + }; + + const createPropertySetter = function (property: string) { + return function (this: ElectronInternal.WebViewElement, arg: any) { + return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_SET', this.getWebContentsId(), property, arg); + }; + }; + + for (const property of properties) { + Object.defineProperty(WebViewElement.prototype, property, { + get: createPropertyGetter(property) as any, + set: createPropertySetter(property) + }); + } +}; export const webViewImplModule = { setupAttributes, @@ -271,4 +301,4 @@ export const webViewImplModule = { guestViewInternal, webFrame, WebViewImpl -} +}; diff --git a/lib/renderer/web-view/web-view-init.ts b/lib/renderer/web-view/web-view-init.ts index 621ba569845df..95d334bafd724 100644 --- a/lib/renderer/web-view/web-view-init.ts +++ b/lib/renderer/web-view/web-view-init.ts @@ -1,18 +1,18 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); function handleFocusBlur (guestInstanceId: number) { // Note that while Chromium content APIs have observer for focus/blur, they // unfortunately do not work for webview. window.addEventListener('focus', () => { - ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', true, guestInstanceId) - }) + ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', true, guestInstanceId); + }); window.addEventListener('blur', () => { - ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', false, guestInstanceId) - }) + ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', false, guestInstanceId); + }); } export function webViewInit ( @@ -20,17 +20,17 @@ export function webViewInit ( ) { // Don't allow recursive ``. if (webviewTag && guestInstanceId == null) { - const { webViewImplModule } = require('@electron/internal/renderer/web-view/web-view-impl') + const { webViewImplModule } = require('@electron/internal/renderer/web-view/web-view-impl'); if (contextIsolation) { - v8Util.setHiddenValue(window, 'web-view-impl', webViewImplModule) + v8Util.setHiddenValue(window, 'web-view-impl', webViewImplModule); } else { - const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element') - setupWebView(v8Util, webViewImplModule) + const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element'); + setupWebView(v8Util, webViewImplModule); } } if (guestInstanceId) { // Report focus/blur events of webview to browser. - handleFocusBlur(guestInstanceId) + handleFocusBlur(guestInstanceId); } } diff --git a/lib/renderer/webpack-provider.ts b/lib/renderer/webpack-provider.ts index 6ed060145e077..829320cd756e4 100644 --- a/lib/renderer/webpack-provider.ts +++ b/lib/renderer/webpack-provider.ts @@ -7,12 +7,12 @@ // Rip global off of window (which is also global) so that webpack doesn't // auto replace it with a looped reference to this file -const _global = (self as any || window as any).global as NodeJS.Global -const process = _global.process -const Buffer = _global.Buffer +const _global = (self as any || window as any).global as NodeJS.Global; +const process = _global.process; +const Buffer = _global.Buffer; export { _global, process, Buffer -} +}; diff --git a/lib/renderer/window-setup.ts b/lib/renderer/window-setup.ts index 500168e3cfc3d..cfd7f491aa8e7 100644 --- a/lib/renderer/window-setup.ts +++ b/lib/renderer/window-setup.ts @@ -1,11 +1,11 @@ -import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal' -import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils' +import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'; +import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'; +import { internalContextBridge } from '@electron/internal/renderer/api/context-bridge'; -// This file implements the following APIs: -// - window.history.back() -// - window.history.forward() -// - window.history.go() -// - window.history.length +const { contextIsolationEnabled, isInIsolatedWorld } = internalContextBridge; +const shouldUseContextBridge = contextIsolationEnabled && isInIsolatedWorld(); + +// This file implements the following APIs over the ctx bridge: // - window.open() // - window.opener.blur() // - window.opener.close() @@ -13,35 +13,40 @@ import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-inte // - window.opener.focus() // - window.opener.location // - window.opener.print() +// - window.opener.closed // - window.opener.postMessage() +// - window.history.back() +// - window.history.forward() +// - window.history.go() +// - window.history.length // - window.prompt() // - document.hidden // - document.visibilityState // Helper function to resolve relative url. -const resolveURL = (url: string, base: string) => new URL(url, base).href +const resolveURL = (url: string, base: string) => new URL(url, base).href; // Use this method to ensure values expected as strings in the main process // are convertible to strings in the renderer process. This ensures exceptions // converting values to strings are thrown in this process. const toString = (value: any) => { - return value != null ? `${value}` : value -} + return value != null ? `${value}` : value; +}; -const windowProxies = new Map() +const windowProxies = new Map(); -const getOrCreateProxy = (guestId: number) => { - let proxy = windowProxies.get(guestId) +const getOrCreateProxy = (guestId: number): SafelyBoundBrowserWindowProxy => { + let proxy = windowProxies.get(guestId); if (proxy == null) { - proxy = new BrowserWindowProxy(guestId) - windowProxies.set(guestId, proxy) + proxy = new BrowserWindowProxy(guestId); + windowProxies.set(guestId, proxy); } - return proxy -} + return proxy.getSafe(); +}; const removeProxy = (guestId: number) => { - windowProxies.delete(guestId) -} + windowProxies.delete(guestId); +}; type LocationProperties = 'hash' | 'href' | 'host' | 'hostname' | 'origin' | 'pathname' | 'port' | 'protocol' | 'search' @@ -64,56 +69,93 @@ class LocationProxy { */ private static ProxyProperty (target: LocationProxy, propertyKey: LocationProperties) { Object.defineProperty(target, propertyKey, { + enumerable: true, + configurable: true, get: function (this: LocationProxy): T | string { - const guestURL = this.getGuestURL() - const value = guestURL ? guestURL[propertyKey] : '' - return value === undefined ? '' : value + const guestURL = this.getGuestURL(); + const value = guestURL ? guestURL[propertyKey] : ''; + return value === undefined ? '' : value; }, set: function (this: LocationProxy, newVal: T) { - const guestURL = this.getGuestURL() + const guestURL = this.getGuestURL(); if (guestURL) { // TypeScript doesn't want us to assign to read-only variables. // It's right, that's bad, but we're doing it anway. - (guestURL as any)[propertyKey] = newVal + (guestURL as any)[propertyKey] = newVal; - return this._invokeWebContentsMethod('loadURL', guestURL.toString()) + return this._invokeWebContentsMethod('loadURL', guestURL.toString()); } } - }) + }); + } + + public getSafe = () => { + const that = this; + return { + get href () { return that.href; }, + set href (newValue) { that.href = newValue; }, + get hash () { return that.hash; }, + set hash (newValue) { that.hash = newValue; }, + get host () { return that.host; }, + set host (newValue) { that.host = newValue; }, + get hostname () { return that.hostname; }, + set hostname (newValue) { that.hostname = newValue; }, + get origin () { return that.origin; }, + set origin (newValue) { that.origin = newValue; }, + get pathname () { return that.pathname; }, + set pathname (newValue) { that.pathname = newValue; }, + get port () { return that.port; }, + set port (newValue) { that.port = newValue; }, + get protocol () { return that.protocol; }, + set protocol (newValue) { that.protocol = newValue; }, + get search () { return that.search; }, + set search (newValue) { that.search = newValue; } + }; } constructor (guestId: number) { // eslint will consider the constructor "useless" // unless we assign them in the body. It's fine, that's what // TS would do anyway. - this.guestId = guestId - this.getGuestURL = this.getGuestURL.bind(this) + this.guestId = guestId; + this.getGuestURL = this.getGuestURL.bind(this); } public toString (): string { - return this.href + return this.href; } private getGuestURL (): URL | null { - const urlString = this._invokeWebContentsMethodSync('getURL') as string + const urlString = this._invokeWebContentsMethodSync('getURL') as string; try { - return new URL(urlString) + return new URL(urlString); } catch (e) { - console.error('LocationProxy: failed to parse string', urlString, e) + console.error('LocationProxy: failed to parse string', urlString, e); } - return null + return null; } private _invokeWebContentsMethod (method: string, ...args: any[]) { - return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args) + return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args); } private _invokeWebContentsMethodSync (method: string, ...args: any[]) { - return ipcRendererUtils.invokeSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args) + return ipcRendererUtils.invokeSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args); } } +interface SafelyBoundBrowserWindowProxy { + location: WindowProxy['location']; + blur: WindowProxy['blur']; + close: WindowProxy['close']; + eval: typeof eval; // eslint-disable-line no-eval + focus: WindowProxy['focus']; + print: WindowProxy['print']; + postMessage: WindowProxy['postMessage']; + closed: boolean; +} + class BrowserWindowProxy { public closed: boolean = false @@ -124,127 +166,162 @@ class BrowserWindowProxy { // so for now, we'll have to make do with an "any" in the mix. // https://github.com/Microsoft/TypeScript/issues/2521 public get location (): LocationProxy | any { - return this._location + return this._location.getSafe(); } public set location (url: string | any) { - url = resolveURL(url, this.location.href) - this._invokeWebContentsMethod('loadURL', url) + url = resolveURL(url, this.location.href); + this._invokeWebContentsMethod('loadURL', url); } constructor (guestId: number) { - this.guestId = guestId - this._location = new LocationProxy(guestId) + this.guestId = guestId; + this._location = new LocationProxy(guestId); ipcRendererInternal.once(`ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_${guestId}`, () => { - removeProxy(guestId) - this.closed = true - }) + removeProxy(guestId); + this.closed = true; + }); + } + + public getSafe = (): SafelyBoundBrowserWindowProxy => { + const that = this; + return { + postMessage: this.postMessage, + blur: this.blur, + close: this.close, + focus: this.focus, + print: this.print, + eval: this.eval, + get location () { + return that.location; + }, + set location (url: string | any) { + that.location = url; + }, + get closed () { + return that.closed; + } + }; } - public close () { - this._invokeWindowMethod('destroy') + public close = () => { + this._invokeWindowMethod('destroy'); } - public focus () { - this._invokeWindowMethod('focus') + public focus = () => { + this._invokeWindowMethod('focus'); } - public blur () { - this._invokeWindowMethod('blur') + public blur = () => { + this._invokeWindowMethod('blur'); } - public print () { - this._invokeWebContentsMethod('print') + public print = () => { + this._invokeWebContentsMethod('print'); } - public postMessage (message: any, targetOrigin: string) { - ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, toString(targetOrigin), window.location.origin) + public postMessage = (message: any, targetOrigin: string) => { + ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, toString(targetOrigin), window.location.origin); } - public eval (code: string) { - this._invokeWebContentsMethod('executeJavaScript', code) + public eval = (code: string) => { + this._invokeWebContentsMethod('executeJavaScript', code); } private _invokeWindowMethod (method: string, ...args: any[]) { - return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, method, ...args) + return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, method, ...args); } private _invokeWebContentsMethod (method: string, ...args: any[]) { - return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args) + return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args); } } export const windowSetup = ( guestInstanceId: number, openerId: number, isHiddenPage: boolean, usesNativeWindowOpen: boolean ) => { - if (guestInstanceId == null) { + if (!process.sandboxed && guestInstanceId == null) { // Override default window.close. window.close = function () { - ipcRendererInternal.sendSync('ELECTRON_BROWSER_WINDOW_CLOSE') - } + ipcRendererInternal.sendSync('ELECTRON_BROWSER_WINDOW_CLOSE'); + }; + if (shouldUseContextBridge) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['close'], window.close); } if (!usesNativeWindowOpen) { + // TODO(MarshallOfSound): Make compatible with ctx isolation without hole-punch // Make the browser window or guest view emit "new-window" event. (window as any).open = function (url?: string, frameName?: string, features?: string) { if (url != null && url !== '') { - url = resolveURL(url, location.href) + url = resolveURL(url, location.href); } - const guestId = ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features)) + const guestId = ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features)); if (guestId != null) { - return getOrCreateProxy(guestId) + return getOrCreateProxy(guestId); } else { - return null + return null; } - } + }; + if (shouldUseContextBridge) internalContextBridge.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['open'], window.open); + } - if (openerId != null) { - window.opener = getOrCreateProxy(openerId) - } + if (openerId != null) { + window.opener = getOrCreateProxy(openerId); + if (shouldUseContextBridge) internalContextBridge.overrideGlobalValueWithDynamicPropsFromIsolatedWorld(['opener'], window.opener); } // But we do not support prompt(). window.prompt = function () { - throw new Error('prompt() is and will not be supported.') + throw new Error('prompt() is and will not be supported.'); + }; + if (shouldUseContextBridge) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['prompt'], window.prompt); + + if (!usesNativeWindowOpen || openerId != null) { + ipcRendererInternal.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function ( + _event, sourceId: number, message: any, sourceOrigin: string + ) { + // Manually dispatch event instead of using postMessage because we also need to + // set event.source. + // + // Why any? We can't construct a MessageEvent and we can't + // use `as MessageEvent` because you're not supposed to override + // data, origin, and source + const event: any = document.createEvent('Event'); + event.initEvent('message', false, false); + + event.data = message; + event.origin = sourceOrigin; + event.source = getOrCreateProxy(sourceId); + + window.dispatchEvent(event as MessageEvent); + }); } - ipcRendererInternal.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function ( - _event, sourceId: number, message: any, sourceOrigin: string - ) { - // Manually dispatch event instead of using postMessage because we also need to - // set event.source. - // - // Why any? We can't construct a MessageEvent and we can't - // use `as MessageEvent` because you're not supposed to override - // data, origin, and source - const event: any = document.createEvent('Event') - event.initEvent('message', false, false) - - event.data = message - event.origin = sourceOrigin - event.source = getOrCreateProxy(sourceId) - - window.dispatchEvent(event as MessageEvent) - }) - - window.history.back = function () { - ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK') + if (!process.sandboxed) { + window.history.back = function () { + ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK'); + }; + if (shouldUseContextBridge) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['history', 'back'], window.history.back); + + window.history.forward = function () { + ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD'); + }; + if (shouldUseContextBridge) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['history', 'forward'], window.history.forward); + + window.history.go = function (offset: number) { + ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset); + }; + if (shouldUseContextBridge) internalContextBridge.overrideGlobalValueFromIsolatedWorld(['history', 'go'], window.history.go); + + const getHistoryLength = () => ipcRendererInternal.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH'); + Object.defineProperty(window.history, 'length', { + get: getHistoryLength, + set () {} + }); + // TODO(MarshallOfSound): Fix so that the internal context bridge can override a non-configurable property + // if (shouldUseContextBridge) internalContextBridge.overrideGlobalPropertyFromIsolatedWorld(['history', 'length'], getHistoryLength); } - window.history.forward = function () { - ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD') - } - - window.history.go = function (offset: number) { - ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset) - } - - Object.defineProperty(window.history, 'length', { - get: function () { - return ipcRendererInternal.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH') - } - }) - if (guestInstanceId != null) { // Webview `document.visibilityState` tracks window visibility (and ignores // the actual element visibility) for backwards compatibility. @@ -253,27 +330,27 @@ export const windowSetup = ( // Note that this results in duplicate visibilitychange events (since // Chromium also fires them) and potentially incorrect visibility change. // We should reconsider this decision for Electron 2.0. - let cachedVisibilityState = isHiddenPage ? 'hidden' : 'visible' + let cachedVisibilityState = isHiddenPage ? 'hidden' : 'visible'; // Subscribe to visibilityState changes. ipcRendererInternal.on('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', function (_event, visibilityState: VisibilityState) { if (cachedVisibilityState !== visibilityState) { - cachedVisibilityState = visibilityState - document.dispatchEvent(new Event('visibilitychange')) + cachedVisibilityState = visibilityState; + document.dispatchEvent(new Event('visibilitychange')); } - }) + }); // Make document.hidden and document.visibilityState return the correct value. + const getDocumentHidden = () => cachedVisibilityState !== 'visible'; Object.defineProperty(document, 'hidden', { - get: function () { - return cachedVisibilityState !== 'visible' - } - }) + get: getDocumentHidden + }); + if (shouldUseContextBridge) internalContextBridge.overrideGlobalPropertyFromIsolatedWorld(['document', 'hidden'], getDocumentHidden); + const getDocumentVisibilityState = () => cachedVisibilityState; Object.defineProperty(document, 'visibilityState', { - get: function () { - return cachedVisibilityState - } - }) + get: getDocumentVisibilityState + }); + if (shouldUseContextBridge) internalContextBridge.overrideGlobalPropertyFromIsolatedWorld(['document', 'visibilityState'], getDocumentVisibilityState); } -} +}; diff --git a/lib/sandboxed_renderer/api/exports/electron.ts b/lib/sandboxed_renderer/api/exports/electron.ts index 421e398a5ad5b..f63029878eb06 100644 --- a/lib/sandboxed_renderer/api/exports/electron.ts +++ b/lib/sandboxed_renderer/api/exports/electron.ts @@ -1,4 +1,6 @@ -import { defineProperties } from '@electron/internal/common/define-properties' -import { moduleList } from '@electron/internal/sandboxed_renderer/api/module-list' +import { defineProperties } from '@electron/internal/common/define-properties'; +import { moduleList } from '@electron/internal/sandboxed_renderer/api/module-list'; -defineProperties(exports, moduleList) +module.exports = {}; + +defineProperties(module.exports, moduleList); diff --git a/lib/sandboxed_renderer/api/module-list.ts b/lib/sandboxed_renderer/api/module-list.ts index 228d4bc7651ae..85c0919538357 100644 --- a/lib/sandboxed_renderer/api/module-list.ts +++ b/lib/sandboxed_renderer/api/module-list.ts @@ -1,4 +1,4 @@ -const features = process.electronBinding('features') +const features = process.electronBinding('features'); export const moduleList: ElectronInternal.ModuleEntry[] = [ { @@ -27,18 +27,18 @@ export const moduleList: ElectronInternal.ModuleEntry[] = [ loader: () => require('@electron/internal/common/api/deprecate'), private: true } -] +]; if (features.isDesktopCapturerEnabled()) { moduleList.push({ name: 'desktopCapturer', loader: () => require('@electron/internal/renderer/api/desktop-capturer') - }) + }); } if (features.isRemoteModuleEnabled() && process.isRemoteModuleEnabled) { moduleList.push({ name: 'remote', loader: () => require('@electron/internal/renderer/api/remote') - }) + }); } diff --git a/lib/sandboxed_renderer/init.js b/lib/sandboxed_renderer/init.js index db711e638adde..72a2e8e73cd7a 100644 --- a/lib/sandboxed_renderer/init.js +++ b/lib/sandboxed_renderer/init.js @@ -1,143 +1,169 @@ -'use strict' +'use strict'; /* eslint no-eval: "off" */ /* global binding, Buffer */ -const events = require('events') -const { EventEmitter } = events +const events = require('events'); +const { EventEmitter } = events; -process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(binding.get, 'renderer') +process.electronBinding = require('@electron/internal/common/electron-binding-setup').electronBindingSetup(binding.get, 'renderer'); -const v8Util = process.electronBinding('v8_util') +const v8Util = process.electronBinding('v8_util'); // Expose Buffer shim as a hidden value. This is used by C++ code to // deserialize Buffer instances sent from browser process. -v8Util.setHiddenValue(global, 'Buffer', Buffer) +v8Util.setHiddenValue(global, 'Buffer', Buffer); // The `lib/renderer/api/ipc-renderer.ts` module looks for the ipc object in the // "ipc" hidden value -v8Util.setHiddenValue(global, 'ipc', new EventEmitter()) +v8Util.setHiddenValue(global, 'ipc', new EventEmitter()); // The `lib/renderer/ipc-renderer-internal.ts` module looks for the ipc object in the // "ipc-internal" hidden value -v8Util.setHiddenValue(global, 'ipc-internal', new EventEmitter()) +v8Util.setHiddenValue(global, 'ipc-internal', new EventEmitter()); // The process object created by webpack is not an event emitter, fix it so // the API is more compatible with non-sandboxed renderers. for (const prop of Object.keys(EventEmitter.prototype)) { if (process.hasOwnProperty(prop)) { - delete process[prop] + delete process[prop]; } } -Object.setPrototypeOf(process, EventEmitter.prototype) +Object.setPrototypeOf(process, EventEmitter.prototype); -const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal') -const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') +const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal'); +const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils'); const { - contentScripts, preloadScripts, isRemoteModuleEnabled, isWebViewTagEnabled, process: processProps -} = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_SANDBOX_LOAD') + contentScripts, + preloadScripts, + isRemoteModuleEnabled, + isWebViewTagEnabled, + guestInstanceId, + openerId, + process: processProps +} = ipcRendererUtils.invokeSync('ELECTRON_BROWSER_SANDBOX_LOAD'); -process.isRemoteModuleEnabled = isRemoteModuleEnabled +process.isRemoteModuleEnabled = isRemoteModuleEnabled; // The electron module depends on process.electronBinding -const electron = require('electron') +const electron = require('electron'); const loadedModules = new Map([ ['electron', electron], ['events', events], ['timers', require('timers')], ['url', require('url')] -]) +]); // ElectronApiServiceImpl will look for the "ipcNative" hidden object when // invoking the 'onMessage' callback. v8Util.setHiddenValue(global, 'ipcNative', { onMessage (internal, channel, args, senderId) { - const sender = internal ? ipcRendererInternal : electron.ipcRenderer - sender.emit(channel, { sender, senderId }, ...args) + const sender = internal ? ipcRendererInternal : electron.ipcRenderer; + sender.emit(channel, { sender, senderId }, ...args); } -}) +}); // AtomSandboxedRendererClient will look for the "lifecycle" hidden object when v8Util.setHiddenValue(global, 'lifecycle', { onLoaded () { - process.emit('loaded') + process.emit('loaded'); }, onExit () { - process.emit('exit') + process.emit('exit'); }, onDocumentStart () { - process.emit('document-start') + process.emit('document-start'); }, onDocumentEnd () { - process.emit('document-end') + process.emit('document-end'); } -}) +}); -const { webFrameInit } = require('@electron/internal/renderer/web-frame-init') -webFrameInit() +const { webFrameInit } = require('@electron/internal/renderer/web-frame-init'); +webFrameInit(); // Pass different process object to the preload script(which should not have // access to things like `process.electronBinding`). -const preloadProcess = new EventEmitter() +const preloadProcess = new EventEmitter(); -Object.assign(preloadProcess, binding.process) -Object.assign(preloadProcess, processProps) +Object.assign(preloadProcess, binding.process); +Object.assign(preloadProcess, processProps); -Object.assign(process, binding.process) -Object.assign(process, processProps) +Object.assign(process, binding.process); +Object.assign(process, processProps); Object.defineProperty(preloadProcess, 'noDeprecation', { get () { - return process.noDeprecation + return process.noDeprecation; }, set (value) { - process.noDeprecation = value + process.noDeprecation = value; } -}) +}); -process.on('loaded', () => preloadProcess.emit('loaded')) -process.on('exit', () => preloadProcess.emit('exit')) +process.on('loaded', () => preloadProcess.emit('loaded')); +process.on('exit', () => preloadProcess.emit('exit')); +process.on('document-start', () => preloadProcess.emit('document-start')); +process.on('document-end', () => preloadProcess.emit('document-end')); // This is the `require` function that will be visible to the preload script function preloadRequire (module) { if (loadedModules.has(module)) { - return loadedModules.get(module) + return loadedModules.get(module); } - throw new Error(`module not found: ${module}`) + throw new Error(`module not found: ${module}`); } // Process command line arguments. -const { hasSwitch } = process.electronBinding('command_line') +const { hasSwitch } = process.electronBinding('command_line'); -const contextIsolation = hasSwitch('context-isolation') +// Similar to nodes --expose-internals flag, this exposes electronBinding so +// that tests can call it to get access to some test only bindings +if (hasSwitch('unsafely-expose-electron-internals-for-testing')) { + preloadProcess.electronBinding = process.electronBinding; +} + +const contextIsolation = hasSwitch('context-isolation'); +const isHiddenPage = hasSwitch('hidden-page'); +const usesNativeWindowOpen = true; + +// The arguments to be passed to isolated world. +const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen }; switch (window.location.protocol) { case 'devtools:': { // Override some inspector APIs. - require('@electron/internal/renderer/inspector') - break + require('@electron/internal/renderer/inspector'); + break; } case 'chrome-extension:': { // Inject the chrome.* APIs that chrome extensions require if (!process.electronBinding('features').isExtensionsEnabled()) { - require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window) + require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window); } - break + break; } case 'chrome': { - break + break; } default: { + // Override default web functions. + const { windowSetup } = require('@electron/internal/renderer/window-setup'); + windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen); + // Inject content scripts. if (!process.electronBinding('features').isExtensionsEnabled()) { - require('@electron/internal/renderer/content-scripts-injector')(contentScripts) + require('@electron/internal/renderer/content-scripts-injector')(contentScripts); } } } -const guestInstanceId = binding.guestInstanceId && parseInt(binding.guestInstanceId) - // Load webview tag implementation. if (process.isMainFrame) { - const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init') - webViewInit(contextIsolation, isWebViewTagEnabled, guestInstanceId) + const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init'); + webViewInit(contextIsolation, isWebViewTagEnabled, guestInstanceId); +} + +// Pass the arguments to isolatedWorld. +if (contextIsolation) { + v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs); } // Wrap the script into a function executed in global scope. It won't have @@ -150,32 +176,32 @@ if (process.isMainFrame) { function runPreloadScript (preloadSrc) { const preloadWrapperSrc = `(function(require, process, Buffer, global, setImmediate, clearImmediate, exports) { ${preloadSrc} - })` + })`; // eval in window scope - const preloadFn = binding.createPreloadScript(preloadWrapperSrc) - const { setImmediate, clearImmediate } = require('timers') + const preloadFn = binding.createPreloadScript(preloadWrapperSrc); + const { setImmediate, clearImmediate } = require('timers'); - preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate, {}) + preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate, {}); } for (const { preloadPath, preloadSrc, preloadError } of preloadScripts) { try { if (preloadSrc) { - runPreloadScript(preloadSrc) + runPreloadScript(preloadSrc); } else if (preloadError) { - throw preloadError + throw preloadError; } } catch (error) { - console.error(`Unable to load preload script: ${preloadPath}`) - console.error(error) + console.error(`Unable to load preload script: ${preloadPath}`); + console.error(error); - ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadPath, error) + ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadPath, error); } } // Warn about security issues if (process.isMainFrame) { - const { securityWarnings } = require('@electron/internal/renderer/security-warnings') - securityWarnings() + const { securityWarnings } = require('@electron/internal/renderer/security-warnings'); + securityWarnings(); } diff --git a/lib/worker/init.js b/lib/worker/init.js index 3a6f2eb2bee29..05a6443693804 100644 --- a/lib/worker/init.js +++ b/lib/worker/init.js @@ -1,36 +1,36 @@ -'use strict' +'use strict'; -const path = require('path') -const Module = require('module') +const path = require('path'); +const Module = require('module'); // We modified the original process.argv to let node.js load the // init.js, we need to restore it here. -process.argv.splice(1, 1) +process.argv.splice(1, 1); // Clear search paths. -require('../common/reset-search-paths') +require('../common/reset-search-paths'); // Import common settings. -require('@electron/internal/common/init') +require('@electron/internal/common/init'); // Export node bindings to global. const { makeRequireFunction } = __non_webpack_require__('internal/modules/cjs/helpers') // eslint-disable-line -global.module = new Module('electron/js2c/worker_init') -global.require = makeRequireFunction(global.module) +global.module = new Module('electron/js2c/worker_init'); +global.require = makeRequireFunction(global.module); // Set the __filename to the path of html file if it is file: protocol. if (self.location.protocol === 'file:') { - const pathname = process.platform === 'win32' && self.location.pathname[0] === '/' ? self.location.pathname.substr(1) : self.location.pathname - global.__filename = path.normalize(decodeURIComponent(pathname)) - global.__dirname = path.dirname(global.__filename) + const pathname = process.platform === 'win32' && self.location.pathname[0] === '/' ? self.location.pathname.substr(1) : self.location.pathname; + global.__filename = path.normalize(decodeURIComponent(pathname)); + global.__dirname = path.dirname(global.__filename); // Set module's filename so relative require can work as expected. - global.module.filename = global.__filename + global.module.filename = global.__filename; // Also search for module under the html file. - global.module.paths = Module._nodeModulePaths(global.__dirname) + global.module.paths = Module._nodeModulePaths(global.__dirname); } else { // For backwards compatibility we fake these two paths here - global.__filename = path.join(process.resourcesPath, 'electron.asar', 'worker', 'init.js') - global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'worker') + global.__filename = path.join(process.resourcesPath, 'electron.asar', 'worker', 'init.js'); + global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'worker'); } diff --git a/native_mate/native_mate/function_template.h b/native_mate/native_mate/function_template.h index babba95063080..d24181f7014e6 100644 --- a/native_mate/native_mate/function_template.h +++ b/native_mate/native_mate/function_template.h @@ -5,6 +5,8 @@ #ifndef NATIVE_MATE_NATIVE_MATE_FUNCTION_TEMPLATE_H_ #define NATIVE_MATE_NATIVE_MATE_FUNCTION_TEMPLATE_H_ +#include + #include "base/callback.h" #include "native_mate/arguments.h" #include "native_mate/wrappable_base.h" @@ -194,7 +196,8 @@ class Invoker, ArgTypes...> void DispatchToCallback(base::Callback callback) { v8::MicrotasksScope script_scope(args_->isolate(), v8::MicrotasksScope::kRunMicrotasks); - args_->Return(callback.Run(ArgumentHolder::value...)); + args_->Return( + callback.Run(std::move(ArgumentHolder::value)...)); } // In C++, you can declare the function foo(void), but you can't pass a void @@ -203,7 +206,7 @@ class Invoker, ArgTypes...> void DispatchToCallback(base::Callback callback) { v8::MicrotasksScope script_scope(args_->isolate(), v8::MicrotasksScope::kRunMicrotasks); - callback.Run(ArgumentHolder::value...); + callback.Run(std::move(ArgumentHolder::value)...); } private: diff --git a/package.json b/package.json index 01bae86dc4580..9a35e8509d881 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "electron", - "version": "8.0.0-nightly.20191023", + "version": "8.3.0", "repository": "https://github.com/electron/electron", "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS", "devDependencies": { "@electron/docs-parser": "^0.4.2", - "@electron/typescript-definitions": "^8.6.1", + "@electron/typescript-definitions": "^8.6.4", "@octokit/rest": "^16.3.2", "@primer/octicons": "^9.1.1", "@types/basic-auth": "^1.1.2", @@ -129,6 +129,9 @@ "node script/gen-filenames.js", "python script/check-trailing-whitespace.py --fix", "git add filenames.auto.gni" + ], + "DEPS": [ + "node script/gen-hunspell-filenames.js" ] }, "dependencies": { diff --git a/patches/angle/.patches b/patches/angle/.patches new file mode 100644 index 0000000000000..bceb307a3fe70 --- /dev/null +++ b/patches/angle/.patches @@ -0,0 +1 @@ +update_the_active_texture_cache_before_changing_the_texture_binding.patch diff --git a/patches/angle/update_the_active_texture_cache_before_changing_the_texture_binding.patch b/patches/angle/update_the_active_texture_cache_before_changing_the_texture_binding.patch new file mode 100644 index 0000000000000..ed12815ea40f8 --- /dev/null +++ b/patches/angle/update_the_active_texture_cache_before_changing_the_texture_binding.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Geoff Lang +Date: Fri, 27 Mar 2020 12:24:52 -0400 +Subject: Update the active texture cache before changing the texture binding. + +When a new texture is bound, the texture binding state is updated before +updating the active texture cache. With this ordering, it is possible to delete +the currently bound texture when the binding changes and then use-after-free it +when updating the active texture cache. + +BUG=angleproject:1065186 + +Change-Id: Id6d56b6c6db423755b195cda1e5cf1bcb1ee7aee +Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2124588 +Commit-Queue: Geoff Lang +Reviewed-by: Jamie Madill + +diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp +index 3fab8404046846e5b8ed6253139537360e83c409..c6c6ee75ebf554ce47cfcef218dca6a22075a4aa 100644 +--- a/src/libANGLE/State.cpp ++++ b/src/libANGLE/State.cpp +@@ -1138,14 +1138,14 @@ void State::setActiveSampler(unsigned int active) + + void State::setSamplerTexture(const Context *context, TextureType type, Texture *texture) + { +- mSamplerTextures[type][mActiveSampler].set(context, texture); +- + if (mProgram && mProgram->getActiveSamplersMask()[mActiveSampler] && + mProgram->getActiveSamplerTypes()[mActiveSampler] == type) + { + updateActiveTexture(context, mActiveSampler, texture); + } + ++ mSamplerTextures[type][mActiveSampler].set(context, texture); ++ + mDirtyBits.set(DIRTY_BIT_TEXTURE_BINDINGS); + } + diff --git a/patches/boringssl/.patches b/patches/boringssl/.patches index 07c69fae2e1b3..419ee092c98e8 100644 --- a/patches/boringssl/.patches +++ b/patches/boringssl/.patches @@ -1,2 +1,3 @@ expose_ripemd160.patch expose_aes-cfb.patch +fix_add_RSA-PSS_keygen_functions.patch diff --git a/patches/boringssl/expose_ripemd160.patch b/patches/boringssl/expose_ripemd160.patch index 28240db0e73c2..826fce289b385 100644 --- a/patches/boringssl/expose_ripemd160.patch +++ b/patches/boringssl/expose_ripemd160.patch @@ -80,7 +80,7 @@ index d540144b293297791c087e0b968a47d368a73695..53cb9d2dc8f1962a70dc12b648d27c32 + callback(EVP_ripemd160(), "ripemd160", NULL, arg); } diff --git a/include/openssl/digest.h b/include/openssl/digest.h -index c7c679760a30329311b4a6c0db475b2375537028..991fa00b8330a663625719436075bcc8ae32e17a 100644 +index c3ceb7f22a123353f23cb33de9cc2931fb5e1c11..704d43a326e7cced925c9db1c2a6094a16f9abe9 100644 --- a/include/openssl/digest.h +++ b/include/openssl/digest.h @@ -88,6 +88,9 @@ OPENSSL_EXPORT const EVP_MD *EVP_sha512(void); diff --git a/patches/boringssl/fix_add_RSA-PSS_keygen_functions.patch b/patches/boringssl/fix_add_RSA-PSS_keygen_functions.patch new file mode 100644 index 0000000000000..d6fb774f660ab --- /dev/null +++ b/patches/boringssl/fix_add_RSA-PSS_keygen_functions.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shelley Vohr +Date: Wed, 23 Oct 2019 12:54:32 -0700 +Subject: fix: add RSA-PSS keygen functions + +This adds support for missing RSA_PSS key generation functions. +Refs https://github.com/nodejs/node/pull/26960. + +Upstreamed at https://boringssl-review.googlesource.com/c/boringssl/+/38524. + +diff --git a/include/openssl/evp.h b/include/openssl/evp.h +index 19baa64ddba84c3dd59e65aef77d1ebbf49e43df..37217c49f7e05eb25562023bf356fdadae1bc66f 100644 +--- a/include/openssl/evp.h ++++ b/include/openssl/evp.h +@@ -723,6 +723,18 @@ OPENSSL_EXPORT int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int padding); + OPENSSL_EXPORT int EVP_PKEY_CTX_get_rsa_padding(EVP_PKEY_CTX *ctx, + int *out_padding); + ++// EVP_PKEY_CTX_set_rsa_pss_keygen_md() always returns 0. ++OPENSSL_EXPORT int EVP_PKEY_CTX_set_rsa_pss_keygen_md(EVP_PKEY_CTX *ctx, ++ const EVP_MD *md); ++ ++// EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen() always returns 0. ++OPENSSL_EXPORT int EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(EVP_PKEY_CTX *ctx, ++ int salt_len); ++ ++// EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md() always returns 0. ++OPENSSL_EXPORT int EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(EVP_PKEY_CTX *ctx, ++ const EVP_MD *md); ++ + // EVP_PKEY_CTX_set_rsa_pss_saltlen sets the length of the salt in a PSS-padded + // signature. A value of -1 cause the salt to be the same length as the digest + // in the signature. A value of -2 causes the salt to be the maximum length diff --git a/patches/breakpad/chore_fix_ftbfs_from_stl_features_used_but_not_included.patch b/patches/breakpad/chore_fix_ftbfs_from_stl_features_used_but_not_included.patch new file mode 100644 index 0000000000000..c59b2fc0e58b3 --- /dev/null +++ b/patches/breakpad/chore_fix_ftbfs_from_stl_features_used_but_not_included.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Charles Kerr +Date: Fri, 15 Nov 2019 17:14:54 -0600 +Subject: chore: fix FTBFS from stl features used but not included + + +diff --git a/src/common/windows/pe_util.cc b/src/common/windows/pe_util.cc +index 6fa63fa3b5699b79d3479c6d4a37a1c68553223f..59c1c1d74f70dca8f871fdddd8e27da102ec9148 100644 +--- a/src/common/windows/pe_util.cc ++++ b/src/common/windows/pe_util.cc +@@ -119,7 +119,6 @@ private: + + namespace google_breakpad { + +-using std::unique_ptr; + using google_breakpad::GUIDString; + + bool ReadModuleInfo(const wstring & pe_file, PDBModuleInfo * info) { +diff --git a/src/tools/windows/dump_syms/dump_syms.cc b/src/tools/windows/dump_syms/dump_syms.cc +index 5b7d1777538cd3cfefd0bef45558f900798d6c51..2b2fdaf3f908db1d6f3f96a6bc23f0698cf879dc 100644 +--- a/src/tools/windows/dump_syms/dump_syms.cc ++++ b/src/tools/windows/dump_syms/dump_syms.cc +@@ -41,7 +41,6 @@ + using std::wstring; + using google_breakpad::PDBSourceLineWriter; + using google_breakpad::PESourceLineWriter; +-using std::unique_ptr; + + int wmain(int argc, wchar_t **argv) { + bool success; diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 9c3096bf8e50b..2130b2beac189 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -34,13 +34,13 @@ scroll_bounce_flag.patch mas-cfisobjc.patch mas-cgdisplayusesforcetogray.patch mas-audiodeviceduck.patch -mas-lssetapplicationlaunchservicesserverconnectionstatus.patch -ignore_rc_check.patch +mas_disable_remote_layer.patch +mas_disable_remote_accessibility.patch +mas_disable_custom_window_frame.patch chrome_key_systems.patch allow_nested_error_trackers.patch blink_initialization_order.patch ssl_security_state_tab_helper.patch -exclude-a-few-test-files-from-build.patch desktop_media_list.patch proxy_config_monitor.patch gritsettings_resource_ids.patch @@ -60,7 +60,6 @@ fix_disable_usage_of_setapplicationisdaemon_and.patch unsandboxed_ppapi_processes_skip_zygote.patch patch_the_ensure_gn_version_py_script_to_work_on_mac_ci.patch build_add_electron_tracing_category.patch -disable_custom_libcxx_on_windows.patch worker_context_will_destroy.patch fix_breakpad_symbol_generation_on_linux_arm.patch frame_host_manager.patch @@ -68,7 +67,6 @@ crashpad_pid_check.patch preconnect_feature.patch network_service_allow_remote_certificate_verification_logic.patch put_back_deleted_colors_for_autofill.patch -build_win_disable_zc_twophase.patch disable_color_correct_rendering.patch add_contentgpuclient_precreatemessageloop_callback.patch picture-in-picture.patch @@ -77,7 +75,28 @@ allow_new_privileges_in_unsandboxed_child_processes.patch expose_setuseragent_on_networkcontext.patch feat_add_set_theme_source_to_allow_apps_to.patch revert_cleanup_remove_menu_subtitles_sublabels.patch -ui_views_fix_jumbo_build.patch export_fetchapi_mojo_traits_to_fix_component_build.patch -build_fix_when_building_with_enable_plugins_false.patch -add_zoom_limit_setters_to_webcontents.patch +revert_remove_contentrendererclient_shouldfork.patch +ignore_rc_check.patch +remove_usage_of_incognito_apis_in_the_spellchecker.patch +chore_use_electron_resources_not_chrome_for_spellchecker.patch +add_trustedauthclient_to_urlloaderfactory.patch +feat_allow_disbaling_blink_scheduler_throttling_per_renderview.patch +accessible_pane_view.patch +fix_add_executable_to_chromedriver_s_rpath_for_electron_8.patch +fix_use_the_new_mediaplaypause_key_listener_for_internal_chrome.patch +port_aria_tree_fix_for_macos_voiceover.patch +fix_route_mouse_event_navigations_through_the_web_contents_delegate.patch +feat_add_support_for_overriding_the_base_spellchecker_download_url.patch +os_metrics_mac.patch +feat_allow_embedders_to_add_observers_on_created_hunspell.patch +feat_enable_offscreen_rendering_with_viz_compositor.patch +when_suspending_context_don_t_clear_handlers.patch +use_keepselfalive_on_audiocontext_to_keep_it_alive_until_rendering.patch +never_let_a_non-zero-size_pixel_snap_to_zero_size.patch +cherry-pick-826a4af58b3d.patch +cherry-pick-686d1bfbcb8f.patch +cherry-pick-7101418f85a0.patch +cherry-pick-38990b7d56e6.patch +cherry-pick-67864c214770.patch +cherry-pick-86c02c5dcd37.patch diff --git a/patches/chromium/accelerator.patch b/patches/chromium/accelerator.patch index 6d13b2725c485..d43167e6d5347 100644 --- a/patches/chromium/accelerator.patch +++ b/patches/chromium/accelerator.patch @@ -10,7 +10,7 @@ This patch makes three changes to Accelerator::GetShortcutText to improve shortc 3. Ctrl-Shift-= should show as Ctrl-+ diff --git a/ui/base/accelerators/accelerator.cc b/ui/base/accelerators/accelerator.cc -index 841a8ccd1f0ca5073cf0f0621f81561ed6425e0d..ac4c04b74b0a3b8e6d6c375fef021abbd8789a14 100644 +index 025a82c14dd379f82e6c8242727346bd21e768f3..8b8b0e77c355acd1ef22fb51b89f65f90bfe5cc6 100644 --- a/ui/base/accelerators/accelerator.cc +++ b/ui/base/accelerators/accelerator.cc @@ -11,6 +11,7 @@ @@ -74,7 +74,7 @@ index 841a8ccd1f0ca5073cf0f0621f81561ed6425e0d..ac4c04b74b0a3b8e6d6c375fef021abb + base::StringPrintf("F%d", key_code_ - VKEY_F1 + 1)); } - // Checking whether the character used for the accelerator is alphanumeric. + #if defined(OS_MACOSX) @@ -356,7 +356,7 @@ base::string16 Accelerator::ApplyLongFormModifiers( // more information. if (IsCtrlDown()) diff --git a/patches/chromium/accessible_pane_view.patch b/patches/chromium/accessible_pane_view.patch new file mode 100644 index 0000000000000..d76a275376e24 --- /dev/null +++ b/patches/chromium/accessible_pane_view.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cheng Zhao +Date: Thu, 4 Oct 2018 14:57:02 -0700 +Subject: fix: add back virtual methods in AccessiblePaneView + +Mark SetPaneFocus and RemovePaneFocus as virtual in AccessiblePaneView, as we +need to override them in MenuBar. + +Pending upstream patch: https://crrev.com/c/1959189 + +diff --git a/ui/views/accessible_pane_view.h b/ui/views/accessible_pane_view.h +index 813fd13860a863cd1e6e5bfec38d15f798418673..990c905e8f19dd015a625010ea30adfcb6f51ea6 100644 +--- a/ui/views/accessible_pane_view.h ++++ b/ui/views/accessible_pane_view.h +@@ -35,7 +35,7 @@ class VIEWS_EXPORT AccessiblePaneView : public View, + // If |initial_focus| is not NULL, that control will get + // the initial focus, if it's enabled and focusable. Returns true if + // the pane was able to receive focus. +- bool SetPaneFocus(View* initial_focus); ++ virtual bool SetPaneFocus(View* initial_focus); + + bool pane_has_focus() const { return pane_has_focus_; } + +@@ -83,7 +83,7 @@ class VIEWS_EXPORT AccessiblePaneView : public View, + bool ContainsForFocusSearch(View* root, const View* v); + + // Remove pane focus. +- void RemovePaneFocus(); ++ virtual void RemovePaneFocus(); + + View* GetFirstFocusableChild(); + View* GetLastFocusableChild(); diff --git a/patches/chromium/add_contentgpuclient_precreatemessageloop_callback.patch b/patches/chromium/add_contentgpuclient_precreatemessageloop_callback.patch index e043bfc175f90..ea29b85d93838 100644 --- a/patches/chromium/add_contentgpuclient_precreatemessageloop_callback.patch +++ b/patches/chromium/add_contentgpuclient_precreatemessageloop_callback.patch @@ -10,10 +10,10 @@ Allows Electron to restore WER when ELECTRON_DEFAULT_ERROR_MODE is set. This should be upstreamed. diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc -index 4373f508964cb1e9fd51c952122ac2f41fb35ae9..065a7a57b77f1e3bf5e03fac900f1936d48ad4ff 100644 +index 35b450a41a9a790aa166316a09f6439be7df08c6..39b1ac57f674777f398e72f565dc0c959f683ec7 100644 --- a/content/gpu/gpu_main.cc +++ b/content/gpu/gpu_main.cc -@@ -236,6 +236,10 @@ int GpuMain(const MainFunctionParams& parameters) { +@@ -258,6 +258,10 @@ int GpuMain(const MainFunctionParams& parameters) { logging::SetLogMessageHandler(GpuProcessLogMessageHandler); @@ -24,7 +24,7 @@ index 4373f508964cb1e9fd51c952122ac2f41fb35ae9..065a7a57b77f1e3bf5e03fac900f1936 // We are experiencing what appear to be memory-stomp issues in the GPU // process. These issues seem to be impacting the task executor and listeners // registered to it. Create the task executor on the heap to guard against -@@ -349,7 +353,6 @@ int GpuMain(const MainFunctionParams& parameters) { +@@ -366,7 +370,6 @@ int GpuMain(const MainFunctionParams& parameters) { GpuProcess gpu_process(io_thread_priority); #endif @@ -33,7 +33,7 @@ index 4373f508964cb1e9fd51c952122ac2f41fb35ae9..065a7a57b77f1e3bf5e03fac900f1936 client->PostIOThreadCreated(gpu_process.io_task_runner()); diff --git a/content/public/gpu/content_gpu_client.h b/content/public/gpu/content_gpu_client.h -index 830d5e89af7bd3ddb409ac7bf9d8eb3109830d41..7d544536ec846c294deb6626f51d5de7846fc5c2 100644 +index f68558bd2c4ff725443b0d6893ebe7da07c26a00..3dda58157f32dbc9c7d9001192c30a3a1c152437 100644 --- a/content/public/gpu/content_gpu_client.h +++ b/content/public/gpu/content_gpu_client.h @@ -39,6 +39,10 @@ class CONTENT_EXPORT ContentGpuClient { @@ -44,6 +44,6 @@ index 830d5e89af7bd3ddb409ac7bf9d8eb3109830d41..7d544536ec846c294deb6626f51d5de7 + // creating the message loop. + virtual void PreCreateMessageLoop() {} + - // Initializes the registry. |registry| will be passed to a ConnectionFilter - // (which lives on the IO thread). Unlike other childthreads, the client must - // register additional interfaces on this registry rather than just creating + // Called during initialization once the GpuService has been initialized. + virtual void GpuServiceInitialized() {} + diff --git a/patches/chromium/add_realloc.patch b/patches/chromium/add_realloc.patch index 0ed46e26c4649..d88474fa0a946 100644 --- a/patches/chromium/add_realloc.patch +++ b/patches/chromium/add_realloc.patch @@ -12,7 +12,7 @@ when we override ReallocateBufferMemory, so we therefore need to implement Realloc on the v8 side and correspondingly in gin. diff --git a/gin/array_buffer.cc b/gin/array_buffer.cc -index a02797e94f61e8c71428633a4585a625dc5aadbd..305b7d307d233af699e3f495f85de0f8097ff311 100644 +index b94a71ef856f92404e16544e22ebbadf57cff363..b15685f6907bcafeb1acd62bfe76f0713dea27f3 100644 --- a/gin/array_buffer.cc +++ b/gin/array_buffer.cc @@ -43,6 +43,10 @@ void* ArrayBufferAllocator::AllocateUninitialized(size_t length) { @@ -39,45 +39,45 @@ index 2aef366ac8194aa261cbca6abc051f7da8a988d3..3c7d66c81032636abcca4f1538ce9b7f GIN_EXPORT static ArrayBufferAllocator* SharedInstance(); diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc -index 3b1ced15de47f6b88716db2b79306bb7d98cfdf4..0ed89b4147d98f2239a3504c898666a758cee618 100644 +index c69cfebccd7d7bff35d2292e61bf6008f0ca7940..2cb5f5d3f89f2af13221e66b2277a28532d53deb 100644 --- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc +++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc -@@ -645,6 +645,10 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { - size, WTF::ArrayBufferContents::kDontInitialize); +@@ -654,6 +654,10 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { + size, ArrayBufferContents::kDontInitialize); } + void* Realloc(void* data, size_t size) override { -+ return WTF::ArrayBufferContents::Realloc(data, size); ++ return ArrayBufferContents::Realloc(data, size); + } + void Free(void* data, size_t size) override { - WTF::ArrayBufferContents::FreeMemory(data); + ArrayBufferContents::FreeMemory(data); } -diff --git a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.cc b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.cc -index ac76d127b96b80c8260a7e2cda0b669cd98787ad..dcab64586700a8740262aede8dba2755f652d8e8 100644 ---- a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.cc -+++ b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.cc -@@ -130,6 +130,11 @@ void* ArrayBufferContents::AllocateMemoryOrNull(size_t size, +diff --git a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc +index 095ca3db051362319611976b9052725ab16c1b3a..13e802a5732f0b04832d6dd8031ae43e1240b79a 100644 +--- a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc ++++ b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc +@@ -126,6 +126,11 @@ void* ArrayBufferContents::AllocateMemoryOrNull(size_t size, return AllocateMemoryWithFlags(size, policy, base::PartitionAllocReturnNull); } +void* ArrayBufferContents::Realloc(void* data, size_t size) { -+ return Partitions::ArrayBufferPartition()->Realloc(data, size, ++ return WTF::Partitions::ArrayBufferPartition()->Realloc(data, size, + WTF_HEAP_PROFILER_TYPE_NAME(ArrayBufferContents)); +} + void ArrayBufferContents::FreeMemory(void* data) { - Partitions::ArrayBufferPartition()->Free(data); + WTF::Partitions::ArrayBufferPartition()->Free(data); } -diff --git a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h -index ee7c89a4950d6b3c97a810c41a62fee7e372018d..c2b60f20a72cca46d05af369f33d9c19395b34ef 100644 ---- a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h -+++ b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h -@@ -140,6 +140,7 @@ class WTF_EXPORT ArrayBufferContents { +diff --git a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h +index 2e871b20c1e6fc3703178957fd03923f02ab1b47..8f2fd80130f1e7f957d642af4e5c22f9a5937ce4 100644 +--- a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h ++++ b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h +@@ -106,6 +106,7 @@ class CORE_EXPORT ArrayBufferContents { void CopyTo(ArrayBufferContents& other); static void* AllocateMemoryOrNull(size_t, InitializationPolicy); + static void* Realloc(void* data, size_t); static void FreeMemory(void*); - static DataHandle CreateDataHandle(size_t, InitializationPolicy); - static void Initialize( + + private: diff --git a/patches/chromium/add_trustedauthclient_to_urlloaderfactory.patch b/patches/chromium/add_trustedauthclient_to_urlloaderfactory.patch new file mode 100644 index 0000000000000..09edb6bf98ccc --- /dev/null +++ b/patches/chromium/add_trustedauthclient_to_urlloaderfactory.patch @@ -0,0 +1,158 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jeremy Apthorp +Date: Tue, 12 Nov 2019 11:50:16 -0800 +Subject: add TrustedAuthClient to URLLoaderFactory + +This allows intercepting authentication requests for the 'net' module. +Without this, the 'login' event for electron.net.ClientRequest can't be +implemented, because the existing path checks for the presence of a +WebContents, and cancels the authentication if there's no WebContents +available, which there isn't in the case of the 'net' module. + +diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom +index 691f9b0f6f658cc259cc9c4e8bb3abb15592e8a3..09ecb3f3406da54440cdd977556f602496ec39a6 100644 +--- a/services/network/public/mojom/network_context.mojom ++++ b/services/network/public/mojom/network_context.mojom +@@ -190,6 +190,25 @@ struct HttpAuthStaticNetworkContextParams { + = DefaultCredentials.ALLOW_DEFAULT_CREDENTIALS; + }; + ++interface TrustedAuthClient { ++ OnAuthRequired( ++ mojo_base.mojom.UnguessableToken? window_id, ++ uint32 process_id, ++ uint32 routing_id, ++ uint32 request_id, ++ url.mojom.Url url, ++ bool first_auth_attempt, ++ AuthChallengeInfo auth_info, ++ URLResponseHead? head, ++ pending_remote auth_challenge_responder); ++}; ++interface TrustedURLLoaderAuthClient { ++ // When a new URLLoader is created, this will be called to pass a ++ // corresponding |auth_client|. ++ OnLoaderCreated(int32 request_id, ++ pending_receiver auth_client); ++}; ++ + interface CertVerifierClient { + Verify( + int32 default_error, +@@ -597,6 +616,8 @@ struct URLLoaderFactoryParams { + // interface. This still respects the per-context block lists. + CorsOriginAccessPatterns? factory_bound_access_patterns; + ++ pending_remote? auth_client; ++ + // Key used to isolate shared network resources like the cache. + NetworkIsolationKey? network_isolation_key; + +diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc +index 486c92182695958a0c97d48d55ebd23e0bd5f3de..acd6c63eff1f94cc9fa8490610e571a73764369a 100644 +--- a/services/network/url_loader.cc ++++ b/services/network/url_loader.cc +@@ -333,6 +333,7 @@ URLLoader::URLLoader( + base::WeakPtr keepalive_statistics_recorder, + base::WeakPtr network_usage_accumulator, + mojom::TrustedURLLoaderHeaderClient* url_loader_header_client, ++ mojom::TrustedURLLoaderAuthClient* url_loader_auth_client, + mojom::OriginPolicyManager* origin_policy_manager) + : url_request_context_(url_request_context), + network_service_client_(network_service_client), +@@ -390,6 +391,11 @@ URLLoader::URLLoader( + header_client_.set_disconnect_handler( + base::BindOnce(&URLLoader::OnMojoDisconnect, base::Unretained(this))); + } ++ if (url_loader_auth_client) { ++ url_loader_auth_client->OnLoaderCreated(request_id_, auth_client_.BindNewPipeAndPassReceiver()); ++ auth_client_.set_disconnect_handler( ++ base::BindOnce(&URLLoader::OnMojoDisconnect, base::Unretained(this))); ++ } + if (want_raw_headers_) { + options_ |= mojom::kURLLoadOptionSendSSLInfoWithResponse | + mojom::kURLLoadOptionSendSSLInfoForCertificateError; +@@ -824,7 +830,7 @@ void URLLoader::OnReceivedRedirect(net::URLRequest* url_request, + + void URLLoader::OnAuthRequired(net::URLRequest* url_request, + const net::AuthChallengeInfo& auth_info) { +- if (!network_context_client_) { ++ if (!network_context_client_ && !auth_client_) { + OnAuthCredentials(base::nullopt); + return; + } +@@ -840,10 +846,18 @@ void URLLoader::OnAuthRequired(net::URLRequest* url_request, + if (url_request->response_headers()) + head.headers = url_request->response_headers(); + head.auth_challenge_info = auth_info; +- network_context_client_->OnAuthRequired( +- fetch_window_id_, factory_params_->process_id, render_frame_id_, +- request_id_, url_request_->url(), first_auth_attempt_, auth_info, head, +- auth_challenge_responder_receiver_.BindNewPipeAndPassRemote()); ++ ++ if (auth_client_) { ++ auth_client_->OnAuthRequired( ++ fetch_window_id_, factory_params_->process_id, render_frame_id_, ++ request_id_, url_request_->url(), first_auth_attempt_, auth_info, head, ++ auth_challenge_responder_receiver_.BindNewPipeAndPassRemote()); ++ } else { ++ network_context_client_->OnAuthRequired( ++ fetch_window_id_, factory_params_->process_id, render_frame_id_, ++ request_id_, url_request_->url(), first_auth_attempt_, auth_info, head, ++ auth_challenge_responder_receiver_.BindNewPipeAndPassRemote()); ++ } + + auth_challenge_responder_receiver_.set_disconnect_handler( + base::BindOnce(&URLLoader::DeleteSelf, base::Unretained(this))); +diff --git a/services/network/url_loader.h b/services/network/url_loader.h +index 564851586d2ad5fe0ae6acd4457257ce8ed665b3..3291fe28deb20b9630589e8ea11ea1366583081c 100644 +--- a/services/network/url_loader.h ++++ b/services/network/url_loader.h +@@ -85,6 +85,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader + base::WeakPtr keepalive_statistics_recorder, + base::WeakPtr network_usage_accumulator, + mojom::TrustedURLLoaderHeaderClient* url_loader_header_client, ++ mojom::TrustedURLLoaderAuthClient* url_loader_auth_client, + mojom::OriginPolicyManager* origin_policy_manager); + ~URLLoader() override; + +@@ -378,6 +379,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader + base::Optional fetch_window_id_; + + mojo::Remote header_client_; ++ mojo::Remote auth_client_; + + std::unique_ptr file_opener_for_upload_; + +diff --git a/services/network/url_loader_factory.cc b/services/network/url_loader_factory.cc +index 9e9baaf8ecd33e6416027014361edc332ab71e3c..cc2c59828f56988d43b680588d4625d9864aa9b6 100644 +--- a/services/network/url_loader_factory.cc ++++ b/services/network/url_loader_factory.cc +@@ -65,6 +65,7 @@ URLLoaderFactory::URLLoaderFactory( + params_(std::move(params)), + resource_scheduler_client_(std::move(resource_scheduler_client)), + header_client_(std::move(params_->header_client)), ++ auth_client_(std::move(params_->auth_client)), + cors_url_loader_factory_(cors_url_loader_factory) { + DCHECK(context); + DCHECK_NE(mojom::kInvalidProcessId, params_->process_id); +@@ -207,6 +208,7 @@ void URLLoaderFactory::CreateLoaderAndStart( + resource_scheduler_client_, std::move(keepalive_statistics_recorder), + std::move(network_usage_accumulator), + header_client_.is_bound() ? header_client_.get() : nullptr, ++ auth_client_.is_bound() ? auth_client_.get() : nullptr, + context_->origin_policy_manager()); + cors_url_loader_factory_->OnLoaderCreated(std::move(loader)); + } +diff --git a/services/network/url_loader_factory.h b/services/network/url_loader_factory.h +index 7d13494649c43be52b06774f2cf5763ebe9129c0..d4b19342c44f86c685f700e4260475ff2235b298 100644 +--- a/services/network/url_loader_factory.h ++++ b/services/network/url_loader_factory.h +@@ -72,6 +72,7 @@ class URLLoaderFactory : public mojom::URLLoaderFactory { + mojom::URLLoaderFactoryParamsPtr params_; + scoped_refptr resource_scheduler_client_; + mojo::Remote header_client_; ++ mojo::Remote auth_client_; + + // |cors_url_loader_factory_| owns this. + cors::CorsURLLoaderFactory* cors_url_loader_factory_; diff --git a/patches/chromium/add_zoom_limit_setters_to_webcontents.patch b/patches/chromium/add_zoom_limit_setters_to_webcontents.patch deleted file mode 100644 index 56a923402aed2..0000000000000 --- a/patches/chromium/add_zoom_limit_setters_to_webcontents.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: John Kleinschmidt -Date: Mon, 23 Sep 2019 17:07:53 -0400 -Subject: add zoom limit setters to webcontents - -Allows minimum_zoom_percent_ and maximum_zoom_percent_ to be set on WebContents. -This is needed by Electron to allow apps to limit how much an app can zoom in -or out. - -diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 8586f71224f1cae6a5e9b4005a6acfef9ff64d4d..4d4dce8124312d46f90db8d2fc59bd0b7acd498d 100644 ---- a/content/browser/web_contents/web_contents_impl.cc -+++ b/content/browser/web_contents/web_contents_impl.cc -@@ -4142,10 +4142,18 @@ bool WebContentsImpl::GetClosedByUserGesture() { - return closed_by_user_gesture_; - } - -+void WebContentsImpl::SetMinimumZoomPercent(int zoom_percent) { -+ minimum_zoom_percent_ = zoom_percent; -+} -+ - int WebContentsImpl::GetMinimumZoomPercent() { - return minimum_zoom_percent_; - } - -+void WebContentsImpl::SetMaximumZoomPercent(int zoom_percent) { -+ maximum_zoom_percent_ = zoom_percent; -+} -+ - int WebContentsImpl::GetMaximumZoomPercent() { - return maximum_zoom_percent_; - } -diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h -index 72f7570ed92886e97629116fec98c1c4dcf95f52..e876929be3df7ba7251886a56acf9b84bbe62e32 100644 ---- a/content/browser/web_contents/web_contents_impl.h -+++ b/content/browser/web_contents/web_contents_impl.h -@@ -450,7 +450,9 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, - void SystemDragEnded(RenderWidgetHost* source_rwh) override; - void SetClosedByUserGesture(bool value) override; - bool GetClosedByUserGesture() override; -+ void SetMinimumZoomPercent(int zoom_percent) override; - int GetMinimumZoomPercent() override; -+ void SetMaximumZoomPercent(int zoom_percent) override; - int GetMaximumZoomPercent() override; - void SetPageScale(float page_scale_factor) override; - gfx::Size GetPreferredSize() override; -@@ -1714,8 +1716,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents, - bool closed_by_user_gesture_; - - // Minimum/maximum zoom percent. -- const int minimum_zoom_percent_; -- const int maximum_zoom_percent_; -+ int minimum_zoom_percent_; -+ int maximum_zoom_percent_; - - // Used to correctly handle integer zooming through a smooth scroll device. - float zoom_scroll_remainder_; -diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h -index 94a5b87ca72998729f1fca14f247914bb81d60d1..3af575d51d0a36f9586a3cfc00575e83e70a46b9 100644 ---- a/content/public/browser/web_contents.h -+++ b/content/public/browser/web_contents.h -@@ -808,8 +808,10 @@ class WebContents : public PageNavigator, - virtual void SetClosedByUserGesture(bool value) = 0; - virtual bool GetClosedByUserGesture() = 0; - -- // Gets the minimum/maximum zoom percent. -+ // Gets/sets the minimum/maximum zoom percent. -+ virtual void SetMinimumZoomPercent(int zoom_percent) = 0; - virtual int GetMinimumZoomPercent() = 0; -+ virtual void SetMaximumZoomPercent(int zoom_percent) = 0; - virtual int GetMaximumZoomPercent() = 0; - - // Set the renderer's page scale to the given factor. diff --git a/patches/chromium/allow_new_privileges_in_unsandboxed_child_processes.patch b/patches/chromium/allow_new_privileges_in_unsandboxed_child_processes.patch index ea300c62f925b..377b1eda364a1 100644 --- a/patches/chromium/allow_new_privileges_in_unsandboxed_child_processes.patch +++ b/patches/chromium/allow_new_privileges_in_unsandboxed_child_processes.patch @@ -6,10 +6,10 @@ Subject: allow new privileges in unsandboxed child processes This allows unsandboxed renderers to launch setuid processes on Linux. diff --git a/content/browser/child_process_launcher_helper_linux.cc b/content/browser/child_process_launcher_helper_linux.cc -index 720b92a1a3a7ab5512f839005b272e4989d2ac65..b1759109627cd00053489dcdd397e942fa9d289f 100644 +index ea5258a03f83bcbda7bae296392a46cdd63d6fe1..14eed861e8828fd2ed7d8f27f9fd3bf180d04677 100644 --- a/content/browser/child_process_launcher_helper_linux.cc +++ b/content/browser/child_process_launcher_helper_linux.cc -@@ -54,6 +54,18 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread( +@@ -53,6 +53,18 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread( const int sandbox_fd = SandboxHostLinux::GetInstance()->GetChildSocket(); options->fds_to_remap.push_back( std::make_pair(sandbox_fd, service_manager::GetSandboxFD())); diff --git a/patches/chromium/blink-worker-enable-csp-in-file-scheme.patch b/patches/chromium/blink-worker-enable-csp-in-file-scheme.patch index 6e3e453034e39..15e29035c2a52 100644 --- a/patches/chromium/blink-worker-enable-csp-in-file-scheme.patch +++ b/patches/chromium/blink-worker-enable-csp-in-file-scheme.patch @@ -5,7 +5,7 @@ Subject: blink-worker-enable-csp-in-file-scheme.patch diff --git a/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc b/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc -index b1b9f451951bb7b11eefe6453a71a6b44be838a5..0f62ba09ef99538a5b0b306f601d10c44a7a7abf 100644 +index 1f8273a782a5d3580e139d724ce8a791a100dcfb..90ad0d663003c0bded47183b61bff71a0c01a196 100644 --- a/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc +++ b/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc @@ -308,7 +308,6 @@ void WorkerClassicScriptLoader::ProcessContentSecurityPolicy( diff --git a/patches/chromium/blink_file_path.patch b/patches/chromium/blink_file_path.patch index 31894d851920c..4555ff27ed7b7 100644 --- a/patches/chromium/blink_file_path.patch +++ b/patches/chromium/blink_file_path.patch @@ -7,10 +7,10 @@ This is used by editors to obtain the filesystem path from a dragged file. See documentation at https://electronjs.org/docs/api/file-object diff --git a/third_party/blink/renderer/core/fileapi/file.h b/third_party/blink/renderer/core/fileapi/file.h -index 05e05fb1631851009897f3789d3e770475ce1363..e2ad5fef884532847d035430c14c25e042e2ab24 100644 +index 7ea8ad8fd162fd6a97052188a8e8868dab1c1fe9..a64966e2db0e2e7b0390a322525522e05ddbb022 100644 --- a/third_party/blink/renderer/core/fileapi/file.h +++ b/third_party/blink/renderer/core/fileapi/file.h -@@ -201,6 +201,9 @@ class CORE_EXPORT File final : public Blob { +@@ -193,6 +193,9 @@ class CORE_EXPORT File final : public Blob { } const String& name() const { return name_; } @@ -21,7 +21,7 @@ index 05e05fb1631851009897f3789d3e770475ce1363..e2ad5fef884532847d035430c14c25e0 // http://dev.w3.org/2006/webapi/FileAPI/#file-attrs int64_t lastModified() const; diff --git a/third_party/blink/renderer/core/fileapi/file.idl b/third_party/blink/renderer/core/fileapi/file.idl -index 41b8183e858b6d955bacbb72281a08e750b6b29f..0db894ecfae63795724335d914f9f992c0755fd1 100644 +index e5cf6cc926ff2670f713018dd750e79c53ee76eb..60eca3cf100b31c3d2c0c68271d98c60321e69a7 100644 --- a/third_party/blink/renderer/core/fileapi/file.idl +++ b/third_party/blink/renderer/core/fileapi/file.idl @@ -32,6 +32,7 @@ diff --git a/patches/chromium/blink_initialization_order.patch b/patches/chromium/blink_initialization_order.patch index 7b137190bfa7d..83ac31b3bb52d 100644 --- a/patches/chromium/blink_initialization_order.patch +++ b/patches/chromium/blink_initialization_order.patch @@ -10,10 +10,10 @@ to fix electron/electron#13787. The backport landed in Chromium 67 but the DidCreateScriptContext re-ordering needs to be upstreamed or kept indefinitely diff --git a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc -index 4eb7d11483b55ef99758aa6782b3516fef706628..c384a9707d0f998b5e8bc80ad9af1ca29d9ef58c 100644 +index 8095edd988c171a8ad7382d1c2f9fa63ee9e5ffc..802316843c6c270efb871a5d697c4e0d182605d0 100644 --- a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc +++ b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc -@@ -210,11 +210,10 @@ void LocalWindowProxy::Initialize() { +@@ -227,11 +227,10 @@ void LocalWindowProxy::Initialize() { GetFrame()->IsMainFrame()); MainThreadDebugger::Instance()->ContextCreated(script_state_, GetFrame(), origin); diff --git a/patches/chromium/blink_local_frame.patch b/patches/chromium/blink_local_frame.patch index c69553951b524..f6d620c5de139 100644 --- a/patches/chromium/blink_local_frame.patch +++ b/patches/chromium/blink_local_frame.patch @@ -14,10 +14,10 @@ when there is code doing that. This patch reverts the change to fix the crash in Electron. diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc -index 0c69b3af9d2144b5d4d61e2d4add7edec839d0b5..acfba2544f6329348f60dd92ca5d9865960db371 100644 +index 0aa1376a9483ec93ecb6763295b3664105f106a4..c1045707d14051e9f990f571a3fe0aa708c5fb74 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc -@@ -325,10 +325,6 @@ void LocalFrame::DetachImpl(FrameDetachType type) { +@@ -336,10 +336,6 @@ void LocalFrame::DetachImpl(FrameDetachType type) { } CHECK(!view_ || !view_->IsAttached()); @@ -28,7 +28,7 @@ index 0c69b3af9d2144b5d4d61e2d4add7edec839d0b5..acfba2544f6329348f60dd92ca5d9865 if (!Client()) return; -@@ -346,6 +342,10 @@ void LocalFrame::DetachImpl(FrameDetachType type) { +@@ -357,6 +353,10 @@ void LocalFrame::DetachImpl(FrameDetachType type) { // Notify ScriptController that the frame is closing, since its cleanup ends // up calling back to LocalFrameClient via WindowProxy. GetScriptController().ClearForClose(); diff --git a/patches/chromium/blink_world_context.patch b/patches/chromium/blink_world_context.patch index eec47c64f99bb..1ef08fc0efaa7 100644 --- a/patches/chromium/blink_world_context.patch +++ b/patches/chromium/blink_world_context.patch @@ -5,10 +5,10 @@ Subject: blink_world_context.patch diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h -index 900c1e97bd5b8ad113def9001be495cae94bbcdf..6cec8d0bdfce94fd3980eb7509dfced79903f0d6 100644 +index 05a33b9a2cbed57e4e037428397b59f30f83ce66..0cd308b38a158a016b099beed6a31fa1c5a487ad 100644 --- a/third_party/blink/public/web/web_local_frame.h +++ b/third_party/blink/public/web/web_local_frame.h -@@ -356,6 +356,9 @@ class WebLocalFrame : public WebFrame { +@@ -341,6 +341,9 @@ class WebLocalFrame : public WebFrame { // be calling this API. virtual v8::Local MainWorldScriptContext() const = 0; @@ -19,10 +19,10 @@ index 900c1e97bd5b8ad113def9001be495cae94bbcdf..6cec8d0bdfce94fd3980eb7509dfced7 // that the script evaluated to with callback. Script execution can be // suspend. diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc -index bb640f13de34de5d966d97cf617faa3cb3fd54c2..686cddd0755ee219eaf367b3ce84320aeeb5053d 100644 +index 8c984ae92de2a53e59782bea94a143ee7fde2248..105e1e92002adde207db5c770197547ab6b55f97 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc -@@ -879,6 +879,13 @@ v8::Local WebLocalFrameImpl::GlobalProxy() const { +@@ -935,6 +935,13 @@ v8::Local WebLocalFrameImpl::GlobalProxy() const { return MainWorldScriptContext()->Global(); } @@ -37,15 +37,15 @@ index bb640f13de34de5d966d97cf617faa3cb3fd54c2..686cddd0755ee219eaf367b3ce84320a return BindingSecurity::ShouldAllowAccessToFrame( CurrentDOMWindow(V8PerIsolateData::MainThreadIsolate()), diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h -index ab52cef219785766b1315ec7e17d15c2ba2d6138..cf12b32172a0dc814a1478c275d1b5c067d5eaeb 100644 +index e3d4a369e6708827497b7ef8ce38817bde8c8010..ad7fd6c34f10d10257ef5c6e5b4b916dc924f20c 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h -@@ -150,6 +150,8 @@ class CORE_EXPORT WebLocalFrameImpl final +@@ -171,6 +171,8 @@ class CORE_EXPORT WebLocalFrameImpl final int argc, v8::Local argv[]) override; v8::Local MainWorldScriptContext() const override; + v8::Local WorldScriptContext( + v8::Isolate* isolate, int world_id) const override; - v8::Local GlobalProxy() const override; - void StartReload(WebFrameLoadType) override; - void ReloadImage(const WebNode&) override; + void RequestExecuteScriptAndReturnValue(const WebScriptSource&, + bool user_gesture, + WebScriptExecutionCallback*) override; diff --git a/patches/chromium/build_add_electron_tracing_category.patch b/patches/chromium/build_add_electron_tracing_category.patch index b2a02cd8f7542..32480c7e0ed3e 100644 --- a/patches/chromium/build_add_electron_tracing_category.patch +++ b/patches/chromium/build_add_electron_tracing_category.patch @@ -8,10 +8,10 @@ categories in use are known / declared. This patch is required for us to introduce a new Electron category for Electron-specific tracing. diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h -index dcb32a5eadb7fc3c431099107bdfdfba8764a187..e1a343fdbdc6098189bce9fc1d81e6df5555e7ab 100644 +index 1ac3513eb4f74bddacae224c1f0fdd1932151224..6ed8074d16580daa48d443653ac21984981aa8e2 100644 --- a/base/trace_event/builtin_categories.h +++ b/base/trace_event/builtin_categories.h -@@ -63,6 +63,7 @@ +@@ -66,6 +66,7 @@ X("dwrite") \ X("DXVA Decoding") \ X("EarlyJava") \ diff --git a/patches/chromium/build_fix_when_building_with_enable_plugins_false.patch b/patches/chromium/build_fix_when_building_with_enable_plugins_false.patch deleted file mode 100644 index 7c7d3ce54d20f..0000000000000 --- a/patches/chromium/build_fix_when_building_with_enable_plugins_false.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: deepak1556 -Date: Tue, 8 Oct 2019 15:40:50 +0000 -Subject: build: fix when building with enable_plugins=false - -Bug: none -Change-Id: If878b3a7f5bb051c6e99c617418475c12754ae90 -Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1845624 -Reviewed-by: Robert Sesek -Commit-Queue: Robert Sesek -Cr-Commit-Position: refs/heads/master@{#703739} - -diff --git a/AUTHORS b/AUTHORS -index 6714e69599bc87c47663bec740b91e352ef92780..41aa5584412e4a325d2360691b00c43d7b08f1bb 100644 ---- a/AUTHORS -+++ b/AUTHORS -@@ -223,6 +223,7 @@ Debashish Samantaray - Debug Wang - Deepak Dilip Borade - Deepak Mittal -+Deepak Mohan - Deepak Sharma - Deepak Singla - Deokjin Kim -diff --git a/content/browser/sandbox_parameters_mac.mm b/content/browser/sandbox_parameters_mac.mm -index 5eead918eb9d9df03c86b5201b3f924643707f4b..a12cc8734c45a4ebb29672306da3a695a883eb9b 100644 ---- a/content/browser/sandbox_parameters_mac.mm -+++ b/content/browser/sandbox_parameters_mac.mm -@@ -25,12 +25,16 @@ - #include "content/public/common/content_client.h" - #include "content/public/common/content_features.h" - #include "content/public/common/content_switches.h" --#include "content/public/common/pepper_plugin_info.h" -+#include "ppapi/buildflags/buildflags.h" - #include "sandbox/mac/seatbelt_exec.h" - #include "services/service_manager/sandbox/mac/sandbox_mac.h" - #include "services/service_manager/sandbox/sandbox_type.h" - #include "services/service_manager/sandbox/switches.h" - -+#if BUILDFLAG(ENABLE_PLUGINS) -+#include "content/public/common/pepper_plugin_info.h" -+#endif -+ - namespace content { - - namespace { -@@ -148,6 +152,7 @@ void SetupNetworkSandboxParameters(sandbox::SeatbeltExecClient* client) { - } - } - -+#if BUILDFLAG(ENABLE_PLUGINS) - void SetupPPAPISandboxParameters(sandbox::SeatbeltExecClient* client) { - SetupCommonSandboxParameters(client); - -@@ -172,6 +177,7 @@ void SetupPPAPISandboxParameters(sandbox::SeatbeltExecClient* client) { - // to n+1 more than the plugins added. - CHECK(index <= 5); - } -+#endif - - void SetupCDMSandboxParameters(sandbox::SeatbeltExecClient* client) { - SetupCommonSandboxParameters(client); -@@ -212,9 +218,11 @@ void SetupSandboxParameters(service_manager::SandboxType sandbox_type, - case service_manager::SANDBOX_TYPE_NETWORK: - SetupNetworkSandboxParameters(client); - break; -+#if BUILDFLAG(ENABLE_PLUGINS) - case service_manager::SANDBOX_TYPE_PPAPI: - SetupPPAPISandboxParameters(client); - break; -+#endif - case service_manager::SANDBOX_TYPE_PROFILING: - case service_manager::SANDBOX_TYPE_UTILITY: - SetupUtilitySandboxParameters(client, command_line); diff --git a/patches/chromium/build_win_disable_zc_twophase.patch b/patches/chromium/build_win_disable_zc_twophase.patch deleted file mode 100644 index 4fd433a7c4118..0000000000000 --- a/patches/chromium/build_win_disable_zc_twophase.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: deepak1556 -Date: Tue, 16 Jul 2019 14:38:52 -0700 -Subject: build_win_disable_zc_twophase.patch - -Temporarily disable /Zc:twoPhase when libcxx is not used on -Windows. This is to workaround the bug in crbug.com/969698#c10 -until fixes have landed in upstream. - -diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn -index a3a164b41b45381061c7fceb7d7ec1fdb4907792..8de0cb6a8d1987f84aaca5c3bb30ead0a2019bef 100644 ---- a/build/config/win/BUILD.gn -+++ b/build/config/win/BUILD.gn -@@ -2,6 +2,7 @@ - # Use of this source code is governed by a BSD-style license that can be - # found in the LICENSE file. - -+import("//build/config/c++/c++.gni") - import("//build/config/chrome_build.gni") - import("//build/config/clang/clang.gni") - import("//build/config/compiler/compiler.gni") -@@ -61,9 +62,13 @@ config("compiler") { - "/FS", # Preserve previous PDB behavior. - "/bigobj", # Some of our files are bigger than the regular limits. - "/utf-8", # Assume UTF-8 by default to avoid code page dependencies. -- "/Zc:twoPhase", - ] - -+ if (use_custom_libcxx) { -+ # Work around crbug.com/969698#c6, bug in MSSTL . -+ cflags += [ "/Zc:twoPhase" ] -+ } -+ - # Force C/C++ mode for the given GN detected file type. This is necessary - # for precompiled headers where the same source file is compiled in both - # modes. diff --git a/patches/chromium/can_create_window.patch b/patches/chromium/can_create_window.patch index 22f266df2a042..d737f3e55aec7 100644 --- a/patches/chromium/can_create_window.patch +++ b/patches/chromium/can_create_window.patch @@ -5,10 +5,10 @@ Subject: can_create_window.patch diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc -index d6d88e7b66f2de4b6733ea454fc45bb804f13a0e..30e9eabf785a0f241e9581ea38f55dd779acd8ce 100644 +index 050531a82b48797012adbf6fe49ab5741a38d989..8a02ff83c8e40d641530211ef944b62b09a03ac8 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc -@@ -4054,6 +4054,7 @@ void RenderFrameHostImpl::CreateNewWindow( +@@ -4379,6 +4379,7 @@ void RenderFrameHostImpl::CreateNewWindow( last_committed_origin_, params->window_container_type, params->target_url, params->referrer.To(), params->frame_name, params->disposition, *params->features, @@ -17,10 +17,10 @@ index d6d88e7b66f2de4b6733ea454fc45bb804f13a0e..30e9eabf785a0f241e9581ea38f55dd7 &no_javascript_access); diff --git a/content/common/frame.mojom b/content/common/frame.mojom -index f0d9cafca7efb0ae0440619b821b1976d8460396..e34e0e6387b907960d234da93a3e90a390488d02 100644 +index 822413b2824cc25ea5c699056a22623b7ac1f92a..79444323a6c8c7ab7e2abf0bd45b59e2699def31 100644 --- a/content/common/frame.mojom +++ b/content/common/frame.mojom -@@ -313,6 +313,10 @@ struct CreateNewWindowParams { +@@ -301,6 +301,10 @@ struct CreateNewWindowParams { // The window features to use for the new window. blink.mojom.WindowFeatures features; @@ -32,10 +32,10 @@ index f0d9cafca7efb0ae0440619b821b1976d8460396..e34e0e6387b907960d234da93a3e90a3 // Operation result when the renderer asks the browser to create a new window. diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc -index a9fddd7dd2d3f5c07a3b15925209a396c9aa096d..da7eca5f443e2ecdcb3f0819848735aafeb43a00 100644 +index bcb85464359ce2f1c2b46e7bd86f71cbeaebb25d..53bafb5b97034078784f8ebb43212b792f6a5d17 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc -@@ -502,6 +502,8 @@ bool ContentBrowserClient::CanCreateWindow( +@@ -514,6 +514,8 @@ bool ContentBrowserClient::CanCreateWindow( const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, @@ -45,18 +45,18 @@ index a9fddd7dd2d3f5c07a3b15925209a396c9aa096d..da7eca5f443e2ecdcb3f0819848735aa bool opener_suppressed, bool* no_javascript_access) { diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index 4e18dfd0dc8d4be9b59f12e7010b7f2be70d6cf0..07fa16953d6167a5a381b28a5da3c08a78de4306 100644 +index b70473936db044506e6d7ada4520920f0774eff7..fa942dd135ecfc6fe735b4ae52410ec00b6098af 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h -@@ -178,6 +178,7 @@ class RenderFrameHost; - class RenderProcessHost; - class RenderViewHost; - class ResourceContext; +@@ -134,6 +134,7 @@ class NetworkService; + class TrustedURLLoaderHeaderClient; + } // namespace mojom + struct ResourceRequest; +class ResourceRequestBody; - class SerialDelegate; - class SiteInstance; - class SpeechRecognitionManagerDelegate; -@@ -801,6 +802,8 @@ class CONTENT_EXPORT ContentBrowserClient { + } // namespace network + + namespace rappor { +@@ -833,6 +834,8 @@ class CONTENT_EXPORT ContentBrowserClient { const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, @@ -66,7 +66,7 @@ index 4e18dfd0dc8d4be9b59f12e7010b7f2be70d6cf0..07fa16953d6167a5a381b28a5da3c08a bool opener_suppressed, bool* no_javascript_access); diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc -index 4a3c760000fca5f53244a03b9a2074991bc0c382..c5ab8ad81bf11d0ec151b9585a4f1a7f5397ae4b 100644 +index 7bf0464c14f81b5c607c541f8ec7e5b4c860a25c..c90fd39058e3e0ab98727a71836e549ee794f7bd 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -71,6 +71,7 @@ @@ -75,9 +75,9 @@ index 4a3c760000fca5f53244a03b9a2074991bc0c382..c5ab8ad81bf11d0ec151b9585a4f1a7f #include "content/renderer/loader/request_extra_data.h" +#include "content/renderer/loader/web_url_request_util.h" #include "content/renderer/media/audio/audio_device_factory.h" - #include "content/renderer/media/webrtc/peer_connection_dependency_factory.h" - #include "content/renderer/media/webrtc/rtc_peer_connection_handler.h" -@@ -1367,6 +1368,8 @@ WebView* RenderViewImpl::CreateView( + #include "content/renderer/render_frame_impl.h" + #include "content/renderer/render_frame_proxy.h" +@@ -1336,6 +1337,8 @@ WebView* RenderViewImpl::CreateView( } params->features = ConvertWebWindowFeaturesToMojoWindowFeatures(features); @@ -87,10 +87,10 @@ index 4a3c760000fca5f53244a03b9a2074991bc0c382..c5ab8ad81bf11d0ec151b9585a4f1a7f // moved on send. bool is_background_tab = diff --git a/content/shell/browser/web_test/web_test_content_browser_client.cc b/content/shell/browser/web_test/web_test_content_browser_client.cc -index 5faaea374484d51b1af261ff93fb96aec2eaa4e1..786d3a904a765b5cb53859c35a045b9e04b6250b 100644 +index b02eb3cef899fd5bd2c8c38fc77039aa240f8a7a..0e2d1e3c50af833ec60e3c49df45da7c4782ee74 100644 --- a/content/shell/browser/web_test/web_test_content_browser_client.cc +++ b/content/shell/browser/web_test/web_test_content_browser_client.cc -@@ -312,6 +312,8 @@ bool WebTestContentBrowserClient::CanCreateWindow( +@@ -330,6 +330,8 @@ bool WebTestContentBrowserClient::CanCreateWindow( const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, @@ -100,10 +100,10 @@ index 5faaea374484d51b1af261ff93fb96aec2eaa4e1..786d3a904a765b5cb53859c35a045b9e bool opener_suppressed, bool* no_javascript_access) { diff --git a/content/shell/browser/web_test/web_test_content_browser_client.h b/content/shell/browser/web_test/web_test_content_browser_client.h -index 435f204a364e54b59c0bd651b760bf350ea94d11..1e6bee1fa08fa2d2a396678838d475e0e7e4ce0c 100644 +index 3b30376feb2d32418978973316ad348ce4973ab1..508381ca134169d0bfe222bcf250ba1d7ca63dee 100644 --- a/content/shell/browser/web_test/web_test_content_browser_client.h +++ b/content/shell/browser/web_test/web_test_content_browser_client.h -@@ -68,6 +68,8 @@ class WebTestContentBrowserClient : public ShellContentBrowserClient { +@@ -69,6 +69,8 @@ class WebTestContentBrowserClient : public ShellContentBrowserClient { const std::string& frame_name, WindowOpenDisposition disposition, const blink::mojom::WindowFeatures& features, diff --git a/patches/chromium/cherry-pick-38990b7d56e6.patch b/patches/chromium/cherry-pick-38990b7d56e6.patch new file mode 100644 index 0000000000000..96732e357c7fe --- /dev/null +++ b/patches/chromium/cherry-pick-38990b7d56e6.patch @@ -0,0 +1,77 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Marijn Kruisselbrink +Date: Tue, 21 Apr 2020 23:51:25 +0000 +Subject: Fix bug when BytesProvider replies with invalid data. + +Bug: 1072983 +Change-Id: Ideaa0a67680375e770995880a4b8d2014b51d642 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2159583 +Reviewed-by: enne +Commit-Queue: Marijn Kruisselbrink +Cr-Commit-Position: refs/heads/master@{#761203} + +diff --git a/storage/browser/blob/blob_registry_impl.cc b/storage/browser/blob/blob_registry_impl.cc +index 1cc34f91e01530754635a27cd6481e367b0a4d4e..b6a12a5c43d0129f6c1dcfe93fbd684ba4f5dc2e 100644 +--- a/storage/browser/blob/blob_registry_impl.cc ++++ b/storage/browser/blob/blob_registry_impl.cc +@@ -432,6 +432,10 @@ void BlobRegistryImpl::BlobUnderConstruction::TransportComplete( + // try to delete |this| again afterwards. + auto weak_this = weak_ptr_factory_.GetWeakPtr(); + ++ // Store the bad_message_callback_, so we can invoke it if needed, after ++ // notifying about the blob being finished. ++ auto bad_message_callback = std::move(bad_message_callback_); ++ + // The blob might no longer have any references, in which case it may no + // longer exist. If that happens just skip calling Complete. + // TODO(mek): Stop building sooner if a blob is no longer referenced. +@@ -445,7 +449,7 @@ void BlobRegistryImpl::BlobUnderConstruction::TransportComplete( + // BlobTransportStrategy might have already reported a BadMessage on the + // BytesProvider binding, but just to be safe, also report one on the + // BlobRegistry binding itself. +- std::move(bad_message_callback_) ++ std::move(bad_message_callback) + .Run("Received invalid data while transporting blob"); + } + if (weak_this) +diff --git a/storage/browser/blob/blob_registry_impl_unittest.cc b/storage/browser/blob/blob_registry_impl_unittest.cc +index 8cbcc69ec6dc6bd368fdb59a3797e412b286e342..2060676987e267a0d55878a27f6a1b53fe6790b1 100644 +--- a/storage/browser/blob/blob_registry_impl_unittest.cc ++++ b/storage/browser/blob/blob_registry_impl_unittest.cc +@@ -770,6 +770,36 @@ TEST_F(BlobRegistryImplTest, Register_ValidBytesAsReply) { + EXPECT_EQ(0u, BlobsUnderConstruction()); + } + ++TEST_F(BlobRegistryImplTest, Register_InvalidBytesAsReply) { ++ const std::string kId = "id"; ++ const std::string kData = "hello"; ++ ++ std::vector elements; ++ elements.push_back( ++ blink::mojom::DataElement::NewBytes(blink::mojom::DataElementBytes::New( ++ kData.size(), base::nullopt, CreateBytesProvider("")))); ++ ++ mojo::PendingRemote blob; ++ EXPECT_TRUE(registry_->Register(blob.InitWithNewPipeAndPassReceiver(), kId, ++ "", "", std::move(elements))); ++ ++ std::unique_ptr handle = context_->GetBlobDataFromUUID(kId); ++ WaitForBlobCompletion(handle.get()); ++ ++ EXPECT_TRUE(handle->IsBroken()); ++ ASSERT_EQ(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, ++ handle->GetBlobStatus()); ++ ++ EXPECT_EQ(1u, reply_request_count_); ++ EXPECT_EQ(0u, stream_request_count_); ++ EXPECT_EQ(0u, file_request_count_); ++ EXPECT_EQ(0u, BlobsUnderConstruction()); ++ ++ // Expect 2 bad messages, one for the bad reply by the bytes provider, and one ++ // for the original register call. ++ EXPECT_EQ(2u, bad_messages_.size()); ++} ++ + TEST_F(BlobRegistryImplTest, Register_ValidBytesAsStream) { + const std::string kId = "id"; + const std::string kData = diff --git a/patches/chromium/cherry-pick-67864c214770.patch b/patches/chromium/cherry-pick-67864c214770.patch new file mode 100644 index 0000000000000..d49fbd08fee5d --- /dev/null +++ b/patches/chromium/cherry-pick-67864c214770.patch @@ -0,0 +1,82 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Daniel Cheng +Date: Fri, 10 Apr 2020 00:43:45 +0000 +Subject: Use std::deque to store the stack of currently executing tasks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The stack of currently executing stacks includes a PendingTask field. A +pointer to this field is stored in TLS. However, std::vector does not +guarantee pointer stability on resize. + +(cherry picked from commit c34431a597aba8f4374975217d97a73eaf7d1f18) + +Bug: 1064891 +Change-Id: I04eb06c9521722f08fd72826f552cedaffe61b53 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2146349 +Commit-Queue: Daniel Cheng +Reviewed-by: Sami Kyöstilä +Reviewed-by: François Doray +Cr-Original-Commit-Position: refs/heads/master@{#759017} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2158048 +Cr-Commit-Position: refs/branch-heads/4044@{#970} +Cr-Branched-From: a6d9daf149a473ceea37f629c41d4527bf2055bd-refs/heads/master@{#737173} + +diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc +index f719a33fff0c98dc7113b4b094bc46809dc66c00..ed951d323a1c7025d99a04d4481abc1f68dfb017 100644 +--- a/base/task/sequence_manager/sequence_manager_impl.cc ++++ b/base/task/sequence_manager/sequence_manager_impl.cc +@@ -50,13 +50,6 @@ const scoped_refptr& GetNullTaskRunner() { + + } // namespace + +-// This controls how big the the initial for +-// |MainThreadOnly::task_execution_stack| should be. We don't expect to see +-// depths of more than 2 unless cooperative scheduling is used on Blink, where +-// we might get up to 6. Anyway 10 was chosen because it's a round number +-// greater than current anticipated usage. +-static constexpr const size_t kInitialTaskExecutionStackReserveCount = 10; +- + std::unique_ptr CreateSequenceManagerOnCurrentThread( + SequenceManager::Settings settings) { + return internal::SequenceManagerImpl::CreateOnCurrentThread( +@@ -267,7 +260,6 @@ SequenceManagerImpl::MainThreadOnly::MainThreadOnly( + random_generator = std::mt19937_64(RandUint64()); + uniform_distribution = std::uniform_real_distribution(0.0, 1.0); + } +- task_execution_stack.reserve(kInitialTaskExecutionStackReserveCount); + } + + SequenceManagerImpl::MainThreadOnly::~MainThreadOnly() = default; +diff --git a/base/task/sequence_manager/sequence_manager_impl.h b/base/task/sequence_manager/sequence_manager_impl.h +index ddd0a580eb71300f6ec68903ff1467808707ec82..e2819c68fd2f3c19940e0f26248835abd1e55fda 100644 +--- a/base/task/sequence_manager/sequence_manager_impl.h ++++ b/base/task/sequence_manager/sequence_manager_impl.h +@@ -5,6 +5,7 @@ + #ifndef BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_IMPL_H_ + #define BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_IMPL_H_ + ++#include + #include + #include + #include +@@ -12,7 +13,6 @@ + #include + #include + #include +-#include + + #include "base/atomic_sequence_num.h" + #include "base/cancelable_callback.h" +@@ -308,7 +308,9 @@ class BASE_EXPORT SequenceManagerImpl + bool nesting_observer_registered_ = false; + + // Due to nested runloops more than one task can be executing concurrently. +- std::vector task_execution_stack; ++ // Note that this uses std::deque for pointer stability, since pointers to ++ // objects in this container are stored in TLS. ++ std::deque task_execution_stack; + + Observer* observer = nullptr; // NOT OWNED + diff --git a/patches/chromium/cherry-pick-686d1bfbcb8f.patch b/patches/chromium/cherry-pick-686d1bfbcb8f.patch new file mode 100644 index 0000000000000..baef8485bf333 --- /dev/null +++ b/patches/chromium/cherry-pick-686d1bfbcb8f.patch @@ -0,0 +1,144 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Rouslan Solomakhin +Date: Wed, 15 Apr 2020 23:03:07 +0000 +Subject: Browser context owned callback. + +Before this patch, an unowned function pointer would be invoked +asynchronously with a reference to the possibly freed reference to the +browser context, which could cause use after free in certain +circumstances. + +This patch makes the browser context own the callback and binds the +function with a weak pointer, so freeing the browser context invalidates +the weak pointer, which cancels the callback execution. + +After this patch, freeing the browser context aborts the asynchronous +callback that dereferences the browser context, so the use after free +is prevented. + +TBR=rouslan@chromium.org + +(cherry picked from commit 2d0aad1e7602a7076d86772cc159b891cf2cf03b) + +Bug: 1065298 +Change-Id: Id6de3099a55c4505e94a8a6d21fb25d6d2b34c6c +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2144311 +Reviewed-by: Danyao Wang +Commit-Queue: Rouslan Solomakhin +Cr-Original-Commit-Position: refs/heads/master@{#758404} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2151474 +Reviewed-by: Rouslan Solomakhin +Cr-Commit-Position: refs/branch-heads/4044@{#942} +Cr-Branched-From: a6d9daf149a473ceea37f629c41d4527bf2055bd-refs/heads/master@{#737173} + +diff --git a/content/browser/payments/payment_app_provider_impl.cc b/content/browser/payments/payment_app_provider_impl.cc +index 3b813cb22c15e85c9e0da1b0060a4514e6522db5..83c26a3e1801dc780d83ee34136ca1513e343339 100644 +--- a/content/browser/payments/payment_app_provider_impl.cc ++++ b/content/browser/payments/payment_app_provider_impl.cc +@@ -15,6 +15,7 @@ + #include "base/metrics/histogram_macros.h" + #include "base/strings/string_number_conversions.h" + #include "base/strings/string_util.h" ++#include "base/supports_user_data.h" + #include "base/task/post_task.h" + #include "base/token.h" + #include "content/browser/payments/payment_app_context_impl.h" +@@ -432,28 +433,65 @@ void OnInstallPaymentApp( + } + } + +-void CheckPermissionForPaymentApps( +- BrowserContext* browser_context, +- PaymentAppProvider::GetAllPaymentAppsCallback callback, +- PaymentAppProvider::PaymentApps apps) { +- DCHECK_CURRENTLY_ON(BrowserThread::UI); ++// Callbacks for checking permissions asynchronously. Owned by the browser ++// context to avoid using the browser context after it has been freed. Deleted ++// after the callback is invoked. ++// Sample usage: ++// PostTask(&PermissionChecker::CheckPermissionForPaymentApps, ++// PermissionChecker::Create(browser_context), std::move(callback)); ++class PermissionChecker : public base::SupportsUserData::Data { ++ public: ++ static base::WeakPtr Create( ++ BrowserContext* browser_context) { ++ auto owned = std::make_unique(browser_context); ++ auto weak_pointer_result = owned->weak_ptr_factory_.GetWeakPtr(); ++ void* key = owned.get(); ++ browser_context->SetUserData(key, std::move(owned)); ++ return weak_pointer_result; ++ } ++ ++ // Do not use this method directly! Use the static PermissionChecker::Create() ++ // method instead. (The constructor must be public for std::make_unique<> in ++ // the Create() method.) ++ explicit PermissionChecker(BrowserContext* browser_context) ++ : browser_context_(browser_context) {} ++ ~PermissionChecker() override = default; ++ ++ // Disallow copy and assign. ++ PermissionChecker(const PermissionChecker& other) = delete; ++ PermissionChecker& operator=(const PermissionChecker& other) = delete; ++ ++ void CheckPermissionForPaymentApps( ++ PaymentAppProvider::GetAllPaymentAppsCallback callback, ++ PaymentAppProvider::PaymentApps apps) { ++ DCHECK_CURRENTLY_ON(BrowserThread::UI); + +- PermissionController* permission_controller = +- BrowserContext::GetPermissionController(browser_context); +- DCHECK(permission_controller); +- +- PaymentAppProvider::PaymentApps permitted_apps; +- for (auto& app : apps) { +- GURL origin = app.second->scope.GetOrigin(); +- if (permission_controller->GetPermissionStatus( +- PermissionType::PAYMENT_HANDLER, origin, origin) == +- blink::mojom::PermissionStatus::GRANTED) { +- permitted_apps[app.first] = std::move(app.second); ++ PermissionController* permission_controller = ++ BrowserContext::GetPermissionController(browser_context_); ++ DCHECK(permission_controller); ++ ++ PaymentAppProvider::PaymentApps permitted_apps; ++ for (auto& app : apps) { ++ GURL origin = app.second->scope.GetOrigin(); ++ if (permission_controller->GetPermissionStatus( ++ PermissionType::PAYMENT_HANDLER, origin, origin) == ++ blink::mojom::PermissionStatus::GRANTED) { ++ permitted_apps[app.first] = std::move(app.second); ++ } + } ++ ++ std::move(callback).Run(std::move(permitted_apps)); ++ ++ // Deletes this PermissionChecker object. ++ browser_context_->RemoveUserData(/*key=*/this); + } + +- std::move(callback).Run(std::move(permitted_apps)); +-} ++ private: ++ // Owns this PermissionChecker object, so it's always valid. ++ BrowserContext* browser_context_; ++ ++ base::WeakPtrFactory weak_ptr_factory_{this}; ++}; + + void AbortInvokePaymentApp(BrowserContext* browser_context, + PaymentEventResponseType reason) { +@@ -606,9 +644,11 @@ void PaymentAppProviderImpl::GetAllPaymentApps( + + RunOrPostTaskOnThread( + FROM_HERE, ServiceWorkerContext::GetCoreThreadId(), +- base::BindOnce(&GetAllPaymentAppsOnCoreThread, payment_app_context, +- base::BindOnce(&CheckPermissionForPaymentApps, +- browser_context, std::move(callback)))); ++ base::BindOnce( ++ &GetAllPaymentAppsOnCoreThread, payment_app_context, ++ base::BindOnce(&PermissionChecker::CheckPermissionForPaymentApps, ++ PermissionChecker::Create(browser_context), ++ std::move(callback)))); + } + + void PaymentAppProviderImpl::InvokePaymentApp( diff --git a/patches/chromium/cherry-pick-7101418f85a0.patch b/patches/chromium/cherry-pick-7101418f85a0.patch new file mode 100644 index 0000000000000..dce88f303ec9c --- /dev/null +++ b/patches/chromium/cherry-pick-7101418f85a0.patch @@ -0,0 +1,191 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mason Freed +Date: Mon, 20 Apr 2020 21:57:52 +0000 +Subject: Fix customized built-in element constructor behavior + +This CL implements two changes: + 1. It fixes the implementation to better match the spec for the + "create an element for the token" [1] algorithm. Prior to this CL, + step 7 of that algorithm was skipping directly to step 6 of the + "create an element" [2] algorithm, skipping over step 5 for + customized built-in elements. This is now fixed. This case is + illustrated by the issue and example at [3] and [4]. This becomes + the first test in customized-built-in-constructor-exceptions.html. + + 2. It updates the comments to match the new behavior discussed in [3] + and the [5] spec PR, which changes the return value in the case + that a customized built-in element constructor throws an exception. + With the change above, that is actually already the behavior. So + this is just a comment change. Two new tests are added to + customized-built-in-constructor-exceptions.html. + +[1] https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token +[2] https://dom.spec.whatwg.org/#concept-create-element +[3] https://github.com/whatwg/html/issues/5084 +[4] https://crbug.com/1024866 +[5] https://github.com/whatwg/dom/pull/797 + +Bug: 1071059, 1024866 +Change-Id: I814c81991eb5e83501304bcb3d2da476743aef52 +Cq-Do-Not-Cancel-Tryjobs: true +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2152986 +Commit-Queue: Mason Freed +Auto-Submit: Mason Freed +Reviewed-by: Kent Tamura +Cr-Commit-Position: refs/heads/master@{#760705} + +diff --git a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc +index 075b20837c75f736d4580e2b17b0568e0c36e48c..363ccb5f7c919ee1990b27684bc89c1cd4a805af 100644 +--- a/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc ++++ b/third_party/blink/renderer/bindings/core/v8/script_custom_element_definition.cc +@@ -137,6 +137,7 @@ HTMLElement* ScriptCustomElementDefinition::HandleCreateElementSyncException( + HTMLElement* ScriptCustomElementDefinition::CreateAutonomousCustomElementSync( + Document& document, + const QualifiedName& tag_name) { ++ DCHECK(CustomElement::ShouldCreateCustomElement(tag_name)) << tag_name; + if (!script_state_->ContextIsValid()) + return CustomElement::CreateFailedElement(document, tag_name); + ScriptState::Scope scope(script_state_); +diff --git a/third_party/blink/renderer/core/html/custom/custom_element.cc b/third_party/blink/renderer/core/html/custom/custom_element.cc +index d21c582052cbf861b792ec6f44e418cc25f3a8cc..1edcd4e60009c9f994c0711a8d373d771b4e8965 100644 +--- a/third_party/blink/renderer/core/html/custom/custom_element.cc ++++ b/third_party/blink/renderer/core/html/custom/custom_element.cc +@@ -205,7 +205,8 @@ Element* CustomElement::CreateUncustomizedOrUndefinedElement( + + HTMLElement* CustomElement::CreateFailedElement(Document& document, + const QualifiedName& tag_name) { +- DCHECK(ShouldCreateCustomElement(tag_name)); ++ CHECK(ShouldCreateCustomElement(tag_name)) ++ << "HTMLUnknownElement with built-in tag name: " << tag_name; + + // "create an element for a token": + // https://html.spec.whatwg.org/C/#create-an-element-for-the-token +diff --git a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc +index 6126e0d04c48e8f7cebe8dbf39418599fcfcb84e..3341f49f48de367cbe4f736c4acac09c5d59ddbd 100644 +--- a/third_party/blink/renderer/core/html/custom/custom_element_definition.cc ++++ b/third_party/blink/renderer/core/html/custom/custom_element_definition.cc +@@ -142,14 +142,19 @@ HTMLElement* CustomElementDefinition::CreateElement( + result->SetCustomElementState(CustomElementState::kUndefined); + result->SetIsValue(Descriptor().GetName()); + +- // 5.3. If the synchronous custom elements flag is set, upgrade +- // element using definition. +- // 5.4. Otherwise, enqueue a custom element upgrade reaction given +- // result and definition. +- if (!flags.IsAsyncCustomElements()) ++ if (!flags.IsAsyncCustomElements()) { ++ // 5.3 If the synchronous custom elements flag is set, then run this step ++ // while catching any exceptions: ++ // 1. Upgrade element using definition. ++ // If this step threw an exception, then: ++ // 1. Report the exception. ++ // 2. Set result's custom element state to "failed". + Upgrade(*result); +- else ++ } else { ++ // 5.4. Otherwise, enqueue a custom element upgrade reaction given ++ // result and definition. + EnqueueUpgradeReaction(*result); ++ } + return To(result); + } + +diff --git a/third_party/blink/renderer/core/html/parser/html_construction_site.cc b/third_party/blink/renderer/core/html/parser/html_construction_site.cc +index e5b8596cb60c8baf9c6d1a18d0c788985f8790b8..e98bb816eaa6f63b672a69425ea338e9521713d0 100644 +--- a/third_party/blink/renderer/core/html/parser/html_construction_site.cc ++++ b/third_party/blink/renderer/core/html/parser/html_construction_site.cc +@@ -919,8 +919,11 @@ Element* HTMLConstructionSite::CreateElement( + // reactions stack." + CEReactionsScope reactions; + +- // 7. +- element = definition->CreateAutonomousCustomElementSync(document, tag_name); ++ // 7. Let element be the result of creating an element given document, ++ // localName, given namespace, null, and is. If will execute script is true, ++ // set the synchronous custom elements flag; otherwise, leave it unset. ++ element = ++ definition->CreateElement(document, tag_name, GetCreateElementFlags()); + + // "8. Append each attribute in the given token to element." We don't use + // setAttributes here because the custom element constructor may have +diff --git a/third_party/blink/web_tests/external/wpt/custom-elements/customized-built-in-constructor-exceptions.html b/third_party/blink/web_tests/external/wpt/custom-elements/customized-built-in-constructor-exceptions.html +new file mode 100644 +index 0000000000000000000000000000000000000000..32729bdb6bb2c82f54c074c7609ff5c79883a37a +--- /dev/null ++++ b/third_party/blink/web_tests/external/wpt/custom-elements/customized-built-in-constructor-exceptions.html +@@ -0,0 +1,75 @@ ++ ++Customized built-in element constructor behavior ++ ++ ++ ++ ++ ++ ++

++ ++ ++ ++ ++ ++ ++ ++ ++ ++
++ ++ diff --git a/patches/chromium/cherry-pick-826a4af58b3d.patch b/patches/chromium/cherry-pick-826a4af58b3d.patch new file mode 100644 index 0000000000000..36a1dc33f63e0 --- /dev/null +++ b/patches/chromium/cherry-pick-826a4af58b3d.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Mustafa Emre Acer +Date: Fri, 10 Apr 2020 00:43:45 +0000 +Subject: Don't decode invalid punycode in URL formatter + +TBR=meacer@chromium.org + +(cherry picked from commit 50c6e900fc4170a14154cbfea57ade2aa50990b5) + +Bug: 1063566 +Change-Id: I631ba68718cf69c5972555d7826b089e27fa5150 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2137872 +Reviewed-by: Peter Kasting +Commit-Queue: Peter Kasting +Cr-Original-Commit-Position: refs/heads/master@{#756819} +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2144830 +Reviewed-by: Mustafa Emre Acer +Cr-Commit-Position: refs/branch-heads/4103@{#62} +Cr-Branched-From: 8ad47e8d21f6866e4a37f47d83a860d41debf514-refs/heads/master@{#756066} + +diff --git a/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc b/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc +index f695e5c403cbcb020b2ef8016fb513ba1b8d97d1..ffd06415c9afd54c008d86f817cb0e704de87601 100644 +--- a/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc ++++ b/components/url_formatter/spoof_checks/idn_spoof_checker_unittest.cc +@@ -55,6 +55,8 @@ const IDNTestCase kIdnCases[] = { + {"www.google.com.", L"www.google.com.", true}, + {".", L".", true}, + {"", L"", true}, ++ // Invalid IDN ++ {"xn--example-.com", L"xn--example-.com", false}, + // IDN + // Hanzi (Traditional Chinese) + {"xn--1lq90ic7f1rc.cn", L"\x5317\x4eac\x5927\x5b78.cn", true}, +diff --git a/components/url_formatter/url_formatter.cc b/components/url_formatter/url_formatter.cc +index 58cadb980bb877c933e37457b1ab0883357ece47..d7cc2ea441c916fb77a520f8fe440cff042b6aab 100644 +--- a/components/url_formatter/url_formatter.cc ++++ b/components/url_formatter/url_formatter.cc +@@ -408,9 +408,11 @@ bool IDNToUnicodeOneComponent(const base::char16* comp, + return false; + + // Early return if the input cannot be an IDN component. ++ // Valid punycode must not end with a dash. + static const base::char16 kIdnPrefix[] = {'x', 'n', '-', '-'}; + if (comp_len <= base::size(kIdnPrefix) || +- memcmp(comp, kIdnPrefix, sizeof(kIdnPrefix)) != 0) { ++ memcmp(comp, kIdnPrefix, sizeof(kIdnPrefix)) != 0 || ++ comp[comp_len - 1] == '-') { + out->append(comp, comp_len); + return false; + } diff --git a/patches/chromium/cherry-pick-86c02c5dcd37.patch b/patches/chromium/cherry-pick-86c02c5dcd37.patch new file mode 100644 index 0000000000000..fefd011bf5a82 --- /dev/null +++ b/patches/chromium/cherry-pick-86c02c5dcd37.patch @@ -0,0 +1,237 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Matthias=20K=C3=B6rber?= +Date: Tue, 14 Apr 2020 16:55:14 +0000 +Subject: Fixed requesting invalid country codes from CountryData. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Change-Id: Id0ce647400bdce2eb4cdd358432b7c647f880570 +Bug: 1062861 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2122057 +Reviewed-by: Matthias Körber +Reviewed-by: Dominic Battré +Reviewed-by: Vadym Doroshenko +Commit-Queue: Matthias Körber +Cr-Commit-Position: refs/heads/master@{#758887} + +diff --git a/components/autofill/core/browser/geo/autofill_country.cc b/components/autofill/core/browser/geo/autofill_country.cc +index 4eb8f2b1a609deb314efd9b37578be9fa803ac67..961b1dc7a19f23c6f2cf9f831f5d544cc19cd211 100644 +--- a/components/autofill/core/browser/geo/autofill_country.cc ++++ b/components/autofill/core/browser/geo/autofill_country.cc +@@ -25,13 +25,28 @@ const size_t kLocaleCapacity = + + AutofillCountry::AutofillCountry(const std::string& country_code, + const std::string& locale) { +- auto result = +- CountryDataMap::GetInstance()->country_data().find(country_code); +- DCHECK(result != CountryDataMap::GetInstance()->country_data().end()); +- const CountryData& data = result->second; ++ CountryDataMap* country_data_map = CountryDataMap::GetInstance(); + +- country_code_ = country_code; +- name_ = l10n_util::GetDisplayNameForCountry(country_code, locale); ++ // If the country code is an alias (e.g. "GB" for "UK") expand the country ++ // code. ++ country_code_ = country_data_map->HasCountryCodeAlias(country_code) ++ ? country_data_map->GetCountryCodeForAlias(country_code) ++ : country_code; ++ ++ // If there is no entry in the |CountryDataMap| for the ++ // |country_code_for_country_data| use the country code derived from the ++ // locale. This reverts to US. ++ country_data_map->HasCountryData(country_code_) ++ ? country_code_ ++ : CountryCodeForLocale(locale); ++ ++ // Acquire the country address data. ++ const CountryData& data = country_data_map->GetCountryData(country_code_); ++ ++ // Translate the country name by the supplied local. ++ name_ = l10n_util::GetDisplayNameForCountry(country_code_, locale); ++ ++ // Get the localized strings associate with the address fields. + postal_code_label_ = l10n_util::GetStringUTF16(data.postal_code_label_id); + state_label_ = l10n_util::GetStringUTF16(data.state_label_id); + address_required_fields_ = data.address_required_fields; +diff --git a/components/autofill/core/browser/geo/autofill_country_unittest.cc b/components/autofill/core/browser/geo/autofill_country_unittest.cc +index 8f3994de4f5d8b924cc47a989d86676ed01bd760..94315c13967f1ce640bba01a555f8b418af97ad3 100644 +--- a/components/autofill/core/browser/geo/autofill_country_unittest.cc ++++ b/components/autofill/core/browser/geo/autofill_country_unittest.cc +@@ -14,6 +14,7 @@ + #include "base/android/build_info.h" + #endif + ++using autofill::CountryDataMap; + using base::ASCIIToUTF16; + + namespace autofill { +@@ -30,6 +31,11 @@ TEST(AutofillCountryTest, AutofillCountry) { + EXPECT_EQ("US", united_states_es.country_code()); + EXPECT_EQ(ASCIIToUTF16("Estados Unidos"), united_states_es.name()); + ++ AutofillCountry great_britain_uk_alias("UK", "en_GB"); ++ EXPECT_EQ("GB", great_britain_uk_alias.country_code()); ++ EXPECT_EQ("GB", great_britain_uk_alias.country_code()); ++ EXPECT_EQ(ASCIIToUTF16("United Kingdom"), great_britain_uk_alias.name()); ++ + AutofillCountry canada_en("CA", "en_US"); + EXPECT_EQ("CA", canada_en.country_code()); + EXPECT_EQ(ASCIIToUTF16("Canada"), canada_en.name()); +@@ -74,4 +80,27 @@ TEST(AutofillCountryTest, AllCountryCodesHaveCountryName) { + } + } + ++// Test alias mappings for falsely existing country codes. ++TEST(AutofillCountryTest, AliasMappingsForCountryData) { ++ CountryDataMap* country_data_map = CountryDataMap::GetInstance(); ++ ++ // There should be country data for the "GB". ++ EXPECT_TRUE(country_data_map->HasCountryData("GB")); ++ ++ // Check the correctness of the alias definitions. ++ EXPECT_TRUE(country_data_map->HasCountryCodeAlias("UK")); ++ EXPECT_FALSE(country_data_map->HasCountryCodeAlias("does_not_exist")); ++ ++ // Query not existing mapping. ++ auto expected_country_code = std::string(); ++ auto actual_country_code = ++ country_data_map->GetCountryCodeForAlias("does_not_exist"); ++ EXPECT_EQ(expected_country_code, actual_country_code); ++ ++ // GB should map the UK. ++ expected_country_code = "GB"; ++ actual_country_code = country_data_map->GetCountryCodeForAlias("UK"); ++ EXPECT_EQ(expected_country_code, actual_country_code); ++} ++ + } // namespace autofill +diff --git a/components/autofill/core/browser/geo/country_data.cc b/components/autofill/core/browser/geo/country_data.cc +index 1fe65ecf65323ce1917cb786b63d10ea4a7ac0b7..ec78e723ca71972d32ba00c2b46de9673fe91f46 100644 +--- a/components/autofill/core/browser/geo/country_data.cc ++++ b/components/autofill/core/browser/geo/country_data.cc +@@ -19,6 +19,17 @@ struct StaticCountryData { + CountryData country_data; + }; + ++// Alias definitions record for CountryData requests. A request for ++// |country_code_alias| is served with the |CountryData| for ++// |country_code_target|. ++struct StaticCountryCodeAliasData { ++ char country_code_alias[3]; ++ char country_code_target[3]; ++}; ++ ++// Alias definitions. ++const StaticCountryCodeAliasData kCountryCodeAliases[] = {{"UK", "GB"}}; ++ + // Maps country codes to localized label string identifiers. Keep this sorted + // by country code. + // This list is comprized of countries appearing in both +@@ -790,7 +801,7 @@ std::vector GetCountryCodes() { + return country_codes; + } + +-std::map GetCountryData() { ++std::map GetCountryDataMap() { + std::map country_data; + // Add all the countries we have explicit data for. + for (const auto& static_data : kCountryData) { +@@ -813,6 +824,18 @@ std::map GetCountryData() { + return country_data; + } + ++std::map GetCountryCodeAliasMap() { ++ std::map country_code_aliases; ++ // Create mappings for the aliases defined in |kCountryCodeAliases|. ++ for (const auto& static_alias_data : kCountryCodeAliases) { ++ // Insert the alias. ++ country_code_aliases.insert( ++ std::make_pair(std::string(static_alias_data.country_code_alias), ++ std::string(static_alias_data.country_code_target))); ++ } ++ return country_code_aliases; ++} ++ + } // namespace + + // static +@@ -821,8 +844,38 @@ CountryDataMap* CountryDataMap::GetInstance() { + } + + CountryDataMap::CountryDataMap() +- : country_data_(GetCountryData()), country_codes_(GetCountryCodes()) {} ++ : country_data_(GetCountryDataMap()), ++ country_code_aliases_(GetCountryCodeAliasMap()), ++ country_codes_(GetCountryCodes()) {} + + CountryDataMap::~CountryDataMap() = default; + ++bool CountryDataMap::HasCountryData(const std::string& country_code) const { ++ return country_data_.count(country_code) > 0; ++} ++ ++const CountryData& CountryDataMap::GetCountryData( ++ const std::string& country_code) const { ++ auto lookup = country_data_.find(country_code); ++ if (lookup != country_data_.end()) ++ return lookup->second; ++ // If there is no entry for country_code return the entry for the US. ++ return country_data_.find("US")->second; ++} ++ ++bool CountryDataMap::HasCountryCodeAlias( ++ const std::string& country_code_alias) const { ++ return country_code_aliases_.count(country_code_alias) > 0; ++} ++ ++const std::string CountryDataMap::GetCountryCodeForAlias( ++ const std::string& country_code_alias) const { ++ auto lookup = country_code_aliases_.find(country_code_alias); ++ if (lookup != country_code_aliases_.end()) { ++ DCHECK(HasCountryData(lookup->second)); ++ return lookup->second; ++ } ++ return std::string(); ++} ++ + } // namespace autofill +diff --git a/components/autofill/core/browser/geo/country_data.h b/components/autofill/core/browser/geo/country_data.h +index b6a9497594b1c22a5521a9c40fab2decae449c3f..8266102deadd40e5f2b9b24703123f59034a9781 100644 +--- a/components/autofill/core/browser/geo/country_data.h ++++ b/components/autofill/core/browser/geo/country_data.h +@@ -58,10 +58,23 @@ class CountryDataMap { + public: + static CountryDataMap* GetInstance(); + +- const std::map& country_data() { +- return country_data_; +- } ++ // Returns true if a |CountryData| entry for the supplied |country_code| ++ // exists. ++ bool HasCountryData(const std::string& country_code) const; + ++ // Returns true if there is a country code alias for |country_code|. ++ bool HasCountryCodeAlias(const std::string& country_code_alias) const; ++ ++ // Returns the country code for a country code alias. If no alias definition ++ // is present return an empty string. ++ const std::string GetCountryCodeForAlias( ++ const std::string& country_code_alias) const; ++ ++ // Lookup the |CountryData| for the supplied |country_code|. If no entry ++ // exists, return the data for the US as a best guess. ++ const CountryData& GetCountryData(const std::string& country_code) const; ++ ++ // Return a constant reference to a vector of all country codes. + const std::vector& country_codes() { return country_codes_; } + + private: +@@ -70,6 +83,7 @@ class CountryDataMap { + friend struct base::DefaultSingletonTraits; + + const std::map country_data_; ++ const std::map country_code_aliases_; + const std::vector country_codes_; + + DISALLOW_COPY_AND_ASSIGN(CountryDataMap); diff --git a/patches/chromium/chore_use_electron_resources_not_chrome_for_spellchecker.patch b/patches/chromium/chore_use_electron_resources_not_chrome_for_spellchecker.patch new file mode 100644 index 0000000000000..69ba317bc8b4c --- /dev/null +++ b/patches/chromium/chore_use_electron_resources_not_chrome_for_spellchecker.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Wed, 23 Oct 2019 14:17:18 -0700 +Subject: chore: use electron resources not chrome for spellchecker + +spellchecker uses a few IDS_ resources. We need to load these from +Electrons grit header instead of Chromes + +diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn +index aefba4a5a3ca9b7cd0b91693727ea77900dc714b..c9cf6c65386b6a600dab37663f3eef411956a96d 100644 +--- a/chrome/browser/BUILD.gn ++++ b/chrome/browser/BUILD.gn +@@ -5125,6 +5125,7 @@ jumbo_static_library("browser") { + deps += [ + "//components/spellcheck/browser", + "//components/spellcheck/common", ++ "//electron:resources", + ] + + if (!is_android) { +diff --git a/chrome/browser/spellchecker/spellcheck_factory.cc b/chrome/browser/spellchecker/spellcheck_factory.cc +index 48ac0a24efde0cb7d3ba71c8b8bdf5178f606e80..e2beefc276098fdc8f1cdab2e0edb8fae4ee67ca 100644 +--- a/chrome/browser/spellchecker/spellcheck_factory.cc ++++ b/chrome/browser/spellchecker/spellcheck_factory.cc +@@ -6,7 +6,7 @@ + + #include "chrome/browser/profiles/incognito_helpers.h" + #include "chrome/browser/spellchecker/spellcheck_service.h" +-#include "chrome/grit/locale_settings.h" ++#include "electron/grit/electron_resources.h" + #include "components/keyed_service/content/browser_context_dependency_manager.h" + #include "components/pref_registry/pref_registry_syncable.h" + #include "components/prefs/pref_service.h" +diff --git a/components/language/core/browser/BUILD.gn b/components/language/core/browser/BUILD.gn +index f28230ea94fece5ae8ca7ac27a14da48f40fadbb..c10eea103852b9411a644e1fe20a7e20a38bf187 100644 +--- a/components/language/core/browser/BUILD.gn ++++ b/components/language/core/browser/BUILD.gn +@@ -31,6 +31,7 @@ static_library("browser") { + "//components/pref_registry", + "//components/prefs", + "//components/strings", ++ "//electron:resources", + "//ui/base", + ] + } +diff --git a/components/language/core/browser/language_prefs.cc b/components/language/core/browser/language_prefs.cc +index 4fd35c8d5bba01c25c906b19bb251222ba3d25b4..4770f0d6a8d145e5d16931d5caf4f1f85abead20 100644 +--- a/components/language/core/browser/language_prefs.cc ++++ b/components/language/core/browser/language_prefs.cc +@@ -21,7 +21,7 @@ + #include "components/pref_registry/pref_registry_syncable.h" + #include "components/prefs/pref_service.h" + #include "components/prefs/scoped_user_pref_update.h" +-#include "components/strings/grit/components_locale_settings.h" ++#include "electron/grit/electron_resources.h" + #include "ui/base/l10n/l10n_util.h" + + namespace language { diff --git a/patches/chromium/command-ismediakey.patch b/patches/chromium/command-ismediakey.patch index fa517840805d4..66f024d120bed 100644 --- a/patches/chromium/command-ismediakey.patch +++ b/patches/chromium/command-ismediakey.patch @@ -3,48 +3,18 @@ From: Jeremy Apthorp Date: Wed, 10 Oct 2018 15:07:34 -0700 Subject: command-ismediakey.patch -define Command::IsMediaKey on mac; copied from //chrome/common/extensions/command.cc, -which also defines a bunch of other stuff that depends on extensions. -since we only need IsMediaKey, and we don't want the extensions stuff -(and aren't compiling command.cc), it's safe to duplicate the -definition. A candidate for upstreaming would be to move the IsMediaKey -function into //ui. +Override MediaKeysListener::IsMediaKeycode to also listen for Volume Up, Volume Down, +and Mute. We also need to patch out Chromium's usage of RemoteCommandCenterDelegate, as +it uses MPRemoteCommandCenter. MPRemoteCommandCenter makes it such that GlobalShortcuts +in Electron will not work as intended, because by design an app does not receive remote +control events until it begins playing audio. This means that a media shortcut would not kick +into effect until you, for example, began playing a YouTube video which sort of defeats the +purpose of GlobalShortcuts. -Also apply electron/electron@0f67b1866a9f00b852370e721affa4efda623f3a -and electron/electron@d2368d2d3b3de9eec4cc32b6aaf035cc89921bf1 as -patches. +At the moment there is no upstream possibility for this; but perhaps Chromium may +consider some kind of switch, enabled by default, which would conditionally choose to avoid usage of +RemoteCommandCenterDelegate on macOS. -diff --git a/chrome/browser/extensions/global_shortcut_listener_mac.mm b/chrome/browser/extensions/global_shortcut_listener_mac.mm -index befe726af9c10b1563a7fc0bb77cc55f65943d5c..46c6fe08bab8471007f78d3ef227e5195bfdf0e1 100644 ---- a/chrome/browser/extensions/global_shortcut_listener_mac.mm -+++ b/chrome/browser/extensions/global_shortcut_listener_mac.mm -@@ -21,6 +21,26 @@ - - namespace extensions { - -+// NOTE: this is defined in command.cc, but command.cc is full of -+// chrome-extensions-specific logic that we don't want to depend on. -+// Since we don't build command.cc in Electron, it's safe to re-define this -+// function here. Ideally, though, `IsMediaKey` would be the responsibility of -+// `ui::Accelerator`, rather than `extensions::Command`. -+ -+// static -+bool Command::IsMediaKey(const ui::Accelerator& accelerator) { -+ if (accelerator.modifiers() != 0) -+ return false; -+ -+ return (accelerator.key_code() == ui::VKEY_MEDIA_NEXT_TRACK || -+ accelerator.key_code() == ui::VKEY_MEDIA_PREV_TRACK || -+ accelerator.key_code() == ui::VKEY_MEDIA_PLAY_PAUSE || -+ accelerator.key_code() == ui::VKEY_MEDIA_STOP || -+ accelerator.key_code() == ui::VKEY_VOLUME_UP || -+ accelerator.key_code() == ui::VKEY_VOLUME_DOWN || -+ accelerator.key_code() == ui::VKEY_VOLUME_MUTE); -+} -+ - // static - GlobalShortcutListener* GlobalShortcutListener::GetInstance() { - CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); diff --git a/chrome/browser/extensions/global_shortcut_listener_win.cc b/chrome/browser/extensions/global_shortcut_listener_win.cc index c5125495b4d178ffb18be4d2d9670f7556412cbd..cddb321abb938c667a4a2089f87eab999510e9b1 100644 --- a/chrome/browser/extensions/global_shortcut_listener_win.cc @@ -87,11 +57,26 @@ index 392cf3d58c64c088596e8d321a2ce37b0ec60b6e..43e30f47240dc10a3a9b950255d4e487 ui::Accelerator accelerator( ui::KeyboardCodeFromXKeyEvent(x_event), modifiers); +diff --git a/ui/base/accelerators/media_keys_listener.cc b/ui/base/accelerators/media_keys_listener.cc +index 1145e1f3d79482b5bb468c3128431ac674310e5f..e319e7a3e34e05b0e96d4a2dbb456355f327137a 100644 +--- a/ui/base/accelerators/media_keys_listener.cc ++++ b/ui/base/accelerators/media_keys_listener.cc +@@ -13,7 +13,9 @@ MediaKeysListener::~MediaKeysListener() = default; + // static + bool MediaKeysListener::IsMediaKeycode(KeyboardCode key_code) { + return key_code == VKEY_MEDIA_PLAY_PAUSE || key_code == VKEY_MEDIA_STOP || +- key_code == VKEY_MEDIA_PREV_TRACK || key_code == VKEY_MEDIA_NEXT_TRACK; ++ key_code == VKEY_MEDIA_PREV_TRACK || key_code == VKEY_MEDIA_NEXT_TRACK || ++ key_code == VKEY_VOLUME_UP || key_code == VKEY_VOLUME_DOWN || ++ key_code == VKEY_VOLUME_MUTE; + } + + } // namespace ui diff --git a/ui/base/accelerators/media_keys_listener_mac.mm b/ui/base/accelerators/media_keys_listener_mac.mm -index f4e3126a4efd66f05c4f13e40ba23db10b8cca96..bb4c1a891dd13855227b39a0e582fd4dbc342ec9 100644 +index 85378bb565de617b1bd611d28c8714361747a357..36de4c0b0353be2418dacd388e92d7c38a7ee139 100644 --- a/ui/base/accelerators/media_keys_listener_mac.mm +++ b/ui/base/accelerators/media_keys_listener_mac.mm -@@ -33,6 +33,12 @@ KeyboardCode MediaKeyCodeToKeyboardCode(int key_code) { +@@ -32,6 +32,12 @@ KeyboardCode MediaKeyCodeToKeyboardCode(int key_code) { case NX_KEYTYPE_NEXT: case NX_KEYTYPE_FAST: return VKEY_MEDIA_NEXT_TRACK; @@ -104,7 +89,7 @@ index f4e3126a4efd66f05c4f13e40ba23db10b8cca96..bb4c1a891dd13855227b39a0e582fd4d } return VKEY_UNKNOWN; } -@@ -193,7 +199,10 @@ static CGEventRef EventTapCallback(CGEventTapProxy proxy, +@@ -192,7 +198,10 @@ CGEventRef MediaKeysListenerImpl::EventTapCallback(CGEventTapProxy proxy, int key_code = (data1 & 0xFFFF0000) >> 16; if (key_code != NX_KEYTYPE_PLAY && key_code != NX_KEYTYPE_NEXT && key_code != NX_KEYTYPE_PREVIOUS && key_code != NX_KEYTYPE_FAST && diff --git a/patches/chromium/content_browser_main_loop.patch b/patches/chromium/content_browser_main_loop.patch index 6aa026772f6f0..187fae32612d7 100644 --- a/patches/chromium/content_browser_main_loop.patch +++ b/patches/chromium/content_browser_main_loop.patch @@ -8,10 +8,10 @@ run before shutdown. This is required to cleanup WebContents asynchronously in atom::CommonWebContentsDelegate::ResetManageWebContents. diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc -index 82f291613ac9682ddf414699046393125eb102eb..440681987b4d6420be93fe2f17180cb42adc8d2c 100644 +index 9da6475030a7962edc776e9427ce411eb11506dc..bc8031cde18f92c1f5a8c24adb4e654e3c381fd3 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc -@@ -1505,7 +1505,7 @@ void BrowserMainLoop::MainMessageLoopRun() { +@@ -1531,7 +1531,7 @@ void BrowserMainLoop::MainMessageLoopRun() { NOTREACHED(); #else base::RunLoop run_loop; diff --git a/patches/chromium/dcheck.patch b/patches/chromium/dcheck.patch index fe838a2e7f471..57e415d203ba0 100644 --- a/patches/chromium/dcheck.patch +++ b/patches/chromium/dcheck.patch @@ -17,10 +17,10 @@ only one or two specific checks fail. Then it's better to simply comment out the failing checks and allow the rest of the target to have them enabled. diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc -index f383b8ee13881b66144ca12e284e1ead4376a27f..42375cd33bb9759ef1c264672b250638a472fe7e 100644 +index 9b489fed825c3a07cd2b3f606f342a7f52b58bb2..af22d8d0852d36d5e8a99f2f9af5feb1ec9e4f65 100644 --- a/content/browser/frame_host/navigation_controller_impl.cc +++ b/content/browser/frame_host/navigation_controller_impl.cc -@@ -1209,8 +1209,10 @@ NavigationType NavigationControllerImpl::ClassifyNavigation( +@@ -1250,8 +1250,10 @@ NavigationType NavigationControllerImpl::ClassifyNavigation( return NAVIGATION_TYPE_NEW_SUBFRAME; } @@ -33,7 +33,7 @@ index f383b8ee13881b66144ca12e284e1ead4376a27f..42375cd33bb9759ef1c264672b250638 if (rfh->GetParent()) { // All manual subframes would be did_create_new_entry and handled above, so -@@ -1462,7 +1464,10 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage( +@@ -1507,7 +1509,10 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage( new_entry->GetFavicon() = GetLastCommittedEntry()->GetFavicon(); } @@ -46,10 +46,10 @@ index f383b8ee13881b66144ca12e284e1ead4376a27f..42375cd33bb9759ef1c264672b250638 // navigation. Now we know that the renderer has updated its state accordingly // and it is safe to also clear the browser side history. diff --git a/ui/base/clipboard/clipboard_win.cc b/ui/base/clipboard/clipboard_win.cc -index 34c13c28117fe5fced5ca3190c5f948168cd010f..554ddabee8952e578a61a9b9c3f358b2475abd69 100644 +index 00a096c43f13d8addcf202545ac2aef97ef9c556..0145c63c41ba67b260b5df273d04e5b87c22af23 100644 --- a/ui/base/clipboard/clipboard_win.cc +++ b/ui/base/clipboard/clipboard_win.cc -@@ -730,9 +730,9 @@ void ClipboardWin::WriteBitmapFromHandle(HBITMAP source_hbitmap, +@@ -744,9 +744,9 @@ void ClipboardWin::WriteBitmapFromHandle(HBITMAP source_hbitmap, } void ClipboardWin::WriteToClipboard(unsigned int format, HANDLE handle) { diff --git a/patches/chromium/desktop_media_list.patch b/patches/chromium/desktop_media_list.patch index 3c3c98aff9454..4aa6fd374b108 100644 --- a/patches/chromium/desktop_media_list.patch +++ b/patches/chromium/desktop_media_list.patch @@ -7,10 +7,20 @@ Subject: desktop_media_list.patch * Free the one-time use capturer after thumbnails are fetched diff --git a/chrome/browser/media/webrtc/desktop_media_list.h b/chrome/browser/media/webrtc/desktop_media_list.h -index a489bf6ea2179059f53e53563e993db9c7cd123b..8d3df446290e2111e4952899539cbc16d37546e5 100644 +index a489bf6ea2179059f53e53563e993db9c7cd123b..93e237569fe94ec8526f67d915e1a7352d586b16 100644 --- a/chrome/browser/media/webrtc/desktop_media_list.h +++ b/chrome/browser/media/webrtc/desktop_media_list.h -@@ -69,6 +69,7 @@ class DesktopMediaList { +@@ -38,6 +38,9 @@ class DesktopMediaList { + + virtual ~DesktopMediaList() {} + ++ // Allows listening to notifications generated by the model. ++ virtual void AddObserver(DesktopMediaListObserver* observer) = 0; ++ + // Sets time interval between updates. By default list of sources and their + // thumbnail are updated once per second. If called after StartUpdating() then + // it will take effect only after the next update. +@@ -69,6 +72,7 @@ class DesktopMediaList { virtual int GetSourceCount() const = 0; virtual const Source& GetSource(int index) const = 0; @@ -19,10 +29,22 @@ index a489bf6ea2179059f53e53563e993db9c7cd123b..8d3df446290e2111e4952899539cbc16 virtual content::DesktopMediaID::Type GetMediaListType() const = 0; }; diff --git a/chrome/browser/media/webrtc/desktop_media_list_base.cc b/chrome/browser/media/webrtc/desktop_media_list_base.cc -index 9d9b7435044c4b84f971bd94765eb875fbac1624..eb7e10f7d9e937ebd6f776e4b4ec495d02fb884b 100644 +index 9d9b7435044c4b84f971bd94765eb875fbac1624..2918f91d8e1308bf5a47313e20ad3cec74918f3a 100644 --- a/chrome/browser/media/webrtc/desktop_media_list_base.cc +++ b/chrome/browser/media/webrtc/desktop_media_list_base.cc -@@ -56,7 +56,7 @@ void DesktopMediaListBase::Update(UpdateCallback callback) { +@@ -22,6 +22,11 @@ DesktopMediaListBase::DesktopMediaListBase(base::TimeDelta update_period) + + DesktopMediaListBase::~DesktopMediaListBase() {} + ++void DesktopMediaListBase::AddObserver(DesktopMediaListObserver* observer) { ++ DCHECK(!observer_); ++ observer_ = observer; ++} ++ + void DesktopMediaListBase::SetUpdatePeriod(base::TimeDelta period) { + DCHECK(!observer_); + update_period_ = period; +@@ -56,7 +61,7 @@ void DesktopMediaListBase::Update(UpdateCallback callback) { DCHECK(sources_.empty()); DCHECK(!refresh_callback_); refresh_callback_ = std::move(callback); @@ -31,7 +53,7 @@ index 9d9b7435044c4b84f971bd94765eb875fbac1624..eb7e10f7d9e937ebd6f776e4b4ec495d } int DesktopMediaListBase::GetSourceCount() const { -@@ -70,6 +70,11 @@ const DesktopMediaList::Source& DesktopMediaListBase::GetSource( +@@ -70,6 +75,11 @@ const DesktopMediaList::Source& DesktopMediaListBase::GetSource( return sources_[index]; } @@ -43,11 +65,32 @@ index 9d9b7435044c4b84f971bd94765eb875fbac1624..eb7e10f7d9e937ebd6f776e4b4ec495d DesktopMediaID::Type DesktopMediaListBase::GetMediaListType() const { return type_; } +@@ -81,6 +91,12 @@ DesktopMediaListBase::SourceDescription::SourceDescription( + + void DesktopMediaListBase::UpdateSourcesList( + const std::vector& new_sources) { ++ // Notify observer when there was no new source captured. ++ if (new_sources.empty()) { ++ observer_->OnSourceUnchanged(this); ++ return; ++ } ++ + typedef std::set SourceSet; + SourceSet new_source_set; + for (size_t i = 0; i < new_sources.size(); ++i) { diff --git a/chrome/browser/media/webrtc/desktop_media_list_base.h b/chrome/browser/media/webrtc/desktop_media_list_base.h -index 3c09ec3111d54cb4f0b74f16a81292b779009eae..26acc18acaef21ced3604fe7ede079d8577f53f4 100644 +index 3c09ec3111d54cb4f0b74f16a81292b779009eae..5c3667822afd47a22878b508b844a615c24f3162 100644 --- a/chrome/browser/media/webrtc/desktop_media_list_base.h +++ b/chrome/browser/media/webrtc/desktop_media_list_base.h -@@ -34,6 +34,7 @@ class DesktopMediaListBase : public DesktopMediaList { +@@ -27,6 +27,7 @@ class DesktopMediaListBase : public DesktopMediaList { + ~DesktopMediaListBase() override; + + // DesktopMediaList interface. ++ void AddObserver(DesktopMediaListObserver* observer) override; + void SetUpdatePeriod(base::TimeDelta period) override; + void SetThumbnailSize(const gfx::Size& thumbnail_size) override; + void SetViewDialogWindowId(content::DesktopMediaID dialog_id) override; +@@ -34,6 +35,7 @@ class DesktopMediaListBase : public DesktopMediaList { void Update(UpdateCallback callback) override; int GetSourceCount() const override; const Source& GetSource(int index) const override; @@ -55,6 +98,18 @@ index 3c09ec3111d54cb4f0b74f16a81292b779009eae..26acc18acaef21ced3604fe7ede079d8 content::DesktopMediaID::Type GetMediaListType() const override; static uint32_t GetImageHash(const gfx::Image& image); +diff --git a/chrome/browser/media/webrtc/desktop_media_list_observer.h b/chrome/browser/media/webrtc/desktop_media_list_observer.h +index ad7f766a36b1b6b2a8bc0f96369f1aaadf6681f7..f6c6c14a0937430df62c9b9c1132c5916a9f2009 100644 +--- a/chrome/browser/media/webrtc/desktop_media_list_observer.h ++++ b/chrome/browser/media/webrtc/desktop_media_list_observer.h +@@ -20,6 +20,7 @@ class DesktopMediaListObserver { + int new_index) = 0; + virtual void OnSourceNameChanged(DesktopMediaList* list, int index) = 0; + virtual void OnSourceThumbnailChanged(DesktopMediaList* list, int index) = 0; ++ virtual void OnSourceUnchanged(DesktopMediaList* list) = 0; + + protected: + virtual ~DesktopMediaListObserver() {} diff --git a/chrome/browser/media/webrtc/native_desktop_media_list.cc b/chrome/browser/media/webrtc/native_desktop_media_list.cc index 2b109bd1dea2aed647ec01c0660b2d4c963312f2..8567b4c84b55f1f7774ef3755dca63e94ddd7a29 100644 --- a/chrome/browser/media/webrtc/native_desktop_media_list.cc diff --git a/patches/chromium/disable-redraw-lock.patch b/patches/chromium/disable-redraw-lock.patch index 9b79945abae5f..3de7209703a14 100644 --- a/patches/chromium/disable-redraw-lock.patch +++ b/patches/chromium/disable-redraw-lock.patch @@ -15,10 +15,10 @@ the redraw locking mechanism, which fixes these issues. The electron issue can be found at https://github.com/electron/electron/issues/1821 diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc -index 5a60f89ef1cb766a7b06ef80c4bee16c7de593e2..4a96f076a200e59289cbb1f152ec9d3d266f4214 100644 +index 08807dc1a6cabea84277de128a92bc8de2bbee63..1fd8da727592fec91056021ea9616996a8f54b92 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc -@@ -348,6 +348,10 @@ constexpr int kSynthesizedMouseMessagesTimeDifference = 500; +@@ -305,6 +305,10 @@ constexpr int kSynthesizedMouseMessagesTimeDifference = 500; } // namespace @@ -29,7 +29,7 @@ index 5a60f89ef1cb766a7b06ef80c4bee16c7de593e2..4a96f076a200e59289cbb1f152ec9d3d // A scoping class that prevents a window from being able to redraw in response // to invalidations that may occur within it for the lifetime of the object. // -@@ -399,6 +403,7 @@ class HWNDMessageHandler::ScopedRedrawLock { +@@ -356,6 +360,7 @@ class HWNDMessageHandler::ScopedRedrawLock { cancel_unlock_(false), should_lock_(owner_->IsVisible() && !owner->HasChildRenderingWindow() && ::IsWindow(hwnd_) && @@ -37,7 +37,7 @@ index 5a60f89ef1cb766a7b06ef80c4bee16c7de593e2..4a96f076a200e59289cbb1f152ec9d3d (!(GetWindowLong(hwnd_, GWL_STYLE) & WS_CAPTION) || !ui::win::IsAeroGlassEnabled())) { if (should_lock_) -@@ -1022,6 +1027,10 @@ HWNDMessageHandler::RegisterUnadjustedMouseEvent() { +@@ -975,6 +980,10 @@ HWNDMessageHandler::RegisterUnadjustedMouseEvent() { return scoped_enable; } @@ -49,7 +49,7 @@ index 5a60f89ef1cb766a7b06ef80c4bee16c7de593e2..4a96f076a200e59289cbb1f152ec9d3d // HWNDMessageHandler, gfx::WindowImpl overrides: diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h -index 4044afcc0fd1f076fc392d34417e3ccaa1347031..2bd5fcdf0293a07938d22902121517a458bd3e88 100644 +index 84dae56aad5bed31caf4e885cc919473f73decd2..4d094132f2c7f7f32328b27406cde8215dc56829 100644 --- a/ui/views/win/hwnd_message_handler.h +++ b/ui/views/win/hwnd_message_handler.h @@ -202,6 +202,8 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, @@ -62,7 +62,7 @@ index 4044afcc0fd1f076fc392d34417e3ccaa1347031..2bd5fcdf0293a07938d22902121517a4 HICON GetDefaultWindowIcon() const override; HICON GetSmallWindowIcon() const override; diff --git a/ui/views/win/hwnd_message_handler_delegate.h b/ui/views/win/hwnd_message_handler_delegate.h -index ddc640e7a5d278e08c056f1989ac258c9ae8bfae..a5d050dadad3c565b0151ee945e5d17f42255aa3 100644 +index 45c4e5b29d05ea0323596fa2c5034c2e30a68f70..e25aac69b09954fbc267309beba98ec0877fa2ac 100644 --- a/ui/views/win/hwnd_message_handler_delegate.h +++ b/ui/views/win/hwnd_message_handler_delegate.h @@ -46,6 +46,8 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { diff --git a/patches/chromium/disable_color_correct_rendering.patch b/patches/chromium/disable_color_correct_rendering.patch index 16a76ae587f6d..82835661dfbb5 100644 --- a/patches/chromium/disable_color_correct_rendering.patch +++ b/patches/chromium/disable_color_correct_rendering.patch @@ -20,10 +20,10 @@ to deal with color spaces. That is being tracked at https://crbug.com/634542 and https://crbug.com/711107. diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc -index 8d8d18e2c62dbabfaa81d79f0555954b0b319d3d..298bfe91b9c9f4cbb3b07fe9bc368a1e84433ed1 100644 +index 18ae9c1f1af64fd34998562ed1edba0b3b1c74a1..d59e618bbee4a31b0a95955204e7ad9e7b98bd9a 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc -@@ -1815,6 +1815,12 @@ const gfx::ColorSpace& LayerTreeHostImpl::GetRasterColorSpace() const { +@@ -1816,6 +1816,12 @@ const gfx::ColorSpace& LayerTreeHostImpl::GetRasterColorSpace() const { const gfx::ColorSpace& LayerTreeHostImpl::GetRasterColorSpaceAndId( int* id) const { @@ -37,10 +37,10 @@ index 8d8d18e2c62dbabfaa81d79f0555954b0b319d3d..298bfe91b9c9f4cbb3b07fe9bc368a1e // The pending tree will have the most recently updated color space, so // prefer that. diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h -index b6dd56de7cde72051caeedeef3ee413b8f24eba6..0da3f40ebc81170f63e61d8dc5714df5dd72c049 100644 +index 0743417d59c41ce2b4ba89f89f7f03db3849fc74..65f3ab8f48477908ed605f9c73d9d21f1a3709c3 100644 --- a/cc/trees/layer_tree_settings.h +++ b/cc/trees/layer_tree_settings.h -@@ -96,6 +96,8 @@ class CC_EXPORT LayerTreeSettings { +@@ -100,6 +100,8 @@ class CC_EXPORT LayerTreeSettings { bool use_rgba_4444 = false; bool unpremultiply_and_dither_low_bit_depth_tiles = false; @@ -62,7 +62,7 @@ index 389f2ed29bd85c35b38df92346a8d6c76ce22fd6..02cf492fa4068d10c4b8b2addee8102b bool force_antialiasing = false; bool force_blending_with_shaders = false; diff --git a/components/viz/host/renderer_settings_creation.cc b/components/viz/host/renderer_settings_creation.cc -index f17aa1fa451f1b99d7f083e07edd49b11f7639e4..09f7c5d6a92d89c199b296771a8ff60c89a07083 100644 +index cb69e08c0185f55497089505ed5b226e57140321..362f28accc248248f3bcb54955c2352819242fa9 100644 --- a/components/viz/host/renderer_settings_creation.cc +++ b/components/viz/host/renderer_settings_creation.cc @@ -13,6 +13,7 @@ @@ -83,7 +83,7 @@ index f17aa1fa451f1b99d7f083e07edd49b11f7639e4..09f7c5d6a92d89c199b296771a8ff60c !command_line->HasSwitch(switches::kUIDisablePartialSwap); #if defined(OS_MACOSX) diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc -index 9eb0276cbedd99b41e537720ea159cdaac7ce705..fbc49a3aa2a5458050acf0280f6caaa659941a68 100644 +index 30533e787515416723ec41095119871a02fa7805..2682fb11f2c0049c3f3be9a34a2b319bc1a6ff15 100644 --- a/components/viz/service/display/gl_renderer.cc +++ b/components/viz/service/display/gl_renderer.cc @@ -83,6 +83,9 @@ @@ -108,7 +108,7 @@ index 9eb0276cbedd99b41e537720ea159cdaac7ce705..fbc49a3aa2a5458050acf0280f6caaa6 // Use the full quad_rect for debug quads to not move the edges based on // partial swaps. -@@ -1428,7 +1432,8 @@ void GLRenderer::ChooseRPDQProgram(DrawRenderPassDrawQuadParams* params, +@@ -1476,7 +1480,8 @@ void GLRenderer::ChooseRPDQProgram(DrawRenderPassDrawQuadParams* params, params->use_color_matrix, tint_gl_composited_content_, params->apply_shader_based_rounded_corner && ShouldApplyRoundedCorner(params->quad)), @@ -118,7 +118,7 @@ index 9eb0276cbedd99b41e537720ea159cdaac7ce705..fbc49a3aa2a5458050acf0280f6caaa6 } void GLRenderer::UpdateRPDQUniforms(DrawRenderPassDrawQuadParams* params) { -@@ -1899,8 +1904,8 @@ void GLRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad, +@@ -1947,8 +1952,8 @@ void GLRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad, SetUseProgram(ProgramKey::SolidColor(use_aa ? USE_AA : NO_AA, tint_gl_composited_content_, ShouldApplyRoundedCorner(quad)), @@ -129,7 +129,7 @@ index 9eb0276cbedd99b41e537720ea159cdaac7ce705..fbc49a3aa2a5458050acf0280f6caaa6 SetShaderColor(color, opacity); if (current_program_->rounded_corner_rect_location() != -1) { SetShaderRoundedCorner( -@@ -2055,8 +2060,8 @@ void GLRenderer::DrawContentQuadAA(const ContentDrawQuadBase* quad, +@@ -2103,8 +2108,8 @@ void GLRenderer::DrawContentQuadAA(const ContentDrawQuadBase* quad, : NON_PREMULTIPLIED_ALPHA, false, false, tint_gl_composited_content_, ShouldApplyRoundedCorner(quad)), @@ -140,7 +140,7 @@ index 9eb0276cbedd99b41e537720ea159cdaac7ce705..fbc49a3aa2a5458050acf0280f6caaa6 if (current_program_->tint_color_matrix_location() != -1) { auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix(); -@@ -2152,8 +2157,8 @@ void GLRenderer::DrawContentQuadNoAA(const ContentDrawQuadBase* quad, +@@ -2200,8 +2205,8 @@ void GLRenderer::DrawContentQuadNoAA(const ContentDrawQuadBase* quad, !quad->ShouldDrawWithBlending(), has_tex_clamp_rect, tint_gl_composited_content_, ShouldApplyRoundedCorner(quad)), @@ -151,16 +151,16 @@ index 9eb0276cbedd99b41e537720ea159cdaac7ce705..fbc49a3aa2a5458050acf0280f6caaa6 if (current_program_->tint_color_matrix_location() != -1) { auto matrix = cc::DebugColors::TintCompositedContentColorTransformMatrix(); -@@ -2256,7 +2261,7 @@ void GLRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad, +@@ -2304,7 +2309,7 @@ void GLRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad, DCHECK_NE(src_color_space, src_color_space.GetAsFullRangeRGB()); gfx::ColorSpace dst_color_space = - current_frame()->current_render_pass->color_space; + PATCH_CS(current_frame()->current_render_pass->color_space); + + #if defined(OS_WIN) // Force sRGB output on Windows for overlay candidate video quads to match - // DirectComposition behavior in case these switch between overlays and - // compositing. See https://crbug.com/811118 for details. -@@ -2412,8 +2417,8 @@ void GLRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad, +@@ -2463,8 +2468,8 @@ void GLRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad, SetUseProgram(ProgramKey::VideoStream(tex_coord_precision, ShouldApplyRoundedCorner(quad)), @@ -171,7 +171,7 @@ index 9eb0276cbedd99b41e537720ea159cdaac7ce705..fbc49a3aa2a5458050acf0280f6caaa6 DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_)); gl_->BindTexture(GL_TEXTURE_EXTERNAL_OES, lock.texture_id()); -@@ -2475,8 +2480,8 @@ void GLRenderer::FlushTextureQuadCache(BoundGeometry flush_binding) { +@@ -2526,8 +2531,8 @@ void GLRenderer::FlushTextureQuadCache(BoundGeometry flush_binding) { draw_cache_.nearest_neighbor ? GL_NEAREST : GL_LINEAR); // Bind the program to the GL state. @@ -182,7 +182,7 @@ index 9eb0276cbedd99b41e537720ea159cdaac7ce705..fbc49a3aa2a5458050acf0280f6caaa6 if (current_program_->rounded_corner_rect_location() != -1) { SetShaderRoundedCorner( -@@ -3185,7 +3190,9 @@ void GLRenderer::PrepareGeometry(BoundGeometry binding) { +@@ -3239,7 +3244,9 @@ void GLRenderer::PrepareGeometry(BoundGeometry binding) { void GLRenderer::SetUseProgram(const ProgramKey& program_key_no_color, const gfx::ColorSpace& src_color_space, const gfx::ColorSpace& dst_color_space) { @@ -193,7 +193,7 @@ index 9eb0276cbedd99b41e537720ea159cdaac7ce705..fbc49a3aa2a5458050acf0280f6caaa6 gfx::ColorSpace adjusted_color_space = src_color_space; float sdr_white_level = current_frame()->sdr_white_level; -@@ -3564,7 +3571,7 @@ void GLRenderer::CopyRenderPassDrawQuadToOverlayResource( +@@ -3634,7 +3641,7 @@ void GLRenderer::CopyRenderPassDrawQuadToOverlayResource( *overlay_texture = FindOrCreateOverlayTexture( params.quad->render_pass_id, iosurface_width, iosurface_height, @@ -202,7 +202,7 @@ index 9eb0276cbedd99b41e537720ea159cdaac7ce705..fbc49a3aa2a5458050acf0280f6caaa6 *new_bounds = gfx::RectF(updated_dst_rect.origin(), gfx::SizeF((*overlay_texture)->texture.size())); -@@ -3782,8 +3789,8 @@ void GLRenderer::FlushOverdrawFeedback(const gfx::Rect& output_rect) { +@@ -3853,8 +3860,8 @@ void GLRenderer::FlushOverdrawFeedback(const gfx::Rect& output_rect) { PrepareGeometry(SHARED_BINDING); @@ -213,17 +213,17 @@ index 9eb0276cbedd99b41e537720ea159cdaac7ce705..fbc49a3aa2a5458050acf0280f6caaa6 gfx::Transform render_matrix; render_matrix.Translate(0.5 * output_rect.width() + output_rect.x(), -@@ -3943,3 +3950,5 @@ gfx::Size GLRenderer::GetRenderPassBackingPixelSize( +@@ -4014,3 +4021,5 @@ gfx::Size GLRenderer::GetRenderPassBackingPixelSize( } } // namespace viz + +#undef PATCH_CS diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc -index e42a2f780170f01edce84753d60b1fba3a429aeb..3cb62924adf93c4d437c8fa81529efc5188d7f77 100644 +index bb814d78afc5dedbcf59ff0a9085d599385d222d..a9336275b2d2ec2d817e489a33ec831843b2cf05 100644 --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc -@@ -196,6 +196,7 @@ GpuTerminationStatus ConvertToGpuTerminationStatus( +@@ -185,6 +185,7 @@ GpuTerminationStatus ConvertToGpuTerminationStatus( // Command-line switches to propagate to the GPU process. static const char* const kSwitchNames[] = { @@ -232,18 +232,18 @@ index e42a2f780170f01edce84753d60b1fba3a429aeb..3cb62924adf93c4d437c8fa81529efc5 service_manager::switches::kGpuSandboxAllowSysVShm, service_manager::switches::kGpuSandboxFailuresFatal, diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index 0408a35db18ecdcad1521e4123aed4bd12bcf2c8..94898420ab1b93f61c830e19cd31ed0a1716922e 100644 +index f942380764aef7e86a0de112e9b70aed960acade..fefb63c75936a67b66aecffec5a3be04fdb0eb51 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc -@@ -225,6 +225,7 @@ +@@ -230,6 +230,7 @@ #include "ui/base/ui_base_switches.h" #include "ui/base/ui_base_switches_util.h" #include "ui/display/display_switches.h" +#include "ui/gfx/switches.h" #include "ui/gl/gl_switches.h" #include "ui/native_theme/native_theme_features.h" - #include "url/url_constants.h" -@@ -3015,6 +3016,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( + #include "url/origin.h" +@@ -3083,6 +3084,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( // Propagate the following switches to the renderer command line (along // with any associated values) if present in the browser command line. static const char* const kSwitchNames[] = { @@ -252,10 +252,10 @@ index 0408a35db18ecdcad1521e4123aed4bd12bcf2c8..94898420ab1b93f61c830e19cd31ed0a service_manager::switches::kDisableInProcessStackTraces, service_manager::switches::kDisableSeccompFilterSandbox, diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc -index 74cdb6031587640946ed08f37f1579ea4e68414c..68a5ec2d04dc3ec1c81d8f62fa067416f2faee29 100644 +index e26b780100821783413e613a1295c831a3670071..4dc57f496513f8d1aeb29a0f408842ecbc2e620a 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc -@@ -2930,6 +2930,9 @@ cc::LayerTreeSettings RenderWidget::GenerateLayerTreeSettings( +@@ -2885,6 +2885,9 @@ cc::LayerTreeSettings RenderWidget::GenerateLayerTreeSettings( settings.main_frame_before_activation_enabled = cmd.HasSwitch(cc::switches::kEnableMainFrameBeforeActivation); @@ -266,10 +266,18 @@ index 74cdb6031587640946ed08f37f1579ea4e68414c..68a5ec2d04dc3ec1c81d8f62fa067416 // is what the renderer uses if its not threaded. settings.enable_checker_imaging = diff --git a/third_party/blink/renderer/platform/graphics/canvas_color_params.cc b/third_party/blink/renderer/platform/graphics/canvas_color_params.cc -index 1aedba288aed698fd1b7ac6a4ef1a67fc892f84a..df2b5b120483225c2bd21a337e6085dbceca4ec4 100644 +index a947db5fb562fb4ccbfb94df88f460f1da9451f4..77c816b2c5e98a4546c74d9358dfcf3f205f1781 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_color_params.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_color_params.cc -@@ -12,6 +12,7 @@ +@@ -4,6 +4,7 @@ + + #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h" + ++#include "base/command_line.h" + #include "cc/paint/skia_paint_canvas.h" + #include "components/viz/common/resources/resource_format_utils.h" + #include "third_party/blink/renderer/platform/runtime_enabled_features.h" +@@ -12,6 +13,7 @@ #include "third_party/khronos/GLES3/gl3.h" #include "third_party/skia/include/core/SkSurfaceProps.h" #include "ui/gfx/color_space.h" @@ -277,7 +285,7 @@ index 1aedba288aed698fd1b7ac6a4ef1a67fc892f84a..df2b5b120483225c2bd21a337e6085db namespace blink { -@@ -89,6 +90,11 @@ uint8_t CanvasColorParams::BytesPerPixel() const { +@@ -85,6 +87,11 @@ uint8_t CanvasColorParams::BytesPerPixel() const { } gfx::ColorSpace CanvasColorParams::GetSamplerGfxColorSpace() const { @@ -289,7 +297,7 @@ index 1aedba288aed698fd1b7ac6a4ef1a67fc892f84a..df2b5b120483225c2bd21a337e6085db gfx::ColorSpace::PrimaryID primary_id = GetPrimaryID(color_space_); // TODO(ccameron): This needs to take into account whether or not this texture -@@ -102,6 +108,11 @@ gfx::ColorSpace CanvasColorParams::GetSamplerGfxColorSpace() const { +@@ -98,6 +105,11 @@ gfx::ColorSpace CanvasColorParams::GetSamplerGfxColorSpace() const { } gfx::ColorSpace CanvasColorParams::GetStorageGfxColorSpace() const { @@ -302,7 +310,7 @@ index 1aedba288aed698fd1b7ac6a4ef1a67fc892f84a..df2b5b120483225c2bd21a337e6085db gfx::ColorSpace::TransferID transfer_id = diff --git a/ui/gfx/mac/io_surface.cc b/ui/gfx/mac/io_surface.cc -index 41f7fcbdd63af315f4b4e768bfef3b5004807a0b..398a4fdea3cc0ab4f5132deeb9365189f9c928c3 100644 +index c383d42c986aa29fcdfc2009901ed452ef480b55..1b8d0818155a407eb42c3f785e9609fe7bfa3dc7 100644 --- a/ui/gfx/mac/io_surface.cc +++ b/ui/gfx/mac/io_surface.cc @@ -16,6 +16,7 @@ @@ -313,19 +321,19 @@ index 41f7fcbdd63af315f4b4e768bfef3b5004807a0b..398a4fdea3cc0ab4f5132deeb9365189 namespace gfx { -@@ -186,6 +187,11 @@ IOSurfaceRef CreateIOSurface(const gfx::Size& size, - - // Ensure that all IOSurfaces start as sRGB. - CGColorSpaceRef color_space = base::mac::GetSRGBColorSpace(); -+ auto* cmd_line = base::CommandLine::ForCurrentProcess(); -+ if (cmd_line->HasSwitch(switches::kDisableColorCorrectRendering)) { -+ color_space = base::mac::GetSystemColorSpace(); -+ } +@@ -261,6 +262,11 @@ IOSurfaceRef CreateIOSurface(const gfx::Size& size, + IOSurfaceSetValue(surface, CFSTR("IOSurfaceColorSpace"), kCGColorSpaceSRGB); + } else { + CGColorSpaceRef color_space = base::mac::GetSRGBColorSpace(); ++ auto* cmd_line = base::CommandLine::ForCurrentProcess(); ++ if (cmd_line->HasSwitch(switches::kDisableColorCorrectRendering)) { ++ color_space = base::mac::GetSystemColorSpace(); ++ } + - base::ScopedCFTypeRef color_space_icc( - CGColorSpaceCopyICCProfile(color_space)); - IOSurfaceSetValue(surface, CFSTR("IOSurfaceColorSpace"), color_space_icc); -@@ -197,6 +203,14 @@ IOSurfaceRef CreateIOSurface(const gfx::Size& size, + base::ScopedCFTypeRef color_space_icc( + CGColorSpaceCopyICCProfile(color_space)); + IOSurfaceSetValue(surface, CFSTR("IOSurfaceColorSpace"), color_space_icc); +@@ -277,6 +283,14 @@ bool IOSurfaceCanSetColorSpace(const ColorSpace& color_space) { void IOSurfaceSetColorSpace(IOSurfaceRef io_surface, const ColorSpace& color_space) { @@ -337,9 +345,9 @@ index 41f7fcbdd63af315f4b4e768bfef3b5004807a0b..398a4fdea3cc0ab4f5132deeb9365189 + return; + } + - // Special-case sRGB. - if (color_space == ColorSpace::CreateSRGB()) { - base::ScopedCFTypeRef srgb_icc( + if (!internal::IOSurfaceSetColorSpace(io_surface, color_space)) { + DLOG(ERROR) << "Failed to set color space for IOSurface: " + << color_space.ToString(); diff --git a/ui/gfx/switches.cc b/ui/gfx/switches.cc index ba3dbf23d1df7a3b0cc199054f36a88014daa0e7..f8a563a78cee2856da0f2ad556beba19b01a2e59 100644 --- a/ui/gfx/switches.cc diff --git a/patches/chromium/disable_compositor_recycling.patch b/patches/chromium/disable_compositor_recycling.patch index 9eba6a0a603fb..993ff7fb38934 100644 --- a/patches/chromium/disable_compositor_recycling.patch +++ b/patches/chromium/disable_compositor_recycling.patch @@ -5,16 +5,20 @@ Subject: fix: disabling compositor recycling Compositor recycling is useful for Chrome because there can be many tabs and spinning up a compositor for each one would be costly. In practice, Chrome uses the parent compositor code path of browser_compositor_view_mac.mm; the NSView of each tab is detached when it's hidden and attached when it's shown. For Electron, there is no parent compositor, so we're forced into the "own compositor" code path, which seems to be non-optimal and pretty ruthless in terms of the release of resources. Electron has no real concept of multiple tabs per window, so it should be okay to disable this ruthless recycling altogether in Electron. -diff --git a/content/browser/renderer_host/browser_compositor_view_mac.mm b/content/browser/renderer_host/browser_compositor_view_mac.mm -index 18019d5794f688ca07b35a665cc9800bb1d3047a..60c7e980dd322ba012c564fca68848c3188ca5dc 100644 ---- a/content/browser/renderer_host/browser_compositor_view_mac.mm -+++ b/content/browser/renderer_host/browser_compositor_view_mac.mm -@@ -209,7 +209,7 @@ - } +diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm +index 5bec9c5d258c06fe338c8abe3e233e0b9b937234..92d2fa2343d1272dcadfa37c07ed368d18488ef8 100644 +--- a/content/browser/renderer_host/render_widget_host_view_mac.mm ++++ b/content/browser/renderer_host/render_widget_host_view_mac.mm +@@ -477,7 +477,11 @@ void RenderWidgetHostViewMac::WasOccluded() { + return; - void BrowserCompositorMac::SetRenderWidgetHostIsHidden(bool hidden) { -- render_widget_host_is_hidden_ = hidden; -+ render_widget_host_is_hidden_ = false; - UpdateState(); + host()->WasHidden(); +- browser_compositor_->SetRenderWidgetHostIsHidden(true); ++ // Consider the RWHV occluded only if it is not attached to a window ++ // (e.g. unattached BrowserView). Otherwise we treat it as visible to ++ // prevent unnecessary compositor recycling. ++ const bool unattached = ![GetInProcessNSView() window]; ++ browser_compositor_->SetRenderWidgetHostIsHidden(unattached); } + void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) { diff --git a/patches/chromium/disable_custom_libcxx_on_windows.patch b/patches/chromium/disable_custom_libcxx_on_windows.patch deleted file mode 100644 index 38bfa0d3be2b7..0000000000000 --- a/patches/chromium/disable_custom_libcxx_on_windows.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Nitish Sakhawalkar -Date: Mon, 13 May 2019 15:48:36 -0700 -Subject: disable_custom_libcxx_on_windows.patch - - -diff --git a/build/config/c++/c++.gni b/build/config/c++/c++.gni -index 834a943deca28655f40490ab78a14496e253c143..7abf6d83b04b515881c8f797e1031082afe7a5c8 100644 ---- a/build/config/c++/c++.gni -+++ b/build/config/c++/c++.gni -@@ -12,7 +12,8 @@ declare_args() { - # is not supported. - use_custom_libcxx = - is_fuchsia || is_android || is_mac || (is_ios && !use_xcode_clang) || -- (is_win && is_clang) || -+ # Do not use custom libcxx on windows -+ #(is_win && is_clang) || - (is_linux && - (!is_chromeos || default_toolchain != "//build/toolchain/cros:target")) - diff --git a/patches/chromium/disable_hidden.patch b/patches/chromium/disable_hidden.patch index 84354422416c3..995bc3e002f4a 100644 --- a/patches/chromium/disable_hidden.patch +++ b/patches/chromium/disable_hidden.patch @@ -5,10 +5,10 @@ Subject: disable_hidden.patch diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc -index 55c13fa06f7c6a5e7b3bd072ed7ffc452fd24438..09b6a881a5644859eafb216eee1172aeb8d086b2 100644 +index 2427458b6b28163fd22427b6df811e8995fb5e7a..d67531f8b5739c17a886993beda4bd12ec43c78b 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc -@@ -683,6 +683,9 @@ void RenderWidgetHostImpl::WasHidden() { +@@ -647,6 +647,9 @@ void RenderWidgetHostImpl::WasHidden() { if (is_hidden_) return; @@ -19,10 +19,10 @@ index 55c13fa06f7c6a5e7b3bd072ed7ffc452fd24438..09b6a881a5644859eafb216eee1172ae TRACE_EVENT0("renderer_host", "RenderWidgetHostImpl::WasHidden"); diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h -index 71bfb5e5e6e0aba5615bc76cae1b551ac1880d09..c5175b7fd5c5394703fca85e57b4be5faf618b63 100644 +index 459d06cd7f5e83c5ec8fa06abfe4661a612f3c62..cbeee0407ae9fe2b5e354fb103b0e6c7024b70d8 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h -@@ -200,6 +200,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl +@@ -187,6 +187,9 @@ class CONTENT_EXPORT RenderWidgetHostImpl // RenderWidgetHostImpl. static RenderWidgetHostImpl* From(RenderWidgetHost* rwh); diff --git a/patches/chromium/disable_user_gesture_requirement_for_beforeunload_dialogs.patch b/patches/chromium/disable_user_gesture_requirement_for_beforeunload_dialogs.patch index 93fd00eba0358..a1a714a8a8646 100644 --- a/patches/chromium/disable_user_gesture_requirement_for_beforeunload_dialogs.patch +++ b/patches/chromium/disable_user_gesture_requirement_for_beforeunload_dialogs.patch @@ -6,10 +6,10 @@ Subject: disable_user_gesture_requirement_for_beforeunload_dialogs.patch See https://github.com/electron/electron/issues/10754 diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc -index 3c8e5db44c4174d5e876a8874fc702cc3233fc7b..31d45afae965efeb15b83d9f4259cc50abe987dd 100644 +index 2247f71c3c975a53edf8486fc5c5b2cfef18aea2..39019fe19391f5df3bfc83248c9099a83c14109f 100644 --- a/third_party/blink/renderer/core/dom/document.cc +++ b/third_party/blink/renderer/core/dom/document.cc -@@ -4181,7 +4181,9 @@ bool Document::DispatchBeforeUnloadEvent(ChromeClient* chrome_client, +@@ -4253,7 +4253,9 @@ bool Document::DispatchBeforeUnloadEvent(ChromeClient* chrome_client, "frame that never had a user gesture since its load. " "https://www.chromestatus.com/feature/5082396709879808"; Intervention::GenerateReport(frame_, "BeforeUnloadNoGesture", message); diff --git a/patches/chromium/dom_storage_limits.patch b/patches/chromium/dom_storage_limits.patch index 5334f8e3cfb94..9f8b88560a3c3 100644 --- a/patches/chromium/dom_storage_limits.patch +++ b/patches/chromium/dom_storage_limits.patch @@ -1,80 +1,47 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jacob Quant -Date: Tue, 6 Nov 2018 15:26:00 -0600 +Date: Thu, 31 Oct 2019 14:00:00 -0500 Subject: dom_storage_limits.patch -This patch circumvents the restriction on DOM storage objects, -namely `localStorage` and `sessionStorage`, which chromium otherwise -limits to approximately 10MiB. +This patch increases the DOM storage (e.g. `localStorage` +and `sessionStorage`) size quota from 10MiB to 100MiB. +Previous versions of this patch attempted to circumvent +the restriction altogether. +However, this can lead to other problems, such as crashing +the Dev Tools when attempting to read or write values that exceed +`IPC::Channel::kMaximumMessageSize` (128MiB). -That restriction originates from a recommendation -[in the Web Storage API specification](https://html.spec.whatwg.org/multipage/webstorage.html#disk-space-2) -that is motivated by the concern that hostile code could abuse this -feature to exhaust available storage capacity. -However, in the case of Electron, where the application developers -have control over all of the code being executed, -this safety precaution becomes a hindrance that does not add much value. -For example, if a malicious developer wanted to consume disk space -on a victim's machine they could do so via Node's native file system API. +Increasing the quota rather than bypassing it reduces the +amount of chromium code that needs to be changed for Electron +as well as keeps these storage areas limited to a bounded +size meanwhile giving application developers more space to work with. -By disabling this restriction or increasing the quota, -electron application developers can use `localStorage` -as their application's "back end", without being having -to limit the amount of data stored to 10MiB. - -There may still be some benefit to keeping this restriction for applications that load remote content. -Although all remote data should be from a trusted source and transferred using -a secure channel, it is nevertheless advisable to include additional layers of protection -to mitigate risks associated with potential compromise of those other technologies. -With that in mind, an acceptable alternative to disabling the limit at compile-time -(as this patch currently does) would be to instead allow it to be disabled at run-time -for a given `BrowserWindow` via a `webPreferences` option, -similar to [`nodeIntegration`](https://electronjs.org/docs/tutorial/security#2-disable-nodejs-integration-for-remote-content). - -diff --git a/content/browser/dom_storage/dom_storage_types.h b/content/browser/dom_storage/dom_storage_types.h -index 6c0b831ebaaa2c1749bbc7436ce1025656588310..b67767751cadc6072c133297c7a6cdcc6bfd0c98 100644 ---- a/content/browser/dom_storage/dom_storage_types.h -+++ b/content/browser/dom_storage/dom_storage_types.h -@@ -21,6 +21,7 @@ typedef std::map DOMStorageValuesMap; - - // The quota for each storage area. - // This value is enforced in renderer processes and the browser process. -+// However, Electron's dom_storage_limits.patch removes the code that checks this limit. - const size_t kPerStorageAreaQuota = 10 * 1024 * 1024; - - // In the browser process we allow some overage to -diff --git a/third_party/blink/renderer/modules/storage/cached_storage_area.cc b/third_party/blink/renderer/modules/storage/cached_storage_area.cc -index d91fdc2a7d52307126bc04d44167edadb8c743a8..630acfca527aaec44742d45e47ce29d7754e3385 100644 ---- a/third_party/blink/renderer/modules/storage/cached_storage_area.cc -+++ b/third_party/blink/renderer/modules/storage/cached_storage_area.cc -@@ -107,11 +107,13 @@ bool CachedStorageArea::SetItem(const String& key, - Source* source) { - DCHECK(areas_->Contains(source)); +diff --git a/components/services/storage/dom_storage/dom_storage_constants.cc b/components/services/storage/dom_storage/dom_storage_constants.cc +index 3fd108c89c3b070a08790850db4dfd6cc8a3ce44..c393f51709efd8b28b07edfe452d2b84b14983ae 100644 +--- a/components/services/storage/dom_storage/dom_storage_constants.cc ++++ b/components/services/storage/dom_storage/dom_storage_constants.cc +@@ -6,7 +6,9 @@ -+#if 0 - // A quick check to reject obviously overbudget items to avoid priming the - // cache. - if ((key.length() + value.length()) * 2 > - mojom::blink::StorageArea::kPerStorageAreaQuota) - return false; -+#endif + namespace storage { - EnsureLoaded(); - String old_value; -diff --git a/third_party/blink/renderer/modules/storage/storage_area_map.cc b/third_party/blink/renderer/modules/storage/storage_area_map.cc -index 0da8a1e891edad60355792c40b7d15e90c1086e8..df71418d598d5bdf41e9a8a4340999d9d277aeef 100644 ---- a/third_party/blink/renderer/modules/storage/storage_area_map.cc -+++ b/third_party/blink/renderer/modules/storage/storage_area_map.cc -@@ -113,10 +113,12 @@ bool StorageAreaMap::SetItemInternal(const String& key, - size_t new_quota_used = quota_used_ - old_item_size + new_item_size; - size_t new_memory_used = memory_used_ - old_item_memory + new_item_memory; +-const size_t kPerStorageAreaQuota = 10 * 1024 * 1024; ++// Electron's dom_storage_limits.patch increased this value from 10MiB to 100MiB ++const size_t kPerStorageAreaQuota = 100 * 1024 * 1024; ++ + const size_t kPerStorageAreaOverQuotaAllowance = 100 * 1024; -+#if 0 - // Only check quota if the size is increasing, this allows - // shrinking changes to pre-existing files that are over budget. - if (check_quota && new_item_size > old_item_size && new_quota_used > quota_) - return false; -+#endif + } // namespace storage +diff --git a/third_party/blink/public/mojom/dom_storage/storage_area.mojom b/third_party/blink/public/mojom/dom_storage/storage_area.mojom +index 1f1b2c6fa109aa52c4e7c7f5fcaac91beb538449..d8f15eed9be83340ffd1f2400df08558e9554710 100644 +--- a/third_party/blink/public/mojom/dom_storage/storage_area.mojom ++++ b/third_party/blink/public/mojom/dom_storage/storage_area.mojom +@@ -40,7 +40,8 @@ interface StorageAreaGetAllCallback { + interface StorageArea { + // The quota for each storage area. + // This value is enforced in renderer processes and the browser process. +- const uint32 kPerStorageAreaQuota = 10485760; // 10 MiB ++ // Electron's dom_storage_limits.patch increased this value from 10MiB to 100MiB ++ const uint32 kPerStorageAreaQuota = 104857600; // 100 MiB - keys_values_.Set(key, value); - ResetKeyIterator(); + // In the browser process we allow some overage to + // accommodate concurrent writes from different renderers diff --git a/patches/chromium/exclude-a-few-test-files-from-build.patch b/patches/chromium/exclude-a-few-test-files-from-build.patch deleted file mode 100644 index 132130f618f06..0000000000000 --- a/patches/chromium/exclude-a-few-test-files-from-build.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aleksei Kuzmin -Date: Thu, 20 Sep 2018 17:50:45 -0700 -Subject: exclude-a-few-test-files-from-build.patch - -Compilation of those files fails with the Chromium 68. -Remove the patch during the Chromium 69 upgrade. - -diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn -index 854d6c6b9763cbec3a4dd74635726e2c0e7c6944..4fe9ee97104c2b021d00a661ff7dbb7fe9a6f681 100644 ---- a/third_party/blink/renderer/platform/BUILD.gn -+++ b/third_party/blink/renderer/platform/BUILD.gn -@@ -1781,7 +1781,7 @@ jumbo_source_set("blink_platform_unittests_sources") { - "graphics/paint/drawing_display_item_test.cc", - "graphics/paint/drawing_recorder_test.cc", - "graphics/paint/float_clip_rect_test.cc", -- "graphics/paint/geometry_mapper_test.cc", -+ #"graphics/paint/geometry_mapper_test.cc", - "graphics/paint/geometry_mapper_transform_cache_test.cc", - "graphics/paint/paint_chunk_test.cc", - "graphics/paint/paint_chunker_test.cc", diff --git a/patches/chromium/expose_setuseragent_on_networkcontext.patch b/patches/chromium/expose_setuseragent_on_networkcontext.patch index bff46f47af4bf..99a91478880ca 100644 --- a/patches/chromium/expose_setuseragent_on_networkcontext.patch +++ b/patches/chromium/expose_setuseragent_on_networkcontext.patch @@ -33,10 +33,10 @@ index 0ccfe130f00ec3b6c75cd8ee04d5a2777e1fd00c..653829457d58bf92057cc36aa8a28970 DISALLOW_COPY_AND_ASSIGN(StaticHttpUserAgentSettings); }; diff --git a/services/network/network_context.cc b/services/network/network_context.cc -index b9a8a12b8abb386fec548a26e4686af2e3822b8c..18af342553139669e485e5c34961de231f927f4c 100644 +index a3b4372d74c2cc792f3f26d03200a41ca8aafa73..97f707ece42711a9d22ca204f48755493abb26d6 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc -@@ -928,6 +928,13 @@ void NetworkContext::SetNetworkConditions( +@@ -969,6 +969,13 @@ void NetworkContext::SetNetworkConditions( std::move(network_conditions)); } @@ -51,10 +51,10 @@ index b9a8a12b8abb386fec548a26e4686af2e3822b8c..18af342553139669e485e5c34961de23 // This may only be called on NetworkContexts created with the constructor // that calls MakeURLRequestContext(). diff --git a/services/network/network_context.h b/services/network/network_context.h -index e98063a71435ee8828f3b5a21b569325965f7c58..dd34e65752d54b193965b7e7fea2cae324782204 100644 +index ffa991ec6020841a33df7870326b52f0ae943846..b82094db17360ffb5c32b347f1b5ba100fa48699 100644 --- a/services/network/network_context.h +++ b/services/network/network_context.h -@@ -222,6 +222,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext +@@ -230,6 +230,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext void CloseIdleConnections(CloseIdleConnectionsCallback callback) override; void SetNetworkConditions(const base::UnguessableToken& throttling_profile_id, mojom::NetworkConditionsPtr conditions) override; @@ -63,10 +63,10 @@ index e98063a71435ee8828f3b5a21b569325965f7c58..dd34e65752d54b193965b7e7fea2cae3 void SetEnableReferrers(bool enable_referrers) override; #if defined(OS_CHROMEOS) diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index 4691fde53fd7d6ff6e89c55a7fec88d77b39701c..33021fcda6d2cbe46225ba8cf56e137ee7ef9912 100644 +index 1ba183b6e6caf064fd003d1cf5e8af4ebc4ba869..691f9b0f6f658cc259cc9c4e8bb3abb15592e8a3 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom -@@ -912,6 +912,9 @@ interface NetworkContext { +@@ -971,6 +971,9 @@ interface NetworkContext { SetNetworkConditions(mojo_base.mojom.UnguessableToken throttling_profile_id, NetworkConditions? conditions); @@ -77,10 +77,10 @@ index 4691fde53fd7d6ff6e89c55a7fec88d77b39701c..33021fcda6d2cbe46225ba8cf56e137e SetAcceptLanguage(string new_accept_language); diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h -index 4fe68bb40d8e009e9765ed884bbf898a10b86124..a84cf0a1b07751bbcad9b193a4a3a1683e0db80c 100644 +index b1d38691d37d0319f746d0db21d21ba7bec83097..40996126da04fea86452949e11b4595badbf7fdb 100644 --- a/services/network/test/test_network_context.h +++ b/services/network/test/test_network_context.h -@@ -98,6 +98,7 @@ class TestNetworkContext : public mojom::NetworkContext { +@@ -99,6 +99,7 @@ class TestNetworkContext : public mojom::NetworkContext { void CloseIdleConnections(CloseIdleConnectionsCallback callback) override {} void SetNetworkConditions(const base::UnguessableToken& throttling_profile_id, mojom::NetworkConditionsPtr conditions) override {} diff --git a/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch b/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch index 96bd8eed86115..1c4fde9b50d2c 100644 --- a/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch +++ b/patches/chromium/feat_add_set_theme_source_to_allow_apps_to.patch @@ -13,7 +13,7 @@ uses internally for things like menus and devtools. We can remove this patch once it has in some shape been upstreamed. diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc -index 5fd3bb3b60bda4449cc4743922758d62ee6decac..860b92c47f42d7517adb5e3370cc02d7e1c67b62 100644 +index d9175ae9661218b58e9b587ad3a9dbd540bfd384..ed178ea7b3d7f8039f47402bc08cae20e74aa789 100644 --- a/ui/native_theme/native_theme.cc +++ b/ui/native_theme/native_theme.cc @@ -40,6 +40,8 @@ NativeTheme::NativeTheme() @@ -26,10 +26,10 @@ index 5fd3bb3b60bda4449cc4743922758d62ee6decac..860b92c47f42d7517adb5e3370cc02d7 } diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h -index ef45da79366c2e3ab242ec140989b407ac70dbb8..98ca8881b0b9f68a464e62f76589b29157126a86 100644 +index 205181053803813b64497884bc93cfd857254866..7b14086b43eeb86baa0f8471e15e7f6f19193889 100644 --- a/ui/native_theme/native_theme.h +++ b/ui/native_theme/native_theme.h -@@ -429,6 +429,22 @@ class NATIVE_THEME_EXPORT NativeTheme { +@@ -431,6 +431,38 @@ class NATIVE_THEME_EXPORT NativeTheme { ColorId color_id, ColorScheme color_scheme = ColorScheme::kDefault) const = 0; @@ -44,15 +44,31 @@ index ef45da79366c2e3ab242ec140989b407ac70dbb8..98ca8881b0b9f68a464e62f76589b291 + } + + void set_theme_source(ThemeSource theme_source) { -+ bool original = ShouldUseDarkColors(); ++ bool original_dark_colors = ShouldUseDarkColors(); ++ PreferredColorScheme original_color_scheme = GetPreferredColorScheme(); ++ + theme_source_ = theme_source; -+ if (ShouldUseDarkColors() != original) NotifyObservers(); ++ ++ bool changed = false; ++ if (ShouldUseDarkColors() != original_dark_colors) { ++ changed = true; ++ } ++ ++ PreferredColorScheme new_color_scheme = CalculatePreferredColorScheme(); ++ if (new_color_scheme != original_color_scheme) { ++ set_preferred_color_scheme(new_color_scheme); ++ changed = true; ++ } ++ ++ if (changed) { ++ NotifyObservers(); ++ } + } + // Returns a shared instance of the native theme that should be used for web // rendering. Do not use it in a normal application context (i.e. browser). // The returned object should not be deleted by the caller. This function is -@@ -550,6 +566,8 @@ class NATIVE_THEME_EXPORT NativeTheme { +@@ -555,6 +587,8 @@ class NATIVE_THEME_EXPORT NativeTheme { PreferredColorScheme preferred_color_scheme_ = PreferredColorScheme::kNoPreference; @@ -62,10 +78,10 @@ index ef45da79366c2e3ab242ec140989b407ac70dbb8..98ca8881b0b9f68a464e62f76589b291 }; diff --git a/ui/native_theme/native_theme_dark_aura.cc b/ui/native_theme/native_theme_dark_aura.cc -index a8fbfee3b13672902aac05fd5a65fa8ee81f9f7e..1be6369acf0b7c02a6f862636c2b2de1fbf8cb5a 100644 +index a8fbfee3b13672902aac05fd5a65fa8ee81f9f7e..80770f0e46641f0d4ea81c6fa6602a68c1f9b9a8 100644 --- a/ui/native_theme/native_theme_dark_aura.cc +++ b/ui/native_theme/native_theme_dark_aura.cc -@@ -20,6 +20,8 @@ SkColor NativeThemeDarkAura::GetSystemColor(ColorId color_id, +@@ -20,12 +20,14 @@ SkColor NativeThemeDarkAura::GetSystemColor(ColorId color_id, } bool NativeThemeDarkAura::ShouldUseDarkColors() const { @@ -74,11 +90,18 @@ index a8fbfee3b13672902aac05fd5a65fa8ee81f9f7e..1be6369acf0b7c02a6f862636c2b2de1 return true; } + NativeTheme::PreferredColorScheme NativeThemeDarkAura::GetPreferredColorScheme() + const { +- return NativeTheme::PreferredColorScheme::kDark; ++ return ShouldUseDarkColors() ? NativeTheme::PreferredColorScheme::kDark : NativeTheme::PreferredColorScheme::kLight; + } + + NativeThemeDarkAura::NativeThemeDarkAura() : NativeThemeAura(false) {} diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc -index 97e72bc38a7231ce295ad943e977c69586cc6e07..a60f7b618ddd29886aac70f16e07cbc871ce38af 100644 +index cff7681a4e0023a67edda37bf9865ab073b32549..83507081e0734b53fd37133014256243dda6864d 100644 --- a/ui/native_theme/native_theme_win.cc +++ b/ui/native_theme/native_theme_win.cc -@@ -725,6 +725,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const { +@@ -706,6 +706,8 @@ bool NativeThemeWin::ShouldUseDarkColors() const { // ...unless --force-dark-mode was specified in which case caveat emptor. if (UsesHighContrastColors() && !IsForcedDarkMode()) return false; diff --git a/patches/chromium/feat_add_support_for_overriding_the_base_spellchecker_download_url.patch b/patches/chromium/feat_add_support_for_overriding_the_base_spellchecker_download_url.patch new file mode 100644 index 0000000000000..e019e694904ac --- /dev/null +++ b/patches/chromium/feat_add_support_for_overriding_the_base_spellchecker_download_url.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Tue, 25 Feb 2020 13:28:30 -0800 +Subject: feat: add support for overriding the base spellchecker download URL + +This patch is required as the testing-only method we were using does not +take into account the dictionary name and therefore will not work for +production use cases. This is unlikely to be upstreamed as the change +is entirely in //chrome. + +diff --git a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc +index 8f1c8d53401694d522ee82f20a3e2aaba2734191..67ec4e285d9bf6b9de300845b2c53bda435e5784 100644 +--- a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc ++++ b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.cc +@@ -48,6 +48,9 @@ namespace { + base::LazyInstance::Leaky g_download_url_for_testing = + LAZY_INSTANCE_INITIALIZER; + ++base::LazyInstance::Leaky g_base_download_url_override = ++ LAZY_INSTANCE_INITIALIZER; ++ + // Close the file. + void CloseDictionary(base::File file) { + base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, +@@ -248,6 +251,10 @@ void SpellcheckHunspellDictionary::SetDownloadURLForTesting(const GURL url) { + g_download_url_for_testing.Get() = url; + } + ++void SpellcheckHunspellDictionary::SetBaseDownloadURL(const GURL url) { ++ g_base_download_url_override.Get() = url; ++} ++ + GURL SpellcheckHunspellDictionary::GetDictionaryURL() { + if (g_download_url_for_testing.Get() != GURL()) + return g_download_url_for_testing.Get(); +@@ -255,6 +262,9 @@ GURL SpellcheckHunspellDictionary::GetDictionaryURL() { + std::string bdict_file = dictionary_file_.path.BaseName().MaybeAsASCII(); + DCHECK(!bdict_file.empty()); + ++ if (g_base_download_url_override.Get() != GURL()) ++ return GURL(g_base_download_url_override.Get().spec() + base::ToLowerASCII(bdict_file)); ++ + static const char kDownloadServerUrl[] = + "https://redirector.gvt1.com/edgedl/chrome/dict/"; + +diff --git a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h +index 074e9516d72239d530a2919d0018472a3539d048..633ec3a96b39824fc9bcf374e59eb80148a2ae27 100644 +--- a/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h ++++ b/chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h +@@ -85,6 +85,8 @@ class SpellcheckHunspellDictionary + // Tests use this method to set a custom URL for downloading dictionaries. + static void SetDownloadURLForTesting(const GURL url); + ++ static void SetBaseDownloadURL(const GURL url); ++ + private: + // Dictionary download status. + enum DownloadStatus { diff --git a/patches/chromium/feat_allow_disbaling_blink_scheduler_throttling_per_renderview.patch b/patches/chromium/feat_allow_disbaling_blink_scheduler_throttling_per_renderview.patch new file mode 100644 index 0000000000000..9d76231fe289c --- /dev/null +++ b/patches/chromium/feat_allow_disbaling_blink_scheduler_throttling_per_renderview.patch @@ -0,0 +1,159 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: deepak1556 +Date: Fri, 29 Nov 2019 16:08:14 -0800 +Subject: feat: allow disabling blink scheduler throttling per RenderView + + +diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc +index a73a05383e445103e08dd5f660897163ca50573d..7f7b05ff0ce28ec83e8df266e646ac282e15862e 100644 +--- a/content/browser/renderer_host/render_view_host_impl.cc ++++ b/content/browser/renderer_host/render_view_host_impl.cc +@@ -436,6 +436,10 @@ void RenderViewHostImpl::SetBackgroundOpaque(bool opaque) { + Send(new ViewMsg_SetBackgroundOpaque(GetRoutingID(), opaque)); + } + ++void RenderViewHostImpl::SetSchedulerThrottling(bool allowed) { ++ Send(new ViewMsg_SetSchedulerThrottling(GetRoutingID(), allowed)); ++} ++ + bool RenderViewHostImpl::IsMainFrameActive() { + return is_active(); + } +diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h +index 9e0d9c21c0a9cd947d568cb22049e003278ff857..354a35d27dc4c92c3d93249d3dcc148555debdaf 100644 +--- a/content/browser/renderer_host/render_view_host_impl.h ++++ b/content/browser/renderer_host/render_view_host_impl.h +@@ -108,6 +108,7 @@ class CONTENT_EXPORT RenderViewHostImpl + SiteInstanceImpl* GetSiteInstance() override; + bool IsRenderViewLive() override; + void NotifyMoveOrResizeStarted() override; ++ void SetSchedulerThrottling(bool allowed) override; + WebPreferences GetWebkitPreferences() override; + void UpdateWebkitPreferences(const WebPreferences& prefs) override; + void OnWebkitPreferencesChanged() override; +diff --git a/content/common/view_messages.h b/content/common/view_messages.h +index a609cc1fa1558987b634dcbc41120ddc93a711a0..3b3058398e97d2f21458ee427aeeee19c78ff649 100644 +--- a/content/common/view_messages.h ++++ b/content/common/view_messages.h +@@ -114,6 +114,9 @@ IPC_STRUCT_TRAITS_END() + // Make the RenderWidget background transparent or opaque. + IPC_MESSAGE_ROUTED1(ViewMsg_SetBackgroundOpaque, bool /* opaque */) + ++// Whether to enable the Renderer scheduler background throttling. ++IPC_MESSAGE_ROUTED1(ViewMsg_SetSchedulerThrottling, bool /* allowed */) ++ + // This passes a set of webkit preferences down to the renderer. + IPC_MESSAGE_ROUTED1(ViewMsg_UpdateWebPreferences, + content::WebPreferences) +diff --git a/content/public/browser/render_view_host.h b/content/public/browser/render_view_host.h +index 95679ab2915ad496ca0018aa13874b84eb11d7fd..c278e0fc072409677beafc7f252ebcf6002f16d4 100644 +--- a/content/public/browser/render_view_host.h ++++ b/content/public/browser/render_view_host.h +@@ -99,6 +99,9 @@ class CONTENT_EXPORT RenderViewHost : public IPC::Sender { + // started. + virtual void NotifyMoveOrResizeStarted() = 0; + ++ // Disable/Enable scheduler throttling. ++ virtual void SetSchedulerThrottling(bool allowed) = 0; ++ + // TODO(mustaq): Replace "Webkit" from the following three method names. + // + // Returns the current WebKit preferences. Note: WebPreferences is cached, so +diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc +index c90fd39058e3e0ab98727a71836e549ee794f7bd..ecbd941c58250d664f240dc3394eccf088d4c405 100644 +--- a/content/renderer/render_view_impl.cc ++++ b/content/renderer/render_view_impl.cc +@@ -1252,6 +1252,8 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) { + IPC_BEGIN_MESSAGE_MAP(RenderViewImpl, message) + IPC_MESSAGE_HANDLER(ViewMsg_SetPageScale, OnSetPageScale) + IPC_MESSAGE_HANDLER(ViewMsg_SetInitialFocus, OnSetInitialFocus) ++ IPC_MESSAGE_HANDLER(ViewMsg_SetSchedulerThrottling, ++ OnSetSchedulerThrottling) + IPC_MESSAGE_HANDLER(ViewMsg_UpdateTargetURL_ACK, OnUpdateTargetURLAck) + IPC_MESSAGE_HANDLER(ViewMsg_UpdateWebPreferences, OnUpdateWebPreferences) + IPC_MESSAGE_HANDLER(ViewMsg_ClosePage, OnClosePage) +@@ -1828,6 +1830,12 @@ void RenderViewImpl::OnSetPageScale(float page_scale_factor) { + webview()->SetPageScaleFactor(page_scale_factor); + } + ++void RenderViewImpl::OnSetSchedulerThrottling(bool allowed) { ++ if (!webview()) ++ return; ++ webview()->SetSchedulerThrottling(allowed); ++} ++ + void RenderViewImpl::ApplyPageVisibilityState( + PageVisibilityState visibility_state, + bool initial_setting) { +diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h +index 0dab88b784c6add42fc192ce5742b7df8a9260b3..10109052630967db13ac1e48bc8cb389ad437dc0 100644 +--- a/content/renderer/render_view_impl.h ++++ b/content/renderer/render_view_impl.h +@@ -449,6 +449,7 @@ class CONTENT_EXPORT RenderViewImpl : public blink::WebViewClient, + void OnSetInitialFocus(bool reverse); + void OnSetRendererPrefs( + const blink::mojom::RendererPreferences& renderer_prefs); ++ void OnSetSchedulerThrottling(bool allowed); + void OnSuppressDialogsUntilSwapOut(); + void OnUpdateTargetURLAck(); + void OnUpdateWebPreferences(const WebPreferences& prefs); +diff --git a/third_party/blink/public/web/web_view.h b/third_party/blink/public/web/web_view.h +index 53d6d4ebd22ad8f3dd9badba7601dcc27a002a2a..7abf1c05c8e821de8df26938e4f098a58a06655b 100644 +--- a/third_party/blink/public/web/web_view.h ++++ b/third_party/blink/public/web/web_view.h +@@ -404,6 +404,7 @@ class WebView { + // Scheduling ----------------------------------------------------------- + + virtual PageScheduler* Scheduler() const = 0; ++ virtual void SetSchedulerThrottling(bool allowed) = 0; + + // Visibility ----------------------------------------------------------- + +diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc +index 2e2b95750635fa5aedc17eabcd84fa97ed0c951a..7649f65d5f05c4609c495ddf4d47f640a010d7e4 100644 +--- a/third_party/blink/renderer/core/exported/web_view_impl.cc ++++ b/third_party/blink/renderer/core/exported/web_view_impl.cc +@@ -3364,12 +3364,20 @@ PageScheduler* WebViewImpl::Scheduler() const { + return GetPage()->GetPageScheduler(); + } + ++void WebViewImpl::SetSchedulerThrottling(bool allowed) { ++ DCHECK(GetPage()); ++ scheduler_throttling_allowed_ = allowed; ++ GetPage()->GetPageScheduler()->SetPageVisible(allowed ? ++ (GetVisibilityState() == PageVisibilityState::kVisible) : true); ++} ++ + void WebViewImpl::SetVisibilityState(PageVisibilityState visibility_state, + bool is_initial_state) { + DCHECK(GetPage()); + GetPage()->SetVisibilityState(visibility_state, is_initial_state); +- GetPage()->GetPageScheduler()->SetPageVisible(visibility_state == +- PageVisibilityState::kVisible); ++ GetPage()->GetPageScheduler()->SetPageVisible( ++ scheduler_throttling_allowed_ ? ++ (visibility_state == PageVisibilityState::kVisible) : true); + } + + PageVisibilityState WebViewImpl::GetVisibilityState() { +diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h +index 7161adf93687544024d7cc9d23fe423bb4e9eca3..d55296a159e9453add9a8a63058b5a5647cf5b3e 100644 +--- a/third_party/blink/renderer/core/exported/web_view_impl.h ++++ b/third_party/blink/renderer/core/exported/web_view_impl.h +@@ -310,6 +310,7 @@ class CORE_EXPORT WebViewImpl final : public WebView, + LocalDOMWindow* PagePopupWindow() const; + + PageScheduler* Scheduler() const override; ++ void SetSchedulerThrottling(bool allowed) override; + void SetVisibilityState(PageVisibilityState visibility_state, + bool is_initial_state) override; + PageVisibilityState GetVisibilityState() override; +@@ -682,6 +683,8 @@ class CORE_EXPORT WebViewImpl final : public WebView, + // WebViewImpl::Close while handling an input event. + bool debug_inside_input_handling_ = false; + ++ bool scheduler_throttling_allowed_ = true; ++ + FloatSize elastic_overscroll_; + + Persistent popup_mouse_wheel_event_listener_; diff --git a/patches/chromium/feat_allow_embedders_to_add_observers_on_created_hunspell.patch b/patches/chromium/feat_allow_embedders_to_add_observers_on_created_hunspell.patch new file mode 100644 index 0000000000000..55b85cda282ee --- /dev/null +++ b/patches/chromium/feat_allow_embedders_to_add_observers_on_created_hunspell.patch @@ -0,0 +1,63 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Sun, 1 Mar 2020 16:33:55 -0800 +Subject: feat: allow embedders to add observers on created hunspell + dictionaries + +diff --git a/chrome/browser/spellchecker/spellcheck_service.cc b/chrome/browser/spellchecker/spellcheck_service.cc +index 6a508168067b70822fabee5d50a5be7ac2ed15a5..5afbef4fb24441a850c45703b2d7a66b8f92abb1 100644 +--- a/chrome/browser/spellchecker/spellcheck_service.cc ++++ b/chrome/browser/spellchecker/spellcheck_service.cc +@@ -266,6 +266,9 @@ void SpellcheckService::LoadHunspellDictionaries() { + std::make_unique(dictionary, context_, + this)); + hunspell_dictionaries_.back()->AddObserver(this); ++ if (hunspell_observer_) { ++ hunspell_dictionaries_.back()->AddObserver(hunspell_observer_); ++ } + hunspell_dictionaries_.back()->Load(); + } + +@@ -279,6 +282,20 @@ SpellcheckService::GetHunspellDictionaries() { + return hunspell_dictionaries_; + } + ++void SpellcheckService::SetHunspellObserver(SpellcheckHunspellDictionary::Observer* observer) { ++ if (hunspell_observer_) { ++ for (auto& dict : hunspell_dictionaries_) { ++ dict->RemoveObserver(hunspell_observer_); ++ } ++ } ++ if (observer) { ++ for (auto& dict : hunspell_dictionaries_) { ++ dict->AddObserver(observer); ++ } ++ } ++ hunspell_observer_ = observer; ++} ++ + bool SpellcheckService::LoadExternalDictionary(std::string language, + std::string locale, + std::string path, +diff --git a/chrome/browser/spellchecker/spellcheck_service.h b/chrome/browser/spellchecker/spellcheck_service.h +index d5af9d5829c362743e64a9f2fefdf58a340c4b9f..8c00b16996192a4b65f4794e0018b50559bbe1c1 100644 +--- a/chrome/browser/spellchecker/spellcheck_service.h ++++ b/chrome/browser/spellchecker/spellcheck_service.h +@@ -114,6 +114,8 @@ class SpellcheckService : public KeyedService, + const std::vector>& + GetHunspellDictionaries(); + ++ void SetHunspellObserver(SpellcheckHunspellDictionary::Observer* observer); ++ + // Load a dictionary from a given path. Format specifies how the dictionary + // is stored. Return value is true if successful. + bool LoadExternalDictionary(std::string language, +@@ -195,6 +197,8 @@ class SpellcheckService : public KeyedService, + // A pointer to the BrowserContext which this service refers to. + content::BrowserContext* context_; + ++ SpellcheckHunspellDictionary::Observer* hunspell_observer_ = nullptr; ++ + std::unique_ptr metrics_; + + std::unique_ptr custom_dictionary_; diff --git a/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch new file mode 100644 index 0000000000000..20ce0026b9e29 --- /dev/null +++ b/patches/chromium/feat_enable_offscreen_rendering_with_viz_compositor.patch @@ -0,0 +1,636 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andy Dill +Date: Thu, 30 Jan 2020 09:36:07 -0800 +Subject: feat: enable off-screen rendering with viz compositor + +This patch adds hooks in the relevant places that allow for off-screen +rendering with the viz compositor by way of a custom HostDisplayClient +and LayeredWindowUpdater. + +diff --git a/components/viz/host/host_display_client.cc b/components/viz/host/host_display_client.cc +index 3547ee865c220731a6b4be4c1b32ec899b3dd11f..715bfa4b5db5fc4beb83c26b1eedda82c87a245b 100644 +--- a/components/viz/host/host_display_client.cc ++++ b/components/viz/host/host_display_client.cc +@@ -43,9 +43,13 @@ void HostDisplayClient::OnDisplayReceivedCALayerParams( + } + #endif + +-#if defined(OS_WIN) ++void HostDisplayClient::IsOffscreen(IsOffscreenCallback callback) { ++ std::move(callback).Run(false); ++} ++ + void HostDisplayClient::CreateLayeredWindowUpdater( + mojo::PendingReceiver receiver) { ++#if defined(OS_WIN) + if (!NeedsToUseLayerWindow(widget_)) { + DLOG(ERROR) << "HWND shouldn't be using a layered window"; + return; +@@ -53,8 +57,12 @@ void HostDisplayClient::CreateLayeredWindowUpdater( + + layered_window_updater_ = + std::make_unique(widget_, std::move(receiver)); +-} ++#else ++ CHECK(false) << "Chromium is calling CreateLayeredWindowUpdater for non-OSR " ++ "windows on POSIX platforms, something is wrong with " ++ "Electron's OSR implementation."; + #endif ++} + + #if defined(OS_LINUX) && !defined(OS_CHROMEOS) + void HostDisplayClient::DidCompleteSwapWithNewSize(const gfx::Size& size) { +diff --git a/components/viz/host/host_display_client.h b/components/viz/host/host_display_client.h +index cedf833d23589dc3157168cfc7e6d3bb12939f7a..4437a7875076ef5a99ddcd767036ec37c2d48d44 100644 +--- a/components/viz/host/host_display_client.h ++++ b/components/viz/host/host_display_client.h +@@ -31,17 +31,17 @@ class VIZ_HOST_EXPORT HostDisplayClient : public mojom::DisplayClient { + mojo::PendingRemote GetBoundRemote( + scoped_refptr task_runner); + +- private: ++ protected: + // mojom::DisplayClient implementation: ++ void IsOffscreen(IsOffscreenCallback callback) override; ++ + #if defined(OS_MACOSX) + void OnDisplayReceivedCALayerParams( + const gfx::CALayerParams& ca_layer_params) override; + #endif + +-#if defined(OS_WIN) + void CreateLayeredWindowUpdater( + mojo::PendingReceiver receiver) override; +-#endif + + #if defined(OS_LINUX) && !defined(OS_CHROMEOS) + void DidCompleteSwapWithNewSize(const gfx::Size& size) override; +diff --git a/components/viz/host/layered_window_updater_impl.cc b/components/viz/host/layered_window_updater_impl.cc +index b04f654fe820f821b18e059cdd40085fc2384c4e..ee22012b01ef92bb3b32b5b1081609a7bdfb0d93 100644 +--- a/components/viz/host/layered_window_updater_impl.cc ++++ b/components/viz/host/layered_window_updater_impl.cc +@@ -44,7 +44,9 @@ void LayeredWindowUpdaterImpl::OnAllocatedSharedMemory( + // |region|'s handle will close when it goes out of scope. + } + +-void LayeredWindowUpdaterImpl::Draw(DrawCallback draw_callback) { ++void LayeredWindowUpdaterImpl::Draw( ++ const gfx::Rect& damage_rect, ++ DrawCallback draw_callback) { + TRACE_EVENT0("viz", "LayeredWindowUpdaterImpl::Draw"); + + if (!canvas_) { +diff --git a/components/viz/host/layered_window_updater_impl.h b/components/viz/host/layered_window_updater_impl.h +index 1026b739d283f0fc252fa2af83a6d4cf51bc8553..fe562ab60ce98b8bb0c5080a6428deb319a4dd04 100644 +--- a/components/viz/host/layered_window_updater_impl.h ++++ b/components/viz/host/layered_window_updater_impl.h +@@ -35,7 +35,7 @@ class VIZ_HOST_EXPORT LayeredWindowUpdaterImpl + // mojom::LayeredWindowUpdater implementation. + void OnAllocatedSharedMemory(const gfx::Size& pixel_size, + base::UnsafeSharedMemoryRegion region) override; +- void Draw(DrawCallback draw_callback) override; ++ void Draw(const gfx::Rect& damage_rect, DrawCallback draw_callback) override; + + private: + const HWND hwnd_; +diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn +index ca67f58b73cc13b1e0895b17d69c9781c567afa1..cddf6d35eaa8fbe36844211dd9798605452d9e9a 100644 +--- a/components/viz/service/BUILD.gn ++++ b/components/viz/service/BUILD.gn +@@ -121,6 +121,8 @@ viz_component("service") { + "display_embedder/output_surface_provider_impl.h", + "display_embedder/server_shared_bitmap_manager.cc", + "display_embedder/server_shared_bitmap_manager.h", ++ "display_embedder/software_output_device_proxy.cc", ++ "display_embedder/software_output_device_proxy.h", + "display_embedder/software_output_surface.cc", + "display_embedder/software_output_surface.h", + "display_embedder/viz_process_context_provider.cc", +diff --git a/components/viz/service/display_embedder/output_surface_provider_impl.cc b/components/viz/service/display_embedder/output_surface_provider_impl.cc +index 6f2eac863d1b82ea1739b369861ada2e626627de..1011080626807dd6baf6c08f9b704905cba4e3c8 100644 +--- a/components/viz/service/display_embedder/output_surface_provider_impl.cc ++++ b/components/viz/service/display_embedder/output_surface_provider_impl.cc +@@ -20,6 +20,7 @@ + #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" + #include "components/viz/service/display_embedder/skia_output_surface_dependency_impl.h" + #include "components/viz/service/display_embedder/skia_output_surface_impl.h" ++#include "components/viz/service/display_embedder/software_output_device_proxy.h" + #include "components/viz/service/display_embedder/software_output_surface.h" + #include "components/viz/service/display_embedder/viz_process_context_provider.h" + #include "components/viz/service/gl/gpu_service_impl.h" +@@ -33,6 +34,7 @@ + #include "gpu/ipc/scheduler_sequence.h" + #include "gpu/ipc/service/gpu_channel_manager_delegate.h" + #include "gpu/ipc/service/image_transport_surface.h" ++#include "services/viz/privileged/mojom/compositing/layered_window_updater.mojom.h" + #include "ui/base/ui_base_switches.h" + #include "ui/gl/gl_context.h" + #include "ui/gl/init/gl_factory.h" +@@ -227,6 +229,22 @@ OutputSurfaceProviderImpl::CreateSoftwareOutputDeviceForPlatform( + if (headless_) + return std::make_unique(); + ++#if !defined(OS_MACOSX) ++ { ++ mojo::ScopedAllowSyncCallForTesting allow_sync; ++ DCHECK(display_client); ++ bool offscreen = false; ++ if (display_client->IsOffscreen(&offscreen) && offscreen) { ++ mojom::LayeredWindowUpdaterPtr layered_window_updater; ++ display_client->CreateLayeredWindowUpdater( ++ mojo::MakeRequest(&layered_window_updater)); ++ ++ return std::make_unique( ++ std::move(layered_window_updater)); ++ } ++ } ++#endif ++ + #if defined(OS_WIN) + return CreateSoftwareOutputDeviceWin(surface_handle, &output_device_backing_, + display_client); +diff --git a/components/viz/service/display_embedder/software_output_device_mac.cc b/components/viz/service/display_embedder/software_output_device_mac.cc +index b9357082293cc55650144ccbc8bada8fe6d1cac4..b4cb07e26d1504719f80e5835c1cb5f138b9f1ab 100644 +--- a/components/viz/service/display_embedder/software_output_device_mac.cc ++++ b/components/viz/service/display_embedder/software_output_device_mac.cc +@@ -102,6 +102,8 @@ void SoftwareOutputDeviceMac::UpdateAndCopyBufferDamage( + + SkCanvas* SoftwareOutputDeviceMac::BeginPaint( + const gfx::Rect& new_damage_rect) { ++ last_damage = new_damage_rect; ++ + // Record the previous paint buffer. + Buffer* previous_paint_buffer = + buffer_queue_.empty() ? nullptr : buffer_queue_.back().get(); +@@ -184,6 +186,7 @@ void SoftwareOutputDeviceMac::EndPaint() { + ca_layer_params.is_empty = false; + ca_layer_params.scale_factor = scale_factor_; + ca_layer_params.pixel_size = pixel_size_; ++ ca_layer_params.damage = last_damage; + ca_layer_params.io_surface_mach_port.reset( + IOSurfaceCreateMachPort(current_paint_buffer_->io_surface)); + client_->SoftwareDeviceUpdatedCALayerParams(ca_layer_params); +diff --git a/components/viz/service/display_embedder/software_output_device_mac.h b/components/viz/service/display_embedder/software_output_device_mac.h +index f3867356e3d641416e00e6d115ae9ae2a0be90ab..b1d192d2b20ccb63fba07093101d745e5ffe86dd 100644 +--- a/components/viz/service/display_embedder/software_output_device_mac.h ++++ b/components/viz/service/display_embedder/software_output_device_mac.h +@@ -56,6 +56,7 @@ class VIZ_SERVICE_EXPORT SoftwareOutputDeviceMac : public SoftwareOutputDevice { + void UpdateAndCopyBufferDamage(Buffer* previous_paint_buffer, + const SkRegion& new_damage_rect); + ++ gfx::Rect last_damage; + gfx::Size pixel_size_; + float scale_factor_ = 1; + +diff --git a/components/viz/service/display_embedder/software_output_device_proxy.cc b/components/viz/service/display_embedder/software_output_device_proxy.cc +new file mode 100644 +index 0000000000000000000000000000000000000000..4efea02f80f8b6818291321a7c63f0f4815a5b98 +--- /dev/null ++++ b/components/viz/service/display_embedder/software_output_device_proxy.cc +@@ -0,0 +1,157 @@ ++// Copyright 2014 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/viz/service/display_embedder/software_output_device_proxy.h" ++ ++#include "base/memory/unsafe_shared_memory_region.h" ++#include "base/threading/thread_checker.h" ++#include "base/trace_event/trace_event.h" ++#include "components/viz/common/resources/resource_sizes.h" ++#include "components/viz/service/display_embedder/output_device_backing.h" ++#include "mojo/public/cpp/system/platform_handle.h" ++#include "services/viz/privileged/mojom/compositing/layered_window_updater.mojom.h" ++#include "skia/ext/platform_canvas.h" ++#include "third_party/skia/include/core/SkCanvas.h" ++#include "ui/gfx/skia_util.h" ++ ++#if defined(OS_WIN) ++#include "skia/ext/skia_utils_win.h" ++#include "ui/gfx/gdi_util.h" ++#include "ui/gfx/win/hwnd_util.h" ++#else ++#include "mojo/public/cpp/base/shared_memory_utils.h" ++#endif ++ ++namespace viz { ++ ++SoftwareOutputDeviceBase::~SoftwareOutputDeviceBase() { ++ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); ++ DCHECK(!in_paint_); ++} ++ ++void SoftwareOutputDeviceBase::Resize(const gfx::Size& viewport_pixel_size, ++ float scale_factor) { ++ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); ++ DCHECK(!in_paint_); ++ ++ if (viewport_pixel_size_ == viewport_pixel_size) ++ return; ++ ++ viewport_pixel_size_ = viewport_pixel_size; ++ ResizeDelegated(); ++} ++ ++SkCanvas* SoftwareOutputDeviceBase::BeginPaint( ++ const gfx::Rect& damage_rect) { ++ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); ++ DCHECK(!in_paint_); ++ ++ damage_rect_ = damage_rect; ++ in_paint_ = true; ++ return BeginPaintDelegated(); ++} ++ ++void SoftwareOutputDeviceBase::EndPaint() { ++ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); ++ DCHECK(in_paint_); ++ ++ in_paint_ = false; ++ ++ gfx::Rect intersected_damage_rect = damage_rect_; ++ intersected_damage_rect.Intersect(gfx::Rect(viewport_pixel_size_)); ++ if (intersected_damage_rect.IsEmpty()) ++ return; ++ ++ EndPaintDelegated(intersected_damage_rect); ++} ++ ++SoftwareOutputDeviceProxy::~SoftwareOutputDeviceProxy() = default; ++ ++SoftwareOutputDeviceProxy::SoftwareOutputDeviceProxy( ++ mojom::LayeredWindowUpdaterPtr layered_window_updater) ++ : layered_window_updater_(std::move(layered_window_updater)) { ++ DCHECK(layered_window_updater_.is_bound()); ++} ++ ++void SoftwareOutputDeviceProxy::OnSwapBuffers( ++ SoftwareOutputDevice::SwapBuffersCallback swap_ack_callback) { ++ DCHECK(swap_ack_callback_.is_null()); ++ ++ // We aren't waiting on DrawAck() and can immediately run the callback. ++ if (!waiting_on_draw_ack_) { ++ task_runner_->PostTask(FROM_HERE, ++ base::BindOnce(std::move(swap_ack_callback), viewport_pixel_size_)); ++ return; ++ } ++ ++ swap_ack_callback_ = std::move(swap_ack_callback); ++} ++ ++void SoftwareOutputDeviceProxy::ResizeDelegated() { ++ canvas_.reset(); ++ ++ size_t required_bytes; ++ if (!ResourceSizes::MaybeSizeInBytes( ++ viewport_pixel_size_, ResourceFormat::RGBA_8888, &required_bytes)) { ++ DLOG(ERROR) << "Invalid viewport size " << viewport_pixel_size_.ToString(); ++ return; ++ } ++ ++ base::UnsafeSharedMemoryRegion region = ++ base::UnsafeSharedMemoryRegion::Create(required_bytes); ++ if (!region.IsValid()) { ++ DLOG(ERROR) << "Failed to allocate " << required_bytes << " bytes"; ++ return; ++ } ++ ++ #if defined(WIN32) ++ canvas_ = skia::CreatePlatformCanvasWithSharedSection( ++ viewport_pixel_size_.width(), viewport_pixel_size_.height(), false, ++ region.GetPlatformHandle(), skia::CRASH_ON_FAILURE); ++ #else ++ shm_mapping_ = region.Map(); ++ if (!shm_mapping_.IsValid()) { ++ DLOG(ERROR) << "Failed to map " << required_bytes << " bytes"; ++ return; ++ } ++ ++ canvas_ = skia::CreatePlatformCanvasWithPixels( ++ viewport_pixel_size_.width(), viewport_pixel_size_.height(), false, ++ static_cast(shm_mapping_.memory()), skia::CRASH_ON_FAILURE); ++ #endif ++ ++ // Transfer region ownership to the browser process. ++ layered_window_updater_->OnAllocatedSharedMemory(viewport_pixel_size_, ++ std::move(region)); ++} ++ ++SkCanvas* SoftwareOutputDeviceProxy::BeginPaintDelegated() { ++ return canvas_.get(); ++} ++ ++void SoftwareOutputDeviceProxy::EndPaintDelegated( ++ const gfx::Rect& damage_rect) { ++ DCHECK(!waiting_on_draw_ack_); ++ ++ if (!canvas_) ++ return; ++ ++ layered_window_updater_->Draw(damage_rect, base::BindOnce( ++ &SoftwareOutputDeviceProxy::DrawAck, base::Unretained(this))); ++ waiting_on_draw_ack_ = true; ++ ++ TRACE_EVENT_ASYNC_BEGIN0("viz", "SoftwareOutputDeviceProxy::Draw", this); ++} ++ ++void SoftwareOutputDeviceProxy::DrawAck() { ++ DCHECK(waiting_on_draw_ack_); ++ DCHECK(!swap_ack_callback_.is_null()); ++ ++ TRACE_EVENT_ASYNC_END0("viz", "SoftwareOutputDeviceProxy::Draw", this); ++ ++ waiting_on_draw_ack_ = false; ++ std::move(swap_ack_callback_).Run(viewport_pixel_size_); ++} ++ ++} // namespace viz +diff --git a/components/viz/service/display_embedder/software_output_device_proxy.h b/components/viz/service/display_embedder/software_output_device_proxy.h +new file mode 100644 +index 0000000000000000000000000000000000000000..48fa86caaab3c15764f105eb7ad2aecf2b89bf36 +--- /dev/null ++++ b/components/viz/service/display_embedder/software_output_device_proxy.h +@@ -0,0 +1,90 @@ ++// Copyright 2014 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_VIZ_SERVICE_DISPLAY_EMBEDDER_SOFTWARE_OUTPUT_DEVICE_PROXY_H_ ++#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SOFTWARE_OUTPUT_DEVICE_PROXY_H_ ++ ++#if defined(OS_WIN) ++#include ++#endif ++ ++#include ++ ++#include "base/memory/shared_memory_mapping.h" ++#include "base/threading/thread_checker.h" ++#include "components/viz/host/host_display_client.h" ++#include "components/viz/service/display/software_output_device.h" ++#include "components/viz/service/viz_service_export.h" ++#include "services/viz/privileged/mojom/compositing/display_private.mojom.h" ++#include "services/viz/privileged/mojom/compositing/layered_window_updater.mojom.h" ++ ++namespace viz { ++ ++// Shared base class for SoftwareOutputDevice implementations. ++class SoftwareOutputDeviceBase : public SoftwareOutputDevice { ++ public: ++ SoftwareOutputDeviceBase() = default; ++ ~SoftwareOutputDeviceBase() override; ++ ++ // SoftwareOutputDevice implementation. ++ void Resize(const gfx::Size& viewport_pixel_size, ++ float scale_factor) override; ++ SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override; ++ void EndPaint() override; ++ ++ // Called from Resize() if |viewport_pixel_size_| has changed. ++ virtual void ResizeDelegated() = 0; ++ ++ // Called from BeginPaint() and should return an SkCanvas. ++ virtual SkCanvas* BeginPaintDelegated() = 0; ++ ++ // Called from EndPaint() if there is damage. ++ virtual void EndPaintDelegated(const gfx::Rect& damage_rect) = 0; ++ ++ private: ++ bool in_paint_ = false; ++ ++ THREAD_CHECKER(thread_checker_); ++ ++ DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceBase); ++}; ++ ++// SoftwareOutputDevice implementation that draws indirectly. An implementation ++// of mojom::LayeredWindowUpdater in the browser process handles the actual ++// drawing. Pixel backing is in SharedMemory so no copying between processes ++// is required. ++class SoftwareOutputDeviceProxy : public SoftwareOutputDeviceBase { ++ public: ++ explicit SoftwareOutputDeviceProxy( ++ mojom::LayeredWindowUpdaterPtr layered_window_updater); ++ ~SoftwareOutputDeviceProxy() override; ++ ++ // SoftwareOutputDevice implementation. ++ void OnSwapBuffers(SoftwareOutputDevice::SwapBuffersCallback swap_ack_callback) override; ++ ++ // SoftwareOutputDeviceBase implementation. ++ void ResizeDelegated() override; ++ SkCanvas* BeginPaintDelegated() override; ++ void EndPaintDelegated(const gfx::Rect& rect) override; ++ ++ private: ++ // Runs |swap_ack_callback_| after draw has happened. ++ void DrawAck(); ++ ++ mojom::LayeredWindowUpdaterPtr layered_window_updater_; ++ ++ std::unique_ptr canvas_; ++ bool waiting_on_draw_ack_ = false; ++ SoftwareOutputDevice::SwapBuffersCallback swap_ack_callback_; ++ ++#if !defined(WIN32) ++ base::WritableSharedMemoryMapping shm_mapping_; ++#endif ++ ++ DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceProxy); ++}; ++ ++} // namespace viz ++ ++#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SOFTWARE_OUTPUT_DEVICE_PROXY_H_ +diff --git a/components/viz/service/display_embedder/software_output_device_win.cc b/components/viz/service/display_embedder/software_output_device_win.cc +index bb07c338fac5251d62f52083b0cdf29f913c0426..e0ebb98d9a020dec059b9eae65d0e39c62b8dc57 100644 +--- a/components/viz/service/display_embedder/software_output_device_win.cc ++++ b/components/viz/service/display_embedder/software_output_device_win.cc +@@ -268,7 +268,7 @@ void SoftwareOutputDeviceWinProxy::EndPaintDelegated( + if (!canvas_) + return; + +- layered_window_updater_->Draw(base::BindOnce( ++ layered_window_updater_->Draw(damage_rect, base::BindOnce( + &SoftwareOutputDeviceWinProxy::DrawAck, base::Unretained(this))); + waiting_on_draw_ack_ = true; + +diff --git a/mojo/public/cpp/bindings/sync_call_restrictions.h b/mojo/public/cpp/bindings/sync_call_restrictions.h +index ac94eab98fd0f89de12db387c4718f2351ff4a92..b89d52a0c869c22147eb78f623a435920a6d34ec 100644 +--- a/mojo/public/cpp/bindings/sync_call_restrictions.h ++++ b/mojo/public/cpp/bindings/sync_call_restrictions.h +@@ -29,6 +29,7 @@ class HostContextFactoryPrivate; + + namespace viz { + class HostFrameSinkManager; ++class GpuDisplayProvider; + } + + namespace mojo { +@@ -82,6 +83,8 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) SyncCallRestrictions { + // For preventing frame swaps of wrong size during resize on Windows. + // (https://crbug.com/811945) + friend class ui::HostContextFactoryPrivate; ++ // For query of whether to use SoftwareOutputDevice or not ++ friend class viz::GpuDisplayProvider; + // END ALLOWED USAGE. + + #if ENABLE_SYNC_CALL_RESTRICTIONS +diff --git a/services/viz/privileged/mojom/compositing/display_private.mojom b/services/viz/privileged/mojom/compositing/display_private.mojom +index be56099d186fce2ea867d83bcae642dafc5cf400..0c6c0d20edf8dad26d9737ca1120e9a66ea27e92 100644 +--- a/services/viz/privileged/mojom/compositing/display_private.mojom ++++ b/services/viz/privileged/mojom/compositing/display_private.mojom +@@ -76,12 +76,14 @@ interface DisplayPrivate { + }; + + interface DisplayClient { ++ [Sync] ++ IsOffscreen() => (bool success); ++ + [EnableIf=is_mac] + OnDisplayReceivedCALayerParams(gfx.mojom.CALayerParams ca_layer_params); + + // Creates a LayeredWindowUpdater implementation to draw into a layered + // window. +- [EnableIf=is_win] + CreateLayeredWindowUpdater(pending_receiver receiver); + + // Notifies that a swap has occurred and provides information about the pixel +diff --git a/services/viz/privileged/mojom/compositing/layered_window_updater.mojom b/services/viz/privileged/mojom/compositing/layered_window_updater.mojom +index 6b7fbb6cf13dc8ee6ade0878a9a2c1efc5d4d3f1..e2af75168cb914a7b3b4a6c9b6a285498c3f8e72 100644 +--- a/services/viz/privileged/mojom/compositing/layered_window_updater.mojom ++++ b/services/viz/privileged/mojom/compositing/layered_window_updater.mojom +@@ -26,5 +26,5 @@ interface LayeredWindowUpdater { + // Draws to the HWND by copying pixels from shared memory. Callback must be + // called after draw operation is complete to signal shared memory can be + // modified. +- Draw() => (); ++ Draw(gfx.mojom.Rect damage_rect) => (); + }; +diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h +index 5410ee7a65abc9888d9061e37daf2928a909a272..3faeea7bc605dd5ee71b1ad463255b078d9d18a6 100644 +--- a/ui/compositor/compositor.h ++++ b/ui/compositor/compositor.h +@@ -66,6 +66,7 @@ class GpuMemoryBufferManager; + + namespace viz { + class ContextProvider; ++class HostDisplayClient; + class HostFrameSinkManager; + class LocalSurfaceIdAllocation; + class RasterContextProvider; +@@ -197,6 +198,15 @@ class COMPOSITOR_EXPORT ContextFactory { + virtual bool SyncTokensRequiredForDisplayCompositor() = 0; + }; + ++class COMPOSITOR_EXPORT CompositorDelegate { ++ public: ++ virtual std::unique_ptr CreateHostDisplayClient( ++ ui::Compositor* compositor) = 0; ++ ++ protected: ++ virtual ~CompositorDelegate() {} ++}; ++ + // Compositor object to take care of GPU painting. + // A Browser compositor object is responsible for generating the final + // displayable form of pixels comprising a single widget's contents. It draws an +@@ -236,6 +246,9 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient, + // Schedules a redraw of the layer tree associated with this compositor. + void ScheduleDraw(); + ++ CompositorDelegate* delegate() const { return delegate_; } ++ void SetDelegate(CompositorDelegate* delegate) { delegate_ = delegate; } ++ + // Sets the root of the layer tree drawn by this Compositor. The root layer + // must have no parent. The compositor's root layer is reset if the root layer + // is destroyed. NULL can be passed to reset the root layer, in which case the +@@ -459,6 +472,8 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient, + ui::ContextFactory* context_factory_; + ui::ContextFactoryPrivate* context_factory_private_; + ++ CompositorDelegate* delegate_ = nullptr; ++ + // The root of the Layer tree drawn by this compositor. + Layer* root_layer_ = nullptr; + +diff --git a/ui/compositor/host/host_context_factory_private.cc b/ui/compositor/host/host_context_factory_private.cc +index d8c278b99d09e663bba21da0e94b00ea77665349..6c2cd4e3e94df594b0aa2b91484dafea3a01fe65 100644 +--- a/ui/compositor/host/host_context_factory_private.cc ++++ b/ui/compositor/host/host_context_factory_private.cc +@@ -113,8 +113,13 @@ void HostContextFactoryPrivate::ConfigureCompositor( + compositor_data.display_private.reset(); + root_params->display_private = + compositor_data.display_private.BindNewEndpointAndPassReceiver(); +- compositor_data.display_client = +- std::make_unique(compositor); ++ if (compositor->delegate()) { ++ compositor_data.display_client = compositor->delegate()->CreateHostDisplayClient( ++ compositor); ++ } else { ++ compositor_data.display_client = ++ std::make_unique(compositor); ++ } + root_params->display_client = + compositor_data.display_client->GetBoundRemote(resize_task_runner_); + +diff --git a/ui/gfx/ca_layer_params.h b/ui/gfx/ca_layer_params.h +index 4014e64a75da88cf66c02e8adb71171c2666cab7..25e57784e1a1ffc546b003daa4cd0059c468432f 100644 +--- a/ui/gfx/ca_layer_params.h ++++ b/ui/gfx/ca_layer_params.h +@@ -6,6 +6,7 @@ + #define UI_GFX_CA_LAYER_PARAMS_H_ + + #include "build/build_config.h" ++#include "ui/gfx/geometry/rect.h" + #include "ui/gfx/geometry/size.h" + #include "ui/gfx/gfx_export.h" + +@@ -41,6 +42,8 @@ struct GFX_EXPORT CALayerParams { + gfx::ScopedRefCountedIOSurfaceMachPort io_surface_mach_port; + #endif + ++ gfx::Rect damage; ++ + // The geometry of the frame. + gfx::Size pixel_size; + float scale_factor = 1.f; +diff --git a/ui/gfx/mojom/ca_layer_params.mojom b/ui/gfx/mojom/ca_layer_params.mojom +index a73b2e678ffe0a682d0aa5409724fb441768bec5..6c36626d204c77ef51278b9e8f6fc6ee24a7a9ab 100644 +--- a/ui/gfx/mojom/ca_layer_params.mojom ++++ b/ui/gfx/mojom/ca_layer_params.mojom +@@ -18,5 +18,6 @@ struct CALayerParams { + bool is_empty; + CALayerContent content; + gfx.mojom.Size pixel_size; ++ gfx.mojom.Rect damage; + float scale_factor; + }; +diff --git a/ui/gfx/mojom/ca_layer_params_mojom_traits.cc b/ui/gfx/mojom/ca_layer_params_mojom_traits.cc +index 843d5c24ec33de07c12d4417eb111f91dbcd42fe..0ea594950fcd2686f1b07248dbc8ceb257d89dca 100644 +--- a/ui/gfx/mojom/ca_layer_params_mojom_traits.cc ++++ b/ui/gfx/mojom/ca_layer_params_mojom_traits.cc +@@ -52,6 +52,9 @@ bool StructTraits::Read( + if (!data.ReadPixelSize(&out->pixel_size)) + return false; + ++ if (!data.ReadDamage(&out->damage)) ++ return false; ++ + out->scale_factor = data.scale_factor(); + return true; + } +diff --git a/ui/gfx/mojom/ca_layer_params_mojom_traits.h b/ui/gfx/mojom/ca_layer_params_mojom_traits.h +index 4cac766eae3161baedac4202f694129cd90c80de..0821495ad22944d8856bb750cac8912a2f8328c3 100644 +--- a/ui/gfx/mojom/ca_layer_params_mojom_traits.h ++++ b/ui/gfx/mojom/ca_layer_params_mojom_traits.h +@@ -20,6 +20,10 @@ struct StructTraits { + return ca_layer_params.pixel_size; + } + ++ static gfx::Rect damage(const gfx::CALayerParams& ca_layer_params) { ++ return ca_layer_params.damage; ++ } ++ + static float scale_factor(const gfx::CALayerParams& ca_layer_params) { + return ca_layer_params.scale_factor; + } diff --git a/patches/chromium/fix_add_executable_to_chromedriver_s_rpath_for_electron_8.patch b/patches/chromium/fix_add_executable_to_chromedriver_s_rpath_for_electron_8.patch new file mode 100644 index 0000000000000..6331470797770 --- /dev/null +++ b/patches/chromium/fix_add_executable_to_chromedriver_s_rpath_for_electron_8.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Erick Zhao +Date: Wed, 29 Jan 2020 14:21:15 -0800 +Subject: fix: add executable to chromedriver's rpath for electron 8+ + +Chromedriver errors on startup for Electron 8+ because FFmpeg +is not included. Adding the executable path to rpath fixes the +issue. + +This is in patch form rather than copying the executable command +in Electron's BUILD.gn because one of Chromedriver's deps +(//net/server:http_server) hsa a visibility list that Electron +is not on. + +diff --git a/chrome/test/chromedriver/BUILD.gn b/chrome/test/chromedriver/BUILD.gn +index 2caa79d19ed0e6a16c39086ebb5e049eb50959be..f1db93f7da98f59c115acac2d22ea003d14a8b20 100644 +--- a/chrome/test/chromedriver/BUILD.gn ++++ b/chrome/test/chromedriver/BUILD.gn +@@ -331,6 +331,13 @@ executable("chromedriver") { + "//net/traffic_annotation:test_support", + "//services/network/public/mojom", + ] ++ # Fixes bad rpath on Electron 8+ ++ if (is_mac) { ++ ldflags = [ ++ "-rpath", ++ "@executable_path/.", ++ ] ++ } + } + + python_library("chromedriver_py_tests") { diff --git a/patches/chromium/fix_disable_usage_of_setapplicationisdaemon_and.patch b/patches/chromium/fix_disable_usage_of_setapplicationisdaemon_and.patch index acb1b6c16f9fe..5a9e52adbf95c 100644 --- a/patches/chromium/fix_disable_usage_of_setapplicationisdaemon_and.patch +++ b/patches/chromium/fix_disable_usage_of_setapplicationisdaemon_and.patch @@ -6,19 +6,6 @@ Subject: fix_disable_usage_of_setapplicationisdaemon_and.patch Disable usage of SetApplicationIsDaemon and _LSSetApplicationLaunchServicesServerConnectionStatus in MAS builds -diff --git a/content/utility/utility_service_factory.cc b/content/utility/utility_service_factory.cc -index 9b0759ab820ddb0e2a9e609b88bbc1673607c4e3..2392fd01736550f404304df324da9d81dfb3d446 100644 ---- a/content/utility/utility_service_factory.cc -+++ b/content/utility/utility_service_factory.cc -@@ -196,7 +196,7 @@ void UtilityServiceFactory::RunService( - std::unique_ptr - UtilityServiceFactory::CreateAudioService( - mojo::PendingReceiver receiver) { --#if defined(OS_MACOSX) -+#if defined(OS_MACOSX) && !defined(MAS_BUILD) - // Don't connect to launch services when running sandboxed - // (https://crbug.com/874785). - if (base::FeatureList::IsEnabled( diff --git a/sandbox/mac/system_services.cc b/sandbox/mac/system_services.cc index caa30bb378b30331f90057fe7ce3aec724104bf8..a766daa808495f7872051e129c6ad9f76f54e4fe 100644 --- a/sandbox/mac/system_services.cc diff --git a/patches/chromium/fix_route_mouse_event_navigations_through_the_web_contents_delegate.patch b/patches/chromium/fix_route_mouse_event_navigations_through_the_web_contents_delegate.patch new file mode 100644 index 0000000000000..3ae1d8f409948 --- /dev/null +++ b/patches/chromium/fix_route_mouse_event_navigations_through_the_web_contents_delegate.patch @@ -0,0 +1,34 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Fri, 14 Feb 2020 13:35:47 -0800 +Subject: fix: route mouse event navigations through the web_contents delegate + +This ensures that embedders can handle browser-side mouse navigations +themselves. We need this so that we can correctly ensure that processes +are not restarted for in-document navigations. + +Refs: https://chromium-review.googlesource.com/c/chromium/src/+/1769525 + +This patch can be removed once app.allowRendererProcessReuse is forced +to true as then Chromiums assumptions around processes become correct. + +diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc +index c2a579ed267f4081f44e9f2da2cfc90ae08bf750..36d84bd8a0a80b9801701c48e863762251a99026 100644 +--- a/content/browser/web_contents/web_contents_impl.cc ++++ b/content/browser/web_contents/web_contents_impl.cc +@@ -2336,11 +2336,13 @@ bool WebContentsImpl::HandleMouseEvent(const blink::WebMouseEvent& event) { + WebContentsImpl* outermost = GetOutermostWebContents(); + if (event.button == blink::WebPointerProperties::Button::kBack && + outermost->controller_.CanGoBack()) { +- outermost->controller_.GoBack(); ++ if (delegate_->OnGoToEntryOffset(-1)) ++ outermost->controller_.GoBack(); + return true; + } else if (event.button == blink::WebPointerProperties::Button::kForward && + outermost->controller_.CanGoForward()) { +- outermost->controller_.GoForward(); ++ if (delegate_->OnGoToEntryOffset(1)) ++ outermost->controller_.GoForward(); + return true; + } + } diff --git a/patches/chromium/fix_use_the_new_mediaplaypause_key_listener_for_internal_chrome.patch b/patches/chromium/fix_use_the_new_mediaplaypause_key_listener_for_internal_chrome.patch new file mode 100644 index 0000000000000..e88af03151bfd --- /dev/null +++ b/patches/chromium/fix_use_the_new_mediaplaypause_key_listener_for_internal_chrome.patch @@ -0,0 +1,65 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Wed, 29 Jan 2020 12:28:48 -0800 +Subject: fix: use the new MediaPlayPause key listener for internal chrome + logic + +The new kGlobalRequiresAccessibility Scope type should be upstreamed +and then we can use that and minimize this patch to just the change in +global_shortcut_listener_mac.mm + +diff --git a/chrome/browser/extensions/global_shortcut_listener_mac.mm b/chrome/browser/extensions/global_shortcut_listener_mac.mm +index befe726af9c10b1563a7fc0bb77cc55f65943d5c..bac51f33f35f96fe4ecc764cf5ca887176642f74 100644 +--- a/chrome/browser/extensions/global_shortcut_listener_mac.mm ++++ b/chrome/browser/extensions/global_shortcut_listener_mac.mm +@@ -39,7 +39,7 @@ GlobalShortcutListenerMac::GlobalShortcutListenerMac() + // global MediaKeysListener to receive media keys. + if (!content::MediaKeysListenerManager::IsMediaKeysListenerManagerEnabled()) { + media_keys_listener_ = ui::MediaKeysListener::Create( +- this, ui::MediaKeysListener::Scope::kGlobal); ++ this, ui::MediaKeysListener::Scope::kGlobalRequiresAccessibility); + DCHECK(media_keys_listener_); + } + } +diff --git a/ui/base/accelerators/media_keys_listener.h b/ui/base/accelerators/media_keys_listener.h +index 997718d8affaa193fcaabff4cd4c8b0c23df8891..e121598d46e7c62a3b14cb7960136f655cc69ab6 100644 +--- a/ui/base/accelerators/media_keys_listener.h ++++ b/ui/base/accelerators/media_keys_listener.h +@@ -20,8 +20,9 @@ class Accelerator; + class UI_BASE_EXPORT MediaKeysListener { + public: + enum class Scope { +- kGlobal, // Listener works whenever application in focus or not. +- kFocused, // Listener only works whan application has focus. ++ kGlobalRequiresAccessibility, // Listener works whenever application in focus or not but requires accessibility permissions on macOS ++ kGlobal, // Listener works whenever application in focus or not but requires media to be playnig. ++ kFocused, // Listener only works whan application has focus. + }; + + // Media keys accelerators receiver. +diff --git a/ui/base/accelerators/media_keys_listener_linux.cc b/ui/base/accelerators/media_keys_listener_linux.cc +index c74807dfae799851bb2e40996e634d8513e590a0..48f459941cae385e49af09410bb1812db5e6d971 100644 +--- a/ui/base/accelerators/media_keys_listener_linux.cc ++++ b/ui/base/accelerators/media_keys_listener_linux.cc +@@ -13,7 +13,7 @@ std::unique_ptr MediaKeysListener::Create( + MediaKeysListener::Scope scope) { + DCHECK(delegate); + +- if (scope == Scope::kGlobal) { ++ if (scope == Scope::kGlobal || scope == Scope::kGlobalRequiresAccessibility) { + if (!SystemMediaControlsMediaKeysListener::has_instance()) { + auto listener = + std::make_unique(delegate); +diff --git a/ui/base/accelerators/media_keys_listener_win.cc b/ui/base/accelerators/media_keys_listener_win.cc +index c50ea0ca2b8d612b96c0c822f5d36e9eb4ff861d..8b89fea7c09be007d8a020eb4d75f783c887f1a7 100644 +--- a/ui/base/accelerators/media_keys_listener_win.cc ++++ b/ui/base/accelerators/media_keys_listener_win.cc +@@ -14,7 +14,7 @@ std::unique_ptr MediaKeysListener::Create( + MediaKeysListener::Scope scope) { + DCHECK(delegate); + +- if (scope == Scope::kGlobal) { ++ if (scope == Scope::kGlobal || scope == Scope::kGlobalRequiresAccessibility) { + // We should never have more than one global media keys listener. + if (!SystemMediaControlsMediaKeysListener::has_instance() && + !GlobalMediaKeysListenerWin::has_instance()) { diff --git a/patches/chromium/frame_host_manager.patch b/patches/chromium/frame_host_manager.patch index c32dd9d83cd22..ba66510bf18e5 100644 --- a/patches/chromium/frame_host_manager.patch +++ b/patches/chromium/frame_host_manager.patch @@ -41,116 +41,109 @@ index 906a1ee4ac58b0744a32153bbaafeac4322a60e4..c90f4aead36cbf3767dc5094728963c2 // Adds the given SiteInstance to our map, to ensure that we do not create // another SiteInstance for the same site. void RegisterSiteInstance(SiteInstanceImpl* site_instance); +diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc +index f6bef176125384d58793fe0b8dddcbfc16550268..bba7dc934cd43df41949ed5b2adc70e152b77627 100644 +--- a/content/browser/frame_host/navigation_request.cc ++++ b/content/browser/frame_host/navigation_request.cc +@@ -1212,6 +1212,8 @@ void NavigationRequest::BeginNavigation() { + TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationRequest", this, + "ResponseStarted"); + ++ state_ = WILL_PROCESS_RESPONSE; ++ + // Select an appropriate RenderFrameHost. + render_frame_host_ = + frame_tree_node_->render_manager()->GetFrameHostForNavigation(this); diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc -index b1be27659d80a3199242d43ec80ccd28a43059b1..f475ec2301011a0840e76dd0e8e40bf65fdf6106 100644 +index 2b2c5538a26d95976ebc8a75b827eebf0e536f45..acdde7935fa617e82d5a53c2cc6bc99d74eb2b7b 100644 --- a/content/browser/frame_host/render_frame_host_manager.cc +++ b/content/browser/frame_host/render_frame_host_manager.cc -@@ -2250,6 +2250,21 @@ bool RenderFrameHostManager::InitRenderView( +@@ -2160,6 +2160,16 @@ bool RenderFrameHostManager::InitRenderView( scoped_refptr RenderFrameHostManager::GetSiteInstanceForNavigationRequest( NavigationRequest* request) { -+ BrowserContext* browser_context = nullptr; -+ scoped_refptr candidate_site_instance; -+ if (!GetContentClient()->browser()->CanUseCustomSiteInstance()) { -+ browser_context = -+ delegate_->GetControllerForRenderManager().GetBrowserContext(); -+ // If the navigation can swap SiteInstances, compute the SiteInstance it -+ // should use. -+ // TODO(clamy): We should also consider as a candidate SiteInstance the -+ // speculative SiteInstance that was computed on redirects. -+ candidate_site_instance = -+ speculative_render_frame_host_ -+ ? speculative_render_frame_host_->GetSiteInstance() -+ : nullptr; -+ } ++ // Compute the SiteInstance that the navigation should use, which will be ++ // either the current SiteInstance or a new one. ++ // ++ // TODO(clamy): We should also consider as a candidate SiteInstance the ++ // speculative SiteInstance that was computed on redirects. ++ SiteInstanceImpl* candidate_site_instance = ++ speculative_render_frame_host_ ++ ? speculative_render_frame_host_->GetSiteInstance() ++ : nullptr; + SiteInstance* current_site_instance = render_frame_host_->GetSiteInstance(); // All children of MHTML documents must be MHTML documents. They all live in -@@ -2293,6 +2308,59 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest( - request->common_params().url); - no_renderer_swap_allowed |= - request->from_begin_navigation() && !can_renderer_initiate_transfer; -+ -+ if (!GetContentClient()->browser()->CanUseCustomSiteInstance()) { -+ bool has_navigation_started = request->state() != NavigationRequest::NOT_STARTED; -+ bool has_response_started = -+ (request->state() == NavigationRequest::RESPONSE_STARTED || -+ request->state() == NavigationRequest::FAILED) && -+ !speculative_render_frame_host_; -+ // Gives user a chance to choose a custom site instance. -+ SiteInstance* affinity_site_instance = nullptr; -+ scoped_refptr overriden_site_instance; -+ bool should_register_site_instance = false; -+ ContentBrowserClient::SiteInstanceForNavigationType siteInstanceType = -+ GetContentClient()->browser()->ShouldOverrideSiteInstanceForNavigation( -+ current_frame_host(), speculative_frame_host(), browser_context, -+ request->common_params().url, has_navigation_started, -+ has_response_started, &affinity_site_instance); -+ switch (siteInstanceType) { -+ case ContentBrowserClient::SiteInstanceForNavigationType:: -+ FORCE_CANDIDATE_OR_NEW: -+ overriden_site_instance = -+ candidate_site_instance -+ ? candidate_site_instance -+ : current_site_instance->CreateRelatedSiteInstance( -+ request->common_params().url); -+ should_register_site_instance = true; -+ break; -+ case ContentBrowserClient::SiteInstanceForNavigationType::FORCE_NEW: -+ overriden_site_instance = current_site_instance->CreateRelatedSiteInstance( -+ request->common_params().url); -+ should_register_site_instance = true; -+ break; -+ case ContentBrowserClient::SiteInstanceForNavigationType::FORCE_CURRENT: -+ overriden_site_instance = render_frame_host_->GetSiteInstance(); -+ break; -+ case ContentBrowserClient::SiteInstanceForNavigationType::FORCE_AFFINITY: -+ DCHECK(affinity_site_instance); -+ overriden_site_instance = -+ scoped_refptr(affinity_site_instance); -+ break; -+ case ContentBrowserClient::SiteInstanceForNavigationType::ASK_CHROMIUM: -+ DCHECK(!affinity_site_instance); -+ break; -+ default: -+ break; -+ } -+ if (overriden_site_instance) { -+ if (should_register_site_instance) { -+ GetContentClient()->browser()->RegisterPendingSiteInstance( -+ render_frame_host_.get(), overriden_site_instance.get()); -+ } -+ return overriden_site_instance; -+ } -+ } - } else { - // Subframe navigations will use the current renderer, unless specifically - // allowed to swap processes. -@@ -2304,23 +2372,28 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest( - if (no_renderer_swap_allowed && !should_swap_for_error_isolation) - return scoped_refptr(current_site_instance); - -+ if (GetContentClient()->browser()->CanUseCustomSiteInstance()) { - // If the navigation can swap SiteInstances, compute the SiteInstance it - // should use. +@@ -2179,10 +2189,61 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest( + // // TODO(clamy): We should also consider as a candidate SiteInstance the // speculative SiteInstance that was computed on redirects. - SiteInstanceImpl* candidate_site_instance = -+ candidate_site_instance = - speculative_render_frame_host_ - ? speculative_render_frame_host_->GetSiteInstance() - : nullptr; +- speculative_render_frame_host_ +- ? speculative_render_frame_host_->GetSiteInstance() +- : nullptr; ++ if (!GetContentClient()->browser()->CanUseCustomSiteInstance() && ++ frame_tree_node_->IsMainFrame()) { ++ BrowserContext* browser_context = ++ delegate_->GetControllerForRenderManager().GetBrowserContext(); ++ bool has_navigation_started = request->state() != NavigationRequest::NOT_STARTED; ++ bool has_response_started = ++ (request->state() == NavigationRequest::WILL_PROCESS_RESPONSE || ++ request->state() == NavigationRequest::CANCELING) && ++ !speculative_render_frame_host_; ++ // Gives user a chance to choose a custom site instance. ++ SiteInstance* affinity_site_instance = nullptr; ++ scoped_refptr overriden_site_instance; ++ bool should_register_site_instance = false; ++ ContentBrowserClient::SiteInstanceForNavigationType siteInstanceType = ++ GetContentClient()->browser()->ShouldOverrideSiteInstanceForNavigation( ++ current_frame_host(), speculative_frame_host(), browser_context, ++ request->common_params().url, has_navigation_started, ++ has_response_started, &affinity_site_instance); ++ switch (siteInstanceType) { ++ case ContentBrowserClient::SiteInstanceForNavigationType:: ++ FORCE_CANDIDATE_OR_NEW: ++ overriden_site_instance = ++ candidate_site_instance ++ ? candidate_site_instance ++ : current_site_instance->CreateRelatedSiteInstance( ++ request->common_params().url); ++ should_register_site_instance = true; ++ break; ++ case ContentBrowserClient::SiteInstanceForNavigationType::FORCE_NEW: ++ overriden_site_instance = current_site_instance->CreateRelatedSiteInstance( ++ request->common_params().url); ++ should_register_site_instance = true; ++ break; ++ case ContentBrowserClient::SiteInstanceForNavigationType::FORCE_CURRENT: ++ overriden_site_instance = render_frame_host_->GetSiteInstance(); ++ break; ++ case ContentBrowserClient::SiteInstanceForNavigationType::FORCE_AFFINITY: ++ DCHECK(affinity_site_instance); ++ overriden_site_instance = ++ scoped_refptr(affinity_site_instance); ++ break; ++ case ContentBrowserClient::SiteInstanceForNavigationType::ASK_CHROMIUM: ++ DCHECK(!affinity_site_instance); ++ break; ++ default: ++ break; ++ } ++ if (overriden_site_instance) { ++ if (should_register_site_instance) { ++ GetContentClient()->browser()->RegisterPendingSiteInstance( ++ render_frame_host_.get(), overriden_site_instance.get()); ++ } ++ return overriden_site_instance; ++ } + } - scoped_refptr dest_site_instance = GetSiteInstanceForNavigation( - request->common_params().url, request->source_site_instance(), -- request->dest_site_instance(), candidate_site_instance, -+ request->dest_site_instance(), candidate_site_instance.get(), - request->common_params().transition, - request->state() == NavigationRequest::FAILED, + // Account for renderer-initiated reload as well. + // Needed as a workaround for https://crbug.com/1045524, remove it when it is +@@ -2202,6 +2263,9 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest( request->GetRestoreType() != RestoreType::NONE, request->is_view_source(), - was_server_redirect); + request->WasServerRedirect()); + GetContentClient()->browser()->RegisterPendingSiteInstance( + render_frame_host_.get(), dest_site_instance.get()); @@ -159,7 +152,7 @@ index b1be27659d80a3199242d43ec80ccd28a43059b1..f475ec2301011a0840e76dd0e8e40bf6 } diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc -index 2cee6bf52088a378a7c53343864d27aa78bdc106..4aca3c9724f8192a4f6e70e38473b408cadc6675 100644 +index 78dfe081fbe560f7c9bc2d39df571a56016f3a6b..7e3ec47028c60b87c52e9fced29ba510706a00fd 100644 --- a/content/browser/site_instance_impl.cc +++ b/content/browser/site_instance_impl.cc @@ -368,6 +368,10 @@ bool SiteInstanceImpl::HasRelatedSiteInstance(const GURL& url) { @@ -174,10 +167,10 @@ index 2cee6bf52088a378a7c53343864d27aa78bdc106..4aca3c9724f8192a4f6e70e38473b408 const GURL& url) { return browsing_instance_->GetSiteInstanceForURL( diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h -index 1edb9fd6b0c383f291735dd1a952fcb7b17cc87f..23967f040eb346be265faa2a92562e1fed190b8d 100644 +index 42042aba56c3cb89332c545ff3e84cca68517da6..c9fdfe196568824df3cce9762aaf0316c80d8255 100644 --- a/content/browser/site_instance_impl.h +++ b/content/browser/site_instance_impl.h -@@ -83,6 +83,7 @@ class CONTENT_EXPORT SiteInstanceImpl final : public SiteInstance, +@@ -84,6 +84,7 @@ class CONTENT_EXPORT SiteInstanceImpl final : public SiteInstance, BrowserContext* GetBrowserContext() override; const GURL& GetSiteURL() override; scoped_refptr GetRelatedSiteInstance(const GURL& url) override; @@ -186,10 +179,10 @@ index 1edb9fd6b0c383f291735dd1a952fcb7b17cc87f..23967f040eb346be265faa2a92562e1f size_t GetRelatedActiveContentsCount() override; bool RequiresDedicatedProcess() override; diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc -index da7eca5f443e2ecdcb3f0819848735aafeb43a00..ee14a76bf705c41082c78f7f0fcce968acf3a3b7 100644 +index 53bafb5b97034078784f8ebb43212b792f6a5d17..a4b15ed39cca38fe7b0c187c2e1139d5966438c1 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc -@@ -44,6 +44,21 @@ +@@ -49,6 +49,21 @@ namespace content { @@ -212,10 +205,10 @@ index da7eca5f443e2ecdcb3f0819848735aafeb43a00..ee14a76bf705c41082c78f7f0fcce968 const MainFunctionParams& parameters) { return nullptr; diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h -index 07fa16953d6167a5a381b28a5da3c08a78de4306..fe6864b5ba4c77a6694510db2f1add479241b156 100644 +index fa942dd135ecfc6fe735b4ae52410ec00b6098af..a4e37996522743471e819c5349c77da201dc283d 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h -@@ -205,8 +205,45 @@ struct WebPreferences; +@@ -215,8 +215,45 @@ struct WebPreferences; // the observer interfaces.) class CONTENT_EXPORT ContentBrowserClient { public: @@ -262,7 +255,7 @@ index 07fa16953d6167a5a381b28a5da3c08a78de4306..fe6864b5ba4c77a6694510db2f1add47 // implementations for the browser startup code. See comments in // browser_main_parts.h. diff --git a/content/public/browser/site_instance.h b/content/public/browser/site_instance.h -index a3e880e20e51d988175f0e8e2c42e7f5c1740104..faadd39d01530092f4f31a896ecb60f235e7f63f 100644 +index a718f53111bbaa0c248930a577f25b34e2d15494..5fafbed85e5549f0039da6414a40a653bbb72853 100644 --- a/content/public/browser/site_instance.h +++ b/content/public/browser/site_instance.h @@ -121,6 +121,11 @@ class CONTENT_EXPORT SiteInstance : public base::RefCounted { diff --git a/patches/chromium/gin_enable_disable_v8_platform.patch b/patches/chromium/gin_enable_disable_v8_platform.patch index 22329ebf6cc7e..0c10dd44734fe 100644 --- a/patches/chromium/gin_enable_disable_v8_platform.patch +++ b/patches/chromium/gin_enable_disable_v8_platform.patch @@ -5,10 +5,10 @@ Subject: gin_enable_disable_v8_platform.patch diff --git a/gin/isolate_holder.cc b/gin/isolate_holder.cc -index 5f8aea7105a188846fd48d199bb4e0ca2017a0ab..2514f591208372d6654ff9c5be50a51a39a881cb 100644 +index ee74e6ea92c64d0e95a4988557b236667f5b7e42..c3ae4c6221686479d05800c51d17d581c095e397 100644 --- a/gin/isolate_holder.cc +++ b/gin/isolate_holder.cc -@@ -98,9 +98,10 @@ IsolateHolder::~IsolateHolder() { +@@ -103,9 +103,10 @@ IsolateHolder::~IsolateHolder() { // static void IsolateHolder::Initialize(ScriptMode mode, v8::ArrayBuffer::Allocator* allocator, @@ -22,10 +22,10 @@ index 5f8aea7105a188846fd48d199bb4e0ca2017a0ab..2514f591208372d6654ff9c5be50a51a g_reference_table = reference_table; } diff --git a/gin/public/isolate_holder.h b/gin/public/isolate_holder.h -index 413e6c5bcc74cd01730c5d4dc66eb92aaf7df8de..6c5d101fef97e880bee20d2f76e4b339a624eb6f 100644 +index f23af2d9738f3aa76e3a49301e1c3216ee4a64b4..ede178acabc63c3c33d6ce93efd5632bec50ba89 100644 --- a/gin/public/isolate_holder.h +++ b/gin/public/isolate_holder.h -@@ -91,7 +91,8 @@ class GIN_EXPORT IsolateHolder { +@@ -88,7 +88,8 @@ class GIN_EXPORT IsolateHolder { // reference pointers. Otherwise, it can be nullptr. static void Initialize(ScriptMode mode, v8::ArrayBuffer::Allocator* allocator, @@ -36,10 +36,10 @@ index 413e6c5bcc74cd01730c5d4dc66eb92aaf7df8de..6c5d101fef97e880bee20d2f76e4b339 v8::Isolate* isolate() { return isolate_; } diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc -index aab46a000c5e9081440298ecef4844ead7a40b28..2cee8dee3f70148570613dc17827da19b193ff62 100644 +index 9298a9c75540d40133a8fc58c128aea521f3a37c..9474b26eba6d9c41162a197e11d6a871a2dd0259 100644 --- a/gin/v8_initializer.cc +++ b/gin/v8_initializer.cc -@@ -204,12 +204,14 @@ enum LoadV8FileResult { +@@ -190,12 +190,14 @@ enum LoadV8FileResult { } // namespace // static @@ -57,7 +57,7 @@ index aab46a000c5e9081440298ecef4844ead7a40b28..2cee8dee3f70148570613dc17827da19 if (!base::FeatureList::IsEnabled(features::kV8OptimizeJavascript)) { // We avoid explicitly passing --opt if kV8OptimizeJavascript is enabled diff --git a/gin/v8_initializer.h b/gin/v8_initializer.h -index aa3863abe0ea71fc26222564425f54ff80077719..fbf84c00174cc081d71aff8a16d51d2211614ec7 100644 +index 2918c6235688ee4eb7c43387296e6d24145c14aa..069c2c96d5067f0444dac3c6ce6d83f75e770129 100644 --- a/gin/v8_initializer.h +++ b/gin/v8_initializer.h @@ -20,7 +20,8 @@ namespace gin { diff --git a/patches/chromium/gritsettings_resource_ids.patch b/patches/chromium/gritsettings_resource_ids.patch index 1382606e1f29c..c7bfbf7587bbd 100644 --- a/patches/chromium/gritsettings_resource_ids.patch +++ b/patches/chromium/gritsettings_resource_ids.patch @@ -6,11 +6,11 @@ Subject: gritsettings_resource_ids.patch Add electron resources file to the list of resource ids generation. diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids -index be4d09028b1232594f90159fa11f67708a55afe8..9a3ae55c11a37ce00c19b63d882afd8c4548a6d9 100644 +index 12bbb5759e64c1defc412a2e285d5906afcd910d..9ea43ce7d01e5e1f59483fbb26b35749b7fa3d2c 100644 --- a/tools/gritsettings/resource_ids +++ b/tools/gritsettings/resource_ids -@@ -457,6 +457,11 @@ - "includes": [28880], +@@ -570,6 +570,11 @@ + "includes": [20060], }, + "electron/electron_resources.grd": { diff --git a/patches/chromium/ignore_rc_check.patch b/patches/chromium/ignore_rc_check.patch index ecc7e4b6bba63..31ac047513e52 100644 --- a/patches/chromium/ignore_rc_check.patch +++ b/patches/chromium/ignore_rc_check.patch @@ -6,20 +6,19 @@ Subject: ignore_rc_check.patch Dont compare RC.exe and RC.py output. FIXME: It has to be reverted once the script is fixed. -diff --git a/build/toolchain/win/tool_wrapper.py b/build/toolchain/win/tool_wrapper.py -index 3834cf2c01a9c63bfe7a6fc9e7549930a5eda01c..454b831a37c13fdd2fe4d8953d60dab661386f9a 100644 ---- a/build/toolchain/win/tool_wrapper.py -+++ b/build/toolchain/win/tool_wrapper.py -@@ -250,7 +250,11 @@ class WinTool(object): - if rc_exe_exit_code == 0: - import filecmp - # Strip "/fo" prefix. -- assert filecmp.cmp(rc_res_output[3:], rcpy_res_output[3:]) -+ # ------ -+ # Temporarily ignore compares -+ # Nightly builds use very large version numbers that fail this check -+ # FIXME(zacwalk): Enable the assert. -+ # assert filecmp.cmp(rc_res_output[3:], rcpy_res_output[3:]) - return rc_exe_exit_code +diff --git a/build/toolchain/win/rc/rc.py b/build/toolchain/win/rc/rc.py +index 5c0442bef19334004647bf6e5dcbabcf399f2ec6..05fb71a1a18d35d361cac8eddece95b665f74ed8 100755 +--- a/build/toolchain/win/rc/rc.py ++++ b/build/toolchain/win/rc/rc.py +@@ -234,7 +234,10 @@ def CompareToMsRcOutput(preprocessed_output, is_utf8, flags): + # Assert Microsoft rc.exe and rc.py produced identical .res files. + if rc_exe_exit_code == 0: + import filecmp +- assert filecmp.cmp(msrc_out, flags.output) ++ # Temporarily ignore compares ++ # Nightly builds use very large version numbers that fail this check ++ # FIXME(zacwalk): Enable the assert. ++ # assert filecmp.cmp(msrc_out, flags.output) + return rc_exe_exit_code + - def ExecActionWrapper(self, arch, rspfile, *dirname): diff --git a/patches/chromium/isolate_holder.patch b/patches/chromium/isolate_holder.patch index cafaa7c917547..c3eb1b20a3895 100644 --- a/patches/chromium/isolate_holder.patch +++ b/patches/chromium/isolate_holder.patch @@ -8,10 +8,10 @@ needs to register on an isolate so that it can be used later down in the initialization process of an isolate. diff --git a/gin/isolate_holder.cc b/gin/isolate_holder.cc -index 2514f591208372d6654ff9c5be50a51a39a881cb..4dd48f6a871cccb374c58adacb8ad9a80da89a5a 100644 +index c3ae4c6221686479d05800c51d17d581c095e397..e4c8a194418dc62f0e0222c08fe9ad39a4b9b9af 100644 --- a/gin/isolate_holder.cc +++ b/gin/isolate_holder.cc -@@ -52,7 +52,8 @@ IsolateHolder::IsolateHolder( +@@ -53,7 +53,8 @@ IsolateHolder::IsolateHolder( AccessMode access_mode, AllowAtomicsWaitMode atomics_wait_mode, IsolateType isolate_type, @@ -21,7 +21,7 @@ index 2514f591208372d6654ff9c5be50a51a39a881cb..4dd48f6a871cccb374c58adacb8ad9a8 : access_mode_(access_mode), isolate_type_(isolate_type) { DCHECK(task_runner); DCHECK(task_runner->BelongsToCurrentThread()); -@@ -60,7 +61,11 @@ IsolateHolder::IsolateHolder( +@@ -61,7 +62,11 @@ IsolateHolder::IsolateHolder( v8::ArrayBuffer::Allocator* allocator = g_array_buffer_allocator; CHECK(allocator) << "You need to invoke gin::IsolateHolder::Initialize first"; @@ -35,7 +35,7 @@ index 2514f591208372d6654ff9c5be50a51a39a881cb..4dd48f6a871cccb374c58adacb8ad9a8 new PerIsolateData(isolate_, allocator, access_mode_, task_runner)); if (isolate_creation_mode == IsolateCreationMode::kCreateSnapshot) { diff --git a/gin/public/isolate_holder.h b/gin/public/isolate_holder.h -index 6c5d101fef97e880bee20d2f76e4b339a624eb6f..576086de0de0a6fea9e2f2077af3aff18fd3138a 100644 +index ede178acabc63c3c33d6ce93efd5632bec50ba89..ffe7331cf1806417a32e66970f81b7797b9b80fc 100644 --- a/gin/public/isolate_holder.h +++ b/gin/public/isolate_holder.h @@ -75,7 +75,8 @@ class GIN_EXPORT IsolateHolder { diff --git a/patches/chromium/libgtkui_export.patch b/patches/chromium/libgtkui_export.patch index 3cbf97097f3be..40362078304d9 100644 --- a/patches/chromium/libgtkui_export.patch +++ b/patches/chromium/libgtkui_export.patch @@ -6,103 +6,46 @@ Subject: libgtkui_export.patch Export libgtkui symbols for the GN component build. diff --git a/chrome/browser/ui/libgtkui/gtk_util.h b/chrome/browser/ui/libgtkui/gtk_util.h -index e0ab34ca088e65b97ea6ce19c765e514acda760f..8c742d93ffabbd9795f9ba021b23c972e66eaec3 100644 +index ab8707407f80dda95f0eb9ff6ff7281c7ae76195..f4a5c41e12453a6a2d6ca1e9fd1727fddb6d5809 100644 --- a/chrome/browser/ui/libgtkui/gtk_util.h +++ b/chrome/browser/ui/libgtkui/gtk_util.h -@@ -11,6 +11,7 @@ +@@ -8,6 +8,7 @@ + #include + #include + ++#include "base/component_export.h" #include "ui/base/glib/scoped_gobject.h" #include "ui/native_theme/native_theme.h" #include "ui/views/window/frame_buttons.h" -+#include "chrome/browser/ui/libgtkui/libgtkui_export.h" - - typedef union _GdkEvent GdkEvent; - -@@ -36,10 +37,10 @@ namespace libgtkui { +@@ -34,11 +35,11 @@ namespace libgtkui { extern const color_utils::HSL kDefaultTintFrameIncognito; -void GtkInitFromCommandLine(const base::CommandLine& command_line); -+LIBGTKUI_EXPORT void GtkInitFromCommandLine(const base::CommandLine& command_line); - - // Returns the name of the ".desktop" file associated with our running process. --std::string GetDesktopName(base::Environment* env); -+LIBGTKUI_EXPORT std::string GetDesktopName(base::Environment* env); - - GdkModifierType GetGdkModifierForAccelerator( - const ui::Accelerator& accelerator); -@@ -52,7 +53,7 @@ void TurnButtonBlue(GtkWidget* button); ++COMPONENT_EXPORT(LIBGTKUI) void GtkInitFromCommandLine(const base::CommandLine& command_line); // Sets |dialog| as transient for |parent|, which will keep it on top and center // it above |parent|. Do nothing if |parent| is nullptr. -void SetGtkTransientForAura(GtkWidget* dialog, aura::Window* parent); -+LIBGTKUI_EXPORT void SetGtkTransientForAura(GtkWidget* dialog, aura::Window* parent); ++COMPONENT_EXPORT(LIBGTKUI) void SetGtkTransientForAura(GtkWidget* dialog, aura::Window* parent); // Gets the transient parent aura window for |dialog|. aura::Window* GetAuraTransientParent(GtkWidget* dialog); -@@ -158,7 +159,7 @@ void ApplyCssToContext(GtkStyleContext* context, const std::string& css); +@@ -143,7 +144,7 @@ void ApplyCssToContext(GtkStyleContext* context, const std::string& css); // Get the 'color' property from the style context created by // GetStyleContextFromCss(|css_selector|). -SkColor GetFgColor(const std::string& css_selector); -+LIBGTKUI_EXPORT SkColor GetFgColor(const std::string& css_selector); ++COMPONENT_EXPORT(LIBGTKUI) SkColor GetFgColor(const std::string& css_selector); ScopedCssProvider GetCssProvider(const std::string& css); -@@ -171,7 +172,7 @@ void RenderBackground(const gfx::Size& size, +@@ -156,7 +157,7 @@ void RenderBackground(const gfx::Size& size, // Renders a background from the style context created by // GetStyleContextFromCss(|css_selector|) into a 24x24 bitmap and // returns the average color. -SkColor GetBgColor(const std::string& css_selector); -+LIBGTKUI_EXPORT SkColor GetBgColor(const std::string& css_selector); ++COMPONENT_EXPORT(LIBGTKUI) SkColor GetBgColor(const std::string& css_selector); // Renders the border from the style context created by // GetStyleContextFromCss(|css_selector|) into a 24x24 bitmap and -diff --git a/chrome/browser/ui/libgtkui/skia_utils_gtk.h b/chrome/browser/ui/libgtkui/skia_utils_gtk.h -index bd6ed68a11b0b6c08962f688c799bd0f6b8a8d0e..b1184475e0ab9f381dc3c78b1092e2e0ec59d60b 100644 ---- a/chrome/browser/ui/libgtkui/skia_utils_gtk.h -+++ b/chrome/browser/ui/libgtkui/skia_utils_gtk.h -@@ -8,6 +8,7 @@ - #include - #include - -+#include "chrome/browser/ui/libgtkui/libgtkui_export.h" - #include "third_party/skia/include/core/SkColor.h" - - #if !GTK_CHECK_VERSION(3, 90, 0) -@@ -48,7 +49,7 @@ const SkBitmap GdkPixbufToImageSkia(GdkPixbuf* pixbuf); - // Convert and copy a SkBitmap to a GdkPixbuf. NOTE: this uses BGRAToRGBA, so - // it is an expensive operation. The returned GdkPixbuf will have a refcount of - // 1, and the caller is responsible for unrefing it when done. --GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap); -+LIBGTKUI_EXPORT GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap); - - } // namespace libgtkui - -diff --git a/chrome/browser/ui/libgtkui/unity_service.h b/chrome/browser/ui/libgtkui/unity_service.h -index 8d67e1460837c7f8adb151adea131cc6440659a3..95fbb27b6a81e4d73d239e94f5105078a936b5af 100644 ---- a/chrome/browser/ui/libgtkui/unity_service.h -+++ b/chrome/browser/ui/libgtkui/unity_service.h -@@ -5,18 +5,20 @@ - #ifndef CHROME_BROWSER_UI_LIBGTKUI_UNITY_SERVICE_H_ - #define CHROME_BROWSER_UI_LIBGTKUI_UNITY_SERVICE_H_ - -+#include "chrome/browser/ui/libgtkui/libgtkui_export.h" -+ - namespace unity { - - // Returns whether unity is currently running. --bool IsRunning(); -+LIBGTKUI_EXPORT bool IsRunning(); - - // If unity is running, sets the download counter in the dock icon. Any value - // other than 0 displays the badge. --void SetDownloadCount(int count); -+LIBGTKUI_EXPORT void SetDownloadCount(int count); - - // If unity is running, sets the download progress bar in the dock icon. Any - // value between 0.0 and 1.0 (exclusive) shows the progress bar. --void SetProgressFraction(float percentage); -+LIBGTKUI_EXPORT void SetProgressFraction(float percentage); - - } // namespace unity - diff --git a/patches/chromium/mas-cfisobjc.patch b/patches/chromium/mas-cfisobjc.patch index 388d72ed6f510..ba3280d793932 100644 --- a/patches/chromium/mas-cfisobjc.patch +++ b/patches/chromium/mas-cfisobjc.patch @@ -9,7 +9,7 @@ diff --git a/base/mac/foundation_util.mm b/base/mac/foundation_util.mm index 2a83d4d8158422c1056725679309c6ced1e531bd..f52934712f8de78193127c426d02982b36e9d422 100644 --- a/base/mac/foundation_util.mm +++ b/base/mac/foundation_util.mm -@@ -27,7 +27,6 @@ +@@ -27,7 +27,6 @@ CFTypeID SecKeyGetTypeID(); #if !defined(OS_IOS) CFTypeID SecACLGetTypeID(); CFTypeID SecTrustedApplicationGetTypeID(); @@ -17,7 +17,7 @@ index 2a83d4d8158422c1056725679309c6ced1e531bd..f52934712f8de78193127c426d02982b #endif } // extern "C" -@@ -316,8 +315,7 @@ void SetBaseBundleID(const char* new_base_bundle_id) { +@@ -316,8 +315,7 @@ NSFont* CFToNSCast(CTFontRef cf_val) { const_cast(reinterpret_cast(cf_val)); DCHECK(!cf_val || CTFontGetTypeID() == CFGetTypeID(cf_val) || @@ -27,7 +27,7 @@ index 2a83d4d8158422c1056725679309c6ced1e531bd..f52934712f8de78193127c426d02982b return ns_val; } -@@ -385,9 +383,6 @@ CTFontRef NSToCFCast(NSFont* ns_val) { +@@ -385,9 +383,6 @@ CFCast(const CFTypeRef& cf_val) { return (CTFontRef)(cf_val); } diff --git a/patches/chromium/mas-cgdisplayusesforcetogray.patch b/patches/chromium/mas-cgdisplayusesforcetogray.patch index 093f2711eff58..c98e87001df19 100644 --- a/patches/chromium/mas-cgdisplayusesforcetogray.patch +++ b/patches/chromium/mas-cgdisplayusesforcetogray.patch @@ -6,13 +6,13 @@ Subject: mas-cgdisplayusesforcetogray.patch Removes usage of the CGDisplayUsesForceToGray private API. diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm -index 85545ec7eaa6494fe542ee14e6f3e29ffa7d693e..bf352f9a6f087c28b6ce207bd0e5c09753ffb9cf 100644 +index 494843e9095c800608858ceecb6d294615bf59af..21397e1847270ac0678fe3c6d35d09843dc71de2 100644 --- a/ui/display/mac/screen_mac.mm +++ b/ui/display/mac/screen_mac.mm @@ -108,7 +108,17 @@ Display BuildDisplayForScreen(NSScreen* screen) { - display.set_color_depth(NSBitsPerPixelFromDepth([screen depth])); - display.set_depth_per_component(NSBitsPerSampleFromDepth([screen depth])); + display.set_color_depth(Display::kDefaultBitsPerPixel); + display.set_depth_per_component(Display::kDefaultBitsPerComponent); +#ifdef MAS_BUILD + // This is equivalent to the CGDisplayUsesForceToGray() API as at 2018-08-06, + // but avoids usage of the private API. diff --git a/patches/chromium/mas-lssetapplicationlaunchservicesserverconnectionstatus.patch b/patches/chromium/mas-lssetapplicationlaunchservicesserverconnectionstatus.patch deleted file mode 100644 index b36444bb22bbd..0000000000000 --- a/patches/chromium/mas-lssetapplicationlaunchservicesserverconnectionstatus.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jeremy Apthorp -Date: Thu, 20 Sep 2018 17:49:34 -0700 -Subject: mas-lssetapplicationlaunchservicesserverconnectionstatus.patch - -Removes usage of the _LSSetApplicationLaunchServicesServerConnectionStatus -private API. - -diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc -index d257830f5943937a634aa85539ad654310579e2c..4373f508964cb1e9fd51c952122ac2f41fb35ae9 100644 ---- a/content/gpu/gpu_main.cc -+++ b/content/gpu/gpu_main.cc -@@ -286,8 +286,10 @@ int GpuMain(const MainFunctionParams& parameters) { - std::make_unique( - base::MessagePumpType::NS_RUNLOOP); - -+#ifndef MAS_BUILD - // Tell LaunchServices to continue without a connection to the daemon. - _LSSetApplicationLaunchServicesServerConnectionStatus(0, nullptr); -+#endif - #else - main_thread_task_executor = - std::make_unique( diff --git a/patches/chromium/mas_blink_no_private_api.patch b/patches/chromium/mas_blink_no_private_api.patch index 7a37adc58d107..bbbc8584b58b8 100644 --- a/patches/chromium/mas_blink_no_private_api.patch +++ b/patches/chromium/mas_blink_no_private_api.patch @@ -18,7 +18,7 @@ index 94afefcee81b87c05bf9b1199d90d3d4b5ea84a6..2ec7f04c71824b47de1ddbf1f0e8625d extern "C" { // Kill ring calls. Would be better to use NSKillRing.h, but that's not -@@ -39,38 +40,53 @@ +@@ -39,38 +40,53 @@ NSString* _NSYankFromKillRing(); void _NSNewKillRingSequence(); void _NSSetKillRingToYankedState(); } @@ -73,7 +73,7 @@ index 94afefcee81b87c05bf9b1199d90d3d4b5ea84a6..2ec7f04c71824b47de1ddbf1f0e8625d } // namespace blink diff --git a/third_party/blink/renderer/core/paint/theme_painter_mac.mm b/third_party/blink/renderer/core/paint/theme_painter_mac.mm -index e94235acb17335fbc78c606ff26036871117bd09..7c4bd19215c67f649636ae69b9a21b5c1ba21ecc 100644 +index 8f4ae94bc1d8188d041654c50511f3346eee79de..fa06f47abbff3dcda937bf0b794f616e4818cd30 100644 --- a/third_party/blink/renderer/core/paint/theme_painter_mac.mm +++ b/third_party/blink/renderer/core/paint/theme_painter_mac.mm @@ -41,6 +41,7 @@ @@ -92,7 +92,7 @@ index e94235acb17335fbc78c606ff26036871117bd09..7c4bd19215c67f649636ae69b9a21b5c namespace blink { -@@ -72,10 +74,12 @@ void _NSDrawCarbonThemeListBox(NSRect frame, +@@ -95,10 +97,12 @@ bool ThemePainterMac::PaintTextField(const Node* node, // behavior change while remaining a fragile solution. // https://bugs.chromium.org/p/chromium/issues/detail?id=658085#c3 if (!use_ns_text_field_cell) { @@ -105,9 +105,9 @@ index e94235acb17335fbc78c606ff26036871117bd09..7c4bd19215c67f649636ae69b9a21b5c return false; } -@@ -161,10 +165,12 @@ void _NSDrawCarbonThemeListBox(NSRect frame, - const PaintInfo& paint_info, +@@ -186,10 +190,12 @@ bool ThemePainterMac::PaintTextArea(const Node* node, const IntRect& r) { + ScopedColorSchemeAppearance appearance(style.UsedColorScheme()); LocalCurrentGraphicsContext local_context(paint_info.context, r); +#ifndef MAS_BUILD _NSDrawCarbonThemeListBox( diff --git a/patches/chromium/mas_disable_custom_window_frame.patch b/patches/chromium/mas_disable_custom_window_frame.patch new file mode 100644 index 0000000000000..8572cea8efa65 --- /dev/null +++ b/patches/chromium/mas_disable_custom_window_frame.patch @@ -0,0 +1,146 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cheng Zhao +Date: Thu, 20 Sep 2018 17:48:49 -0700 +Subject: mas_disable_custom_window_frame.patch + +Disable private window frame APIs (NSNextStepFrame and NSThemeFrame) for MAS +build. + +diff --git a/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm +index 1ffb647e85e00ff60d84234e47f5d7d385f65245..439cc6df5e0cc1ec3732d6f2a2e00d543c73e837 100644 +--- a/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm ++++ b/components/remote_cocoa/app_shim/browser_native_widget_window_mac.mm +@@ -9,6 +9,7 @@ + #include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" + #include "components/remote_cocoa/common/native_widget_ns_window_host.mojom.h" + ++#ifndef MAS_BUILD + @interface NSWindow (PrivateBrowserNativeWidgetAPI) + + (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle; + @end +@@ -69,10 +70,13 @@ + + @end + ++#endif // MAS_BUILD ++ + @implementation BrowserNativeWidgetWindow + + // NSWindow (PrivateAPI) overrides. + ++#ifndef MAS_BUILD + + (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle { + // - NSThemeFrame and its subclasses will be nil if it's missing at runtime. + if ([BrowserWindowFrame class]) +@@ -87,6 +91,8 @@ + return NO; + } + ++#endif // MAS_BUILD ++ + // Handle "Move focus to the window toolbar" configured in System Preferences -> + // Keyboard -> Shortcuts -> Keyboard. Usually Ctrl+F5. The argument (|unknown|) + // tends to just be nil. +diff --git a/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.mm +index 8416c7c6e052dafb2aad61c0bd3224c36e945d23..cd356beda023ab2409b16d58ca38c70ba3674913 100644 +--- a/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.mm ++++ b/components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.mm +@@ -4,6 +4,8 @@ + + #import "components/remote_cocoa/app_shim/native_widget_mac_frameless_nswindow.h" + ++#ifndef MAS_BUILD ++ + @interface NSWindow (PrivateAPI) + + (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle; + @end +@@ -18,8 +20,12 @@ + } + @end + ++#endif // MAS_BUILD ++ + @implementation NativeWidgetMacFramelessNSWindow + ++#ifndef MAS_BUILD ++ + + (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle { + if ([NativeWidgetMacFramelessNSWindowFrame class]) { + return [NativeWidgetMacFramelessNSWindowFrame class]; +@@ -27,4 +33,6 @@ + return [super frameViewClassForStyleMask:windowStyle]; + } + ++#endif // MAS_BUILD ++ + @end +diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h +index e03bbc724cfd01967e71998394361556df1c4915..783745b11365c04c1e1052197d20d494bc901d45 100644 +--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h ++++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.h +@@ -17,6 +17,7 @@ class NativeWidgetNSWindowBridge; + + @protocol WindowTouchBarDelegate; + ++#ifndef MAS_BUILD + // Weak lets Chrome launch even if a future macOS doesn't have the below classes + WEAK_IMPORT_ATTRIBUTE + @interface NSNextStepFrame : NSView +@@ -33,6 +34,7 @@ REMOTE_COCOA_APP_SHIM_EXPORT + REMOTE_COCOA_APP_SHIM_EXPORT + @interface NativeWidgetMacNSWindowTitledFrame : NSThemeFrame + @end ++#endif + + // The NSWindow used by BridgedNativeWidget. Provides hooks into AppKit that + // can only be accomplished by overriding methods. +diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm +index 576ef14c1d67ce7f4a1b2095124d1d302440635c..dd068fc339b80c3e99d89e6d7cc73db85e6621d7 100644 +--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm ++++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm +@@ -15,7 +15,9 @@ + #import "ui/base/cocoa/window_size_constants.h" + + @interface NSWindow (Private) ++#ifndef MAS_BUILD + + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle; ++#endif + - (BOOL)hasKeyAppearance; + - (long long)_resizeDirectionForMouseLocation:(CGPoint)location; + - (BOOL)_isConsideredOpenForPersistentState; +@@ -57,6 +59,8 @@ + } + @end + ++#ifndef MAS_BUILD ++ + @implementation NativeWidgetMacNSWindowTitledFrame + - (void)mouseDown:(NSEvent*)event { + if (self.window.isMovable) +@@ -78,6 +82,8 @@ + } + @end + ++#endif // MAS_BUILD ++ + @implementation NativeWidgetMacNSWindow { + @private + base::scoped_nsobject commandDispatcher_; +@@ -159,6 +165,8 @@ + + // NSWindow overrides. + ++#ifndef MAS_BUILD ++ + + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle { + if (windowStyle & NSWindowStyleMaskTitled) { + if (Class customFrame = [NativeWidgetMacNSWindowTitledFrame class]) +@@ -170,6 +178,8 @@ + return [super frameViewClassForStyleMask:windowStyle]; + } + ++#endif ++ + - (BOOL)_isTitleHidden { + bool shouldShowWindowTitle = YES; + if (bridge_) diff --git a/patches/chromium/mas_disable_remote_accessibility.patch b/patches/chromium/mas_disable_remote_accessibility.patch new file mode 100644 index 0000000000000..d7f8a9caf2c3a --- /dev/null +++ b/patches/chromium/mas_disable_remote_accessibility.patch @@ -0,0 +1,283 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cheng Zhao +Date: Thu, 20 Sep 2018 17:48:49 -0700 +Subject: mas_disable_remote_accessibility.patch + +Disable remote accessibility APIs (NSAccessibilityRemoteUIElement) for MAS +build. + +diff --git a/components/remote_cocoa/app_shim/application_bridge.mm b/components/remote_cocoa/app_shim/application_bridge.mm +index 9ddda9116e7284cbccde8a51e23ad7560dd06367..e846091ad99b0154636489e53491209ff3cbfaaa 100644 +--- a/components/remote_cocoa/app_shim/application_bridge.mm ++++ b/components/remote_cocoa/app_shim/application_bridge.mm +@@ -49,6 +49,7 @@ class NativeWidgetBridgeOwner : public NativeWidgetNSWindowHostHelper { + + // NativeWidgetNSWindowHostHelper: + id GetNativeViewAccessible() override { ++#ifndef MAS_BUILD + if (!remote_accessibility_element_) { + int64_t browser_pid = 0; + std::vector element_token; +@@ -59,6 +60,9 @@ class NativeWidgetBridgeOwner : public NativeWidgetNSWindowHostHelper { + ui::RemoteAccessibility::GetRemoteElementFromToken(element_token); + } + return remote_accessibility_element_.get(); ++#else ++ return nil; ++#endif + } + void DispatchKeyEvent(ui::KeyEvent* event) override { + bool event_handled = false; +@@ -96,8 +100,10 @@ class NativeWidgetBridgeOwner : public NativeWidgetNSWindowHostHelper { + mojo::AssociatedRemote text_input_host_remote_; + + std::unique_ptr bridge_; ++#ifndef MAS_BUILD + base::scoped_nsobject + remote_accessibility_element_; ++#endif + }; + + } // namespace +diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm +index 53659a0483967a959acada632cd6b504563d95ad..1fbdc2980386583c33eb7a801794cbd8c522bd79 100644 +--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm ++++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm +@@ -556,10 +556,12 @@ void NativeWidgetNSWindowBridge::CreateContentView(uint64_t ns_view_id, + // this should be treated as an error and caught early. + CHECK(bridged_view_); + ++#ifndef MAS_BUILD + // Send the accessibility tokens for the NSView now that it exists. + host_->SetRemoteAccessibilityTokens( + ui::RemoteAccessibility::GetTokenForLocalElement(window_), + ui::RemoteAccessibility::GetTokenForLocalElement(bridged_view_)); ++#endif + + // Beware: This view was briefly removed (in favor of a bare CALayer) in + // crrev/c/1236675. The ordering of unassociated layers relative to NSView +diff --git a/content/app_shim_remote_cocoa/ns_view_bridge_factory_impl.mm b/content/app_shim_remote_cocoa/ns_view_bridge_factory_impl.mm +index a8e5c8888cb7ea8a85de7c8e8c613cc2b3c5be15..d01468fe77703b2812865b0198b84b0b645283c4 100644 +--- a/content/app_shim_remote_cocoa/ns_view_bridge_factory_impl.mm ++++ b/content/app_shim_remote_cocoa/ns_view_bridge_factory_impl.mm +@@ -66,8 +66,10 @@ class RenderWidgetHostNSViewBridgeOwner + return nil; + } + void SetAccessibilityWindow(NSWindow* window) override { ++#ifndef MAS_BUILD + host_->SetRemoteAccessibilityWindowToken( + ui::RemoteAccessibility::GetTokenForLocalElement(window)); ++#endif + } + + void ForwardKeyboardEvent(const content::NativeWebKeyboardEvent& key_event, +@@ -126,8 +128,10 @@ class RenderWidgetHostNSViewBridgeOwner + + mojo::AssociatedRemote host_; + std::unique_ptr bridge_; ++#ifndef MAS_BUILD + base::scoped_nsobject + remote_accessibility_element_; ++#endif + + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostNSViewBridgeOwner); + }; +diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h +index fb8589fe372dbd652d55b21a39260cc988531bea..d12dabad62116acda2d61caceb18f14a9d8dbe93 100644 +--- a/content/browser/renderer_host/render_widget_host_view_mac.h ++++ b/content/browser/renderer_host/render_widget_host_view_mac.h +@@ -46,7 +46,9 @@ class ScopedPasswordInputEnabler; + + @protocol RenderWidgetHostViewMacDelegate; + ++#ifndef MAS_BUILD + @class NSAccessibilityRemoteUIElement; ++#endif + @class RenderWidgetHostViewCocoa; + + namespace content { +@@ -628,10 +630,12 @@ class CONTENT_EXPORT RenderWidgetHostViewMac + // EnsureSurfaceSynchronizedForWebTest(). + uint32_t latest_capture_sequence_number_ = 0u; + ++#ifndef MAS_BUILD + // Remote accessibility objects corresponding to the NSWindow that this is + // displayed to the user in. + base::scoped_nsobject + remote_window_accessible_; ++#endif + + // Used to force the NSApplication's focused accessibility element to be the + // content::BrowserAccessibilityCocoa accessibility tree when the NSView for +diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm +index b6897c88c2c433e73f092f2ccebf29f097db35b0..5bec9c5d258c06fe338c8abe3e233e0b9b937234 100644 +--- a/content/browser/renderer_host/render_widget_host_view_mac.mm ++++ b/content/browser/renderer_host/render_widget_host_view_mac.mm +@@ -239,8 +239,10 @@ RenderWidgetHostViewMac::~RenderWidgetHostViewMac() { + void RenderWidgetHostViewMac::MigrateNSViewBridge( + remote_cocoa::mojom::Application* remote_cocoa_application, + uint64_t parent_ns_view_id) { ++#ifndef MAS_BUILD + // Destroy the previous remote accessibility element. + remote_window_accessible_.reset(); ++#endif + + // Disconnect from the previous bridge (this will have the effect of + // destroying the associated bridge), and close the receiver (to allow it +@@ -1373,8 +1375,10 @@ RenderWidgetHostViewMac::AccessibilityGetNativeViewAccessible() { + + gfx::NativeViewAccessible + RenderWidgetHostViewMac::AccessibilityGetNativeViewAccessibleForWindow() { ++#ifndef MAS_BUILD + if (remote_window_accessible_) + return remote_window_accessible_.get(); ++#endif + return [GetInProcessNSView() window]; + } + +@@ -1406,9 +1410,11 @@ id RenderWidgetHostViewMac::GetFocusedBrowserAccessibilityElement() { + } + + void RenderWidgetHostViewMac::SetAccessibilityWindow(NSWindow* window) { ++#ifndef MAS_BUILD + // When running in-process, just use the NSView's NSWindow as its own + // accessibility element. + remote_window_accessible_.reset(); ++#endif + } + + bool RenderWidgetHostViewMac::SyncIsWidgetForMainFrame( +@@ -1889,12 +1895,14 @@ void RenderWidgetHostViewMac::StopSpeaking() { + + void RenderWidgetHostViewMac::SetRemoteAccessibilityWindowToken( + const std::vector& window_token) { ++#ifndef MAS_BUILD + if (window_token.empty()) { + remote_window_accessible_.reset(); + } else { + remote_window_accessible_ = + ui::RemoteAccessibility::GetRemoteElementFromToken(window_token); + } ++#endif + } + + /////////////////////////////////////////////////////////////////////////////// +diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn +index 224ced89a4280f411788024a516e7576aa7dcd44..83ef5225953085f64ad65ddbb02a1d82765d175a 100644 +--- a/ui/base/BUILD.gn ++++ b/ui/base/BUILD.gn +@@ -306,6 +306,13 @@ jumbo_component("base") { + ] + } + ++ if (is_mas_build) { ++ sources -= [ ++ "cocoa/remote_accessibility_api.h", ++ "cocoa/remote_accessibility_api.mm", ++ ] ++ } ++ + if (is_posix) { + sources += [ "l10n/l10n_util_posix.cc" ] + } +diff --git a/ui/base/cocoa/remote_accessibility_api.h b/ui/base/cocoa/remote_accessibility_api.h +index 2a58aebabb23443a2c11364af4988c573f3909ba..3424b6011e80e9c995519b6a8d652abd843d926b 100644 +--- a/ui/base/cocoa/remote_accessibility_api.h ++++ b/ui/base/cocoa/remote_accessibility_api.h +@@ -11,6 +11,8 @@ + #include "base/mac/scoped_nsobject.h" + #include "ui/base/ui_base_export.h" + ++#ifndef MAS_BUILD ++ + @interface NSAccessibilityRemoteUIElement : NSObject + + (void)registerRemoteUIProcessIdentifier:(int)pid; + + (NSData*)remoteTokenForLocalUIElement:(id)element; +@@ -32,4 +34,6 @@ class UI_BASE_EXPORT RemoteAccessibility { + + } // namespace ui + ++#endif // MAS_BUILD ++ + #endif // UI_BASE_COCOA_REMOTE_ACCESSIBILITY_API_H_ +diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.h b/ui/views/cocoa/native_widget_mac_ns_window_host.h +index 7697f06aa481303c174ec303f595d0ed4d90276c..cf7ee1014a9e8e804aba2f0b5e192fb3b173a0d0 100644 +--- a/ui/views/cocoa/native_widget_mac_ns_window_host.h ++++ b/ui/views/cocoa/native_widget_mac_ns_window_host.h +@@ -29,7 +29,9 @@ + #include "ui/views/window/dialog_observer.h" + + @class NativeWidgetMacNSWindow; ++#ifndef MAS_BUILD + @class NSAccessibilityRemoteUIElement; ++#endif + @class NSView; + + namespace remote_cocoa { +@@ -425,11 +427,13 @@ class VIEWS_EXPORT NativeWidgetMacNSWindowHost + mojo::AssociatedRemote + remote_ns_window_remote_; + ++#ifndef MAS_BUILD + // Remote accessibility objects corresponding to the NSWindow and its root + // NSView. + base::scoped_nsobject + remote_window_accessible_; + base::scoped_nsobject remote_view_accessible_; ++#endif + + // Used to force the NSApplication's focused accessibility element to be the + // views::Views accessibility tree when the NSView for this is focused. +diff --git a/ui/views/cocoa/native_widget_mac_ns_window_host.mm b/ui/views/cocoa/native_widget_mac_ns_window_host.mm +index dd9e195a4fd0d506f15de93e9c3536efd4bae627..205910ac9a3f2bb12bddf38f50580c21618ef497 100644 +--- a/ui/views/cocoa/native_widget_mac_ns_window_host.mm ++++ b/ui/views/cocoa/native_widget_mac_ns_window_host.mm +@@ -296,14 +296,22 @@ gfx::NativeViewAccessible + NativeWidgetMacNSWindowHost::GetNativeViewAccessibleForNSView() const { + if (in_process_ns_window_bridge_) + return in_process_ns_window_bridge_->ns_view(); ++#ifndef MAS_BUILD + return remote_view_accessible_.get(); ++#else ++ return nullptr; ++#endif + } + + gfx::NativeViewAccessible + NativeWidgetMacNSWindowHost::GetNativeViewAccessibleForNSWindow() const { + if (in_process_ns_window_bridge_) + return in_process_ns_window_bridge_->ns_window(); ++#ifndef MAS_BUILD + return remote_window_accessible_.get(); ++#else ++ return nullptr; ++#endif + } + + remote_cocoa::mojom::NativeWidgetNSWindow* +@@ -1177,6 +1185,7 @@ void NativeWidgetMacNSWindowHost::OnFocusWindowToolbar() { + void NativeWidgetMacNSWindowHost::SetRemoteAccessibilityTokens( + const std::vector& window_token, + const std::vector& view_token) { ++#ifndef MAS_BUILD + remote_window_accessible_ = + ui::RemoteAccessibility::GetRemoteElementFromToken(window_token); + remote_view_accessible_ = +@@ -1184,14 +1193,17 @@ void NativeWidgetMacNSWindowHost::SetRemoteAccessibilityTokens( + [remote_view_accessible_ setWindowUIElement:remote_window_accessible_.get()]; + [remote_view_accessible_ + setTopLevelUIElement:remote_window_accessible_.get()]; ++#endif + } + + bool NativeWidgetMacNSWindowHost::GetRootViewAccessibilityToken( + int64_t* pid, + std::vector* token) { ++#ifndef MAS_BUILD + *pid = getpid(); + id element_id = GetNativeViewAccessible(); + *token = ui::RemoteAccessibility::GetTokenForLocalElement(element_id); ++#endif + return true; + } + diff --git a/patches/chromium/mas_disable_remote_layer.patch b/patches/chromium/mas_disable_remote_layer.patch new file mode 100644 index 0000000000000..15c7a7283f9e4 --- /dev/null +++ b/patches/chromium/mas_disable_remote_layer.patch @@ -0,0 +1,126 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cheng Zhao +Date: Thu, 20 Sep 2018 17:48:49 -0700 +Subject: mas_disable_remote_layer.patch + +Disable remote layer APIs (CAContext and CALayerHost) for MAS build. + +diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.h b/gpu/ipc/service/image_transport_surface_overlay_mac.h +index f65ad035e90cc6d9196416c7d974d6156eb7438d..9edb62e713e8d5da1f2a99a7b4795b46e42e4a41 100644 +--- a/gpu/ipc/service/image_transport_surface_overlay_mac.h ++++ b/gpu/ipc/service/image_transport_surface_overlay_mac.h +@@ -20,7 +20,9 @@ + #include "ui/gl/gl_surface_egl.h" + #endif + ++#ifndef MAS_BUILD + @class CAContext; ++#endif + @class CALayer; + + namespace ui { +@@ -97,7 +99,9 @@ class ImageTransportSurfaceOverlayMacBase : public BaseClass, + base::WeakPtr delegate_; + + bool use_remote_layer_api_; ++#ifndef MAS_BUILD + base::scoped_nsobject ca_context_; ++#endif + std::unique_ptr ca_layer_tree_coordinator_; + + gfx::Size pixel_size_; +diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.mm b/gpu/ipc/service/image_transport_surface_overlay_mac.mm +index c1af03a268dc409b043ce3a085a7365f07e0f9e7..b883883ad52b0d625ec7be6e3b0c86503ab33582 100644 +--- a/gpu/ipc/service/image_transport_surface_overlay_mac.mm ++++ b/gpu/ipc/service/image_transport_surface_overlay_mac.mm +@@ -63,6 +63,7 @@ ImageTransportSurfaceOverlayMacBase< + template + bool ImageTransportSurfaceOverlayMacBase::Initialize( + gl::GLSurfaceFormat format) { ++#ifndef MAS_BUILD + // Create the CAContext to send this to the GPU process, and the layer for + // the context. + if (use_remote_layer_api_) { +@@ -71,6 +72,7 @@ bool ImageTransportSurfaceOverlayMacBase::Initialize( + [CAContext contextWithCGSConnection:connection_id options:@{}] retain]); + [ca_context_ setLayer:ca_layer_tree_coordinator_->GetCALayerForDisplay()]; + } ++#endif + return true; + } + +@@ -139,7 +141,9 @@ ImageTransportSurfaceOverlayMacBase::SwapBuffersInternal( + "GLImpl", static_cast(gl::GetGLImplementation()), + "width", pixel_size_.width()); + if (use_remote_layer_api_) { ++#ifndef MAS_BUILD + params.ca_layer_params.ca_context_id = [ca_context_ contextId]; ++#endif + } else { + IOSurfaceRef io_surface = + ca_layer_tree_coordinator_->GetIOSurfaceForDisplay(); +diff --git a/ui/accelerated_widget_mac/display_ca_layer_tree.mm b/ui/accelerated_widget_mac/display_ca_layer_tree.mm +index 60abe639bd9c9cf6885f811c741a45eeb683ec58..c38eed5fbdefc96a3d60e4ab70d3b007264ccb3c 100644 +--- a/ui/accelerated_widget_mac/display_ca_layer_tree.mm ++++ b/ui/accelerated_widget_mac/display_ca_layer_tree.mm +@@ -98,6 +98,7 @@ void DisplayCALayerTree::UpdateCALayerTree( + } + + void DisplayCALayerTree::GotCALayerFrame(uint32_t ca_context_id) { ++#ifndef MAS_BUILD + // Early-out if the remote layer has not changed. + if ([remote_layer_ contextId] == ca_context_id) + return; +@@ -122,6 +123,9 @@ void DisplayCALayerTree::GotCALayerFrame(uint32_t ca_context_id) { + [io_surface_layer_ removeFromSuperlayer]; + io_surface_layer_.reset(); + } ++#else ++ NOTREACHED() << "Remote layer is being used in MAS build"; ++#endif + } + + void DisplayCALayerTree::GotIOSurfaceFrame( +diff --git a/ui/base/cocoa/remote_layer_api.h b/ui/base/cocoa/remote_layer_api.h +index 2057fe69d1bb4a2eb0b1dabc5473a30d676847fe..2aba330fc488660ef874caae26a06e6847cdaf93 100644 +--- a/ui/base/cocoa/remote_layer_api.h ++++ b/ui/base/cocoa/remote_layer_api.h +@@ -13,6 +13,7 @@ + + #include "ui/base/ui_base_export.h" + ++#ifndef MAS_BUILD + // The CGSConnectionID is used to create the CAContext in the process that is + // going to share the CALayers that it is rendering to another process to + // display. +@@ -50,6 +51,8 @@ typedef uint32_t CAContextID; + + #endif // __OBJC__ + ++#endif // MAS_BUILD ++ + namespace ui { + + // This function will check if all of the interfaces listed above are supported +diff --git a/ui/base/cocoa/remote_layer_api.mm b/ui/base/cocoa/remote_layer_api.mm +index bbaf9f466f4999acb5bfccf3b9565fd8f556ca2f..8c846ce9523a4b2f6fbdbdbeae4f94b45ac3c115 100644 +--- a/ui/base/cocoa/remote_layer_api.mm ++++ b/ui/base/cocoa/remote_layer_api.mm +@@ -12,6 +12,7 @@ + namespace ui { + + bool RemoteLayerAPISupported() { ++#ifndef MAS_BUILD + static bool disabled_at_command_line = + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableRemoteCoreAnimation); +@@ -46,6 +47,9 @@ bool RemoteLayerAPISupported() { + + // If everything is there, we should be able to use the API. + return true; ++#else ++ return false; ++#endif // MAS_BUILD + } + + } // namespace diff --git a/patches/chromium/mas_no_private_api.patch b/patches/chromium/mas_no_private_api.patch index 2d17ddaa3dfe2..2ce8b2565385e 100644 --- a/patches/chromium/mas_no_private_api.patch +++ b/patches/chromium/mas_no_private_api.patch @@ -50,10 +50,26 @@ index 743d1364bcd13e24ecbe5ced730161d15b8c3e93..a5529ac8841b79f230f0fa8eae2b3cb2 // is concerned. @property(nonatomic, readonly) NSString* subrole; diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm -index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be868742069dafc72 100644 +index e82b89ff5e92c4ff1e62fd8592915ae331a1adc5..917660269257bb51716e902d5ad70578793a4bb1 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/content/browser/accessibility/browser_accessibility_cocoa.mm -@@ -229,6 +229,7 @@ +@@ -207,6 +207,7 @@ NSString* const + NSString* const NSAccessibilityLengthForTextMarkerRangeParameterizedAttribute = + @"AXLengthForTextMarkerRange"; + ++#ifndef MAS_BUILD + // Private attributes that can be used for testing text markers, e.g. in dump + // tree tests. + NSString* const +@@ -218,6 +219,7 @@ NSString* const + NSString* const + NSAccessibilityTextMarkerNodeDebugDescriptionParameterizedAttribute = + @"AXTextMarkerNodeDebugDescription"; ++#endif + + // Other private attributes. + NSString* const NSAccessibilitySelectTextWithCriteriaParameterizedAttribute = +@@ -241,6 +243,7 @@ NSDictionary* attributeToMethodNameMap = nil; // VoiceOver uses -1 to mean "no limit" for AXResultsLimit. const int kAXResultsLimitNoLimit = -1; @@ -61,7 +77,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 extern "C" { // The following are private accessibility APIs required for cursor navigation -@@ -468,6 +469,7 @@ void AddMisspelledTextAttributes(const AXPlatformRange& ax_range, +@@ -480,6 +483,7 @@ NSAttributedString* GetAttributedTextForTextMarkerRange(id marker_range) { AddMisspelledTextAttributes(ax_range, attributed_text); return attributed_text; } @@ -69,7 +85,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 // Returns an autoreleased copy of the AXNodeData's attribute. NSString* NSStringForStringAttribute(BrowserAccessibility* browserAccessibility, -@@ -735,7 +737,9 @@ + (void)initialize { +@@ -748,7 +752,9 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; {NSAccessibilityEditableAncestorAttribute, @"editableAncestor"}, {NSAccessibilityElementBusyAttribute, @"elementBusy"}, {NSAccessibilityEnabledAttribute, @"enabled"}, @@ -79,7 +95,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 {NSAccessibilityExpandedAttribute, @"expanded"}, {NSAccessibilityFocusableAncestorAttribute, @"focusableAncestor"}, {NSAccessibilityFocusedAttribute, @"focused"}, -@@ -747,8 +751,10 @@ + (void)initialize { +@@ -760,8 +766,10 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; {NSAccessibilityHighestEditableAncestorAttribute, @"highestEditableAncestor"}, {NSAccessibilityIndexAttribute, @"index"}, @@ -90,7 +106,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 {NSAccessibilityInvalidAttribute, @"invalid"}, {NSAccessibilityIsMultiSelectableAttribute, @"isMultiSelectable"}, {NSAccessibilityLanguageAttribute, @"language"}, -@@ -770,13 +776,17 @@ + (void)initialize { +@@ -783,13 +791,17 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; {NSAccessibilityRowsAttribute, @"rows"}, // TODO(aboxhall): expose // NSAccessibilityServesAsTitleForUIElementsAttribute @@ -108,7 +124,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 {NSAccessibilitySizeAttribute, @"size"}, {NSAccessibilitySortDirectionAttribute, @"sortDirection"}, {NSAccessibilitySubroleAttribute, @"subrole"}, -@@ -1272,6 +1282,7 @@ - (NSNumber*)enabled { +@@ -1285,6 +1297,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; ax::mojom::Restriction::kDisabled]; } @@ -116,7 +132,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 // Returns a text marker that points to the last character in the document that // can be selected with VoiceOver. - (id)endTextMarker { -@@ -1282,6 +1293,7 @@ - (id)endTextMarker { +@@ -1295,6 +1308,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; BrowserAccessibilityPositionInstance position = root->CreatePositionAt(0); return CreateTextMarker(position->CreatePositionAtEndOfAnchor()); } @@ -124,7 +140,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 - (NSNumber*)expanded { if (![self instanceActive]) -@@ -1427,6 +1439,8 @@ - (NSNumber*)index { +@@ -1444,6 +1458,8 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; return nil; } @@ -133,7 +149,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 - (NSNumber*)insertionPointLineNumber { if (![self instanceActive]) return nil; -@@ -1449,6 +1463,7 @@ - (NSNumber*)insertionPointLineNumber { +@@ -1466,6 +1482,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; caretPosition->AsTextPosition()->text_offset()); return @(std::distance(lineBreaks.begin(), iterator)); } @@ -141,7 +157,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 // Returns whether or not this node should be ignored in the // accessibility tree. -@@ -2134,6 +2149,7 @@ - (NSArray*)selectedChildren { +@@ -2145,6 +2162,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; return ret; } @@ -149,7 +165,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 - (NSString*)selectedText { if (![self instanceActive]) return nil; -@@ -2145,11 +2161,13 @@ - (NSString*)selectedText { +@@ -2156,11 +2174,13 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; return nil; return base::SysUTF16ToNSString(range.GetText()); } @@ -163,7 +179,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 - (NSValue*)selectedTextRange { if (![self instanceActive]) return nil; -@@ -2170,12 +2188,15 @@ - (NSValue*)selectedTextRange { +@@ -2181,12 +2201,15 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; int selLength = range.GetText().length(); return [NSValue valueWithRange:NSMakeRange(selStart, selLength)]; } @@ -179,7 +195,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 - (NSValue*)size { if (![self instanceActive]) -@@ -2208,6 +2229,7 @@ - (NSString*)sortDirection { +@@ -2219,6 +2242,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; return nil; } @@ -187,7 +203,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 // Returns a text marker that points to the first character in the document that // can be selected with VoiceOver. - (id)startTextMarker { -@@ -2218,6 +2240,7 @@ - (id)startTextMarker { +@@ -2229,6 +2253,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; BrowserAccessibilityPositionInstance position = root->CreatePositionAt(0); return CreateTextMarker(position->CreatePositionAtStartOfAnchor()); } @@ -195,7 +211,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 // Returns a subrole based upon the role. - (NSString*)subrole { -@@ -2509,11 +2532,13 @@ - (NSAttributedString*)attributedValueForRange:(NSRange)range { +@@ -2520,11 +2545,13 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; NSMutableAttributedString* attributedValue = [[[NSMutableAttributedString alloc] initWithString:value] autorelease]; @@ -209,7 +225,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 return [attributedValue attributedSubstringFromRange:range]; } -@@ -2616,9 +2641,8 @@ - (id)accessibilityAttributeValue:(NSString*)attribute +@@ -2627,9 +2654,8 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; return ToBrowserAccessibilityCocoa(cell); } @@ -221,7 +237,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 BrowserAccessibilityPositionInstance position = CreatePositionFromTextMarker(parameter); if (!position->IsNullPosition()) -@@ -2929,6 +2953,7 @@ AXPlatformRange range(std::move(lineStartPosition), +@@ -2940,6 +2966,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; return CreateTextMarker(root->CreatePositionAt(index)); } @@ -229,7 +245,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 if ([attribute isEqualToString: NSAccessibilityBoundsForRangeParameterizedAttribute]) { -@@ -2962,6 +2987,7 @@ AXPlatformRange range(std::move(lineStartPosition), +@@ -2973,6 +3000,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; return nil; } @@ -237,7 +253,7 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 if ([attribute isEqualToString: NSAccessibilityLineTextMarkerRangeForTextMarkerParameterizedAttribute]) { -@@ -3042,6 +3068,7 @@ AXPlatformRange range(position->CreatePreviousLineStartPosition( +@@ -3087,6 +3115,7 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; return @(child->GetIndexInParent()); } @@ -246,10 +262,10 @@ index 5bdcea914780468d93b55b2e89dc3628a683f85c..0358f557432590ed48f9cd9be8687420 return nil; } diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm -index 851da832bd30e05974317601712f309b2d542ab6..dd6d4c50468ec5f3364213e43b9336f13b153a19 100644 +index 05e9b67b3e4d356ce4c90b2da577f169f8aeae0a..2833b50e78add4caa1d5927b697d91e728ee0a06 100644 --- a/content/browser/accessibility/browser_accessibility_manager_mac.mm +++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm -@@ -493,6 +493,7 @@ void PostAnnouncementNotification(NSString* announcement) { +@@ -532,6 +532,7 @@ NSDictionary* BrowserAccessibilityManagerMac:: [user_info setObject:native_focus_object forKey:NSAccessibilityTextChangeElement]; @@ -257,7 +273,7 @@ index 851da832bd30e05974317601712f309b2d542ab6..dd6d4c50468ec5f3364213e43b9336f1 id selected_text = [native_focus_object selectedTextMarkerRange]; if (selected_text) { NSString* const NSAccessibilitySelectedTextMarkerRangeAttribute = -@@ -500,6 +501,7 @@ void PostAnnouncementNotification(NSString* announcement) { +@@ -539,6 +540,7 @@ NSDictionary* BrowserAccessibilityManagerMac:: [user_info setObject:selected_text forKey:NSAccessibilitySelectedTextMarkerRangeAttribute]; } @@ -281,7 +297,7 @@ index e59ac93d0e1554a2df5d8c74db2beba25d090228..6657c48664bdec4964b382f80309d1bf namespace content { -@@ -22,6 +24,7 @@ +@@ -22,6 +24,7 @@ namespace { // verifies there are no existing open connections), and then indicates that // Chrome should continue execution without access to launchservicesd. void DisableSystemServices() { @@ -298,7 +314,7 @@ index e59ac93d0e1554a2df5d8c74db2beba25d090228..6657c48664bdec4964b382f80309d1bf } // namespace diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm -index 8e4a469b6f0675dc7b82543d5758f0c2ec226809..2fcfb6edb5f57bd25756257b77361d25768f9abf 100644 +index b2425416b1993f037530619df0458d43d008bd03..39b096120b57c3192519a607d96c8a3ddab434e5 100644 --- a/device/bluetooth/bluetooth_adapter_mac.mm +++ b/device/bluetooth/bluetooth_adapter_mac.mm @@ -36,6 +36,7 @@ @@ -309,7 +325,7 @@ index 8e4a469b6f0675dc7b82543d5758f0c2ec226809..2fcfb6edb5f57bd25756257b77361d25 extern "C" { // Undocumented IOBluetooth Preference API [1]. Used by `blueutil` [2] and // `Karabiner` [3] to programmatically control the Bluetooth state. Calling the -@@ -49,6 +50,7 @@ +@@ -49,6 +50,7 @@ extern "C" { // [4] https://support.apple.com/kb/PH25091 void IOBluetoothPreferenceSetControllerPowerState(int state); } @@ -317,7 +333,7 @@ index 8e4a469b6f0675dc7b82543d5758f0c2ec226809..2fcfb6edb5f57bd25756257b77361d25 namespace { -@@ -121,8 +123,10 @@ CBCentralManagerState GetCBManagerState(CBCentralManager* manager) { +@@ -121,8 +123,10 @@ BluetoothAdapterMac::BluetoothAdapterMac() controller_state_function_( base::BindRepeating(&BluetoothAdapterMac::GetHostControllerState, base::Unretained(this))), @@ -328,7 +344,7 @@ index 8e4a469b6f0675dc7b82543d5758f0c2ec226809..2fcfb6edb5f57bd25756257b77361d25 should_update_name_(true), classic_discovery_manager_( BluetoothDiscoveryManagerMac::CreateClassic(this)), -@@ -303,8 +307,12 @@ CBCentralManagerState GetCBManagerState(CBCentralManager* manager) { +@@ -307,8 +311,12 @@ base::WeakPtr BluetoothAdapterMac::GetWeakPtr() { } bool BluetoothAdapterMac::SetPoweredImpl(bool powered) { @@ -342,10 +358,10 @@ index 8e4a469b6f0675dc7b82543d5758f0c2ec226809..2fcfb6edb5f57bd25756257b77361d25 void BluetoothAdapterMac::RemovePairingDelegateInternal( diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn -index de4f3ceb0fb01bb924c19a576fc201a14a6470cd..10ca4b247e170cb9d17a1a3c6ecac2efbffb6a5d 100644 +index d8e556e18263864930c223235fe4ae475df7747b..ce01f4cd7c4debeb88eff7c593f835c9d78ed002 100644 --- a/media/audio/BUILD.gn +++ b/media/audio/BUILD.gn -@@ -191,6 +191,12 @@ source_set("audio") { +@@ -177,6 +177,12 @@ source_set("audio") { "mac/scoped_audio_unit.cc", "mac/scoped_audio_unit.h", ] 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 dae9dac513471..fcee1ef58bd19 100644 --- a/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch +++ b/patches/chromium/network_service_allow_remote_certificate_verification_logic.patch @@ -5,10 +5,10 @@ Subject: network_service_allow_remote_certificate_verification_logic.patch diff --git a/services/network/network_context.cc b/services/network/network_context.cc -index 647fd0d633e868c5f06bbb97f03333e863075f06..b9a8a12b8abb386fec548a26e4686af2e3822b8c 100644 +index 83d6b8a9e1d99f78437b690fe321974a7d9ac0a1..a3b4372d74c2cc792f3f26d03200a41ca8aafa73 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc -@@ -90,6 +90,11 @@ +@@ -92,6 +92,11 @@ #include "services/network/url_loader.h" #include "services/network/url_request_context_builder_mojo.h" @@ -20,7 +20,7 @@ index 647fd0d633e868c5f06bbb97f03333e863075f06..b9a8a12b8abb386fec548a26e4686af2 #if BUILDFLAG(IS_CT_SUPPORTED) #include "components/certificate_transparency/chrome_ct_policy_enforcer.h" #include "components/certificate_transparency/chrome_require_ct_delegate.h" -@@ -319,6 +324,79 @@ std::string HashesToBase64String(const net::HashValueVector& hashes) { +@@ -326,6 +331,79 @@ std::string HashesToBase64String(const net::HashValueVector& hashes) { } // namespace @@ -100,7 +100,7 @@ index 647fd0d633e868c5f06bbb97f03333e863075f06..b9a8a12b8abb386fec548a26e4686af2 constexpr uint32_t NetworkContext::kMaxOutstandingRequestsPerProcess; NetworkContext::PendingCertVerify::PendingCertVerify() = default; -@@ -474,6 +552,13 @@ void NetworkContext::SetClient( +@@ -501,6 +579,13 @@ void NetworkContext::SetClient( client_.Bind(std::move(client)); } @@ -112,21 +112,40 @@ index 647fd0d633e868c5f06bbb97f03333e863075f06..b9a8a12b8abb386fec548a26e4686af2 +} + void NetworkContext::CreateURLLoaderFactory( - mojom::URLLoaderFactoryRequest request, + mojo::PendingReceiver receiver, mojom::URLLoaderFactoryParamsPtr params) { -@@ -1552,12 +1637,19 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext() { - net::SystemTrustStoreProvider::CreateDefaultForSSL()))); +@@ -1629,6 +1714,7 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext() { + base::CommandLine::ForCurrentProcess(); + + std::unique_ptr cert_verifier; ++ std::unique_ptr temp_verifier; + if (g_cert_verifier_for_testing) { + cert_verifier = std::make_unique(); + } else { +@@ -1676,8 +1762,8 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext() { + } + #endif + #if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) +- if (!cert_verifier) { +- cert_verifier = std::make_unique( ++ if (!temp_verifier) { ++ temp_verifier = std::make_unique( + std::make_unique( + std::make_unique( + params_->use_builtin_cert_verifier +@@ -1687,12 +1773,19 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext() { + cert_net_fetcher_)))); } #endif - if (!cert_verifier) - cert_verifier = net::CertVerifier::CreateDefault(cert_net_fetcher_); -+ if (!cert_verifier) { -+ auto mt_verifier = std::make_unique( -+ net::CertVerifyProc::CreateDefault(std::move(cert_net_fetcher_))); -+ auto remote_cert_verifier = std::make_unique(std::move(mt_verifier)); -+ remote_cert_verifier_ = remote_cert_verifier.get(); -+ cert_verifier = std::make_unique(std::move(remote_cert_verifier)); ++ if (!temp_verifier) { ++ temp_verifier = std::make_unique( ++ net::CertVerifyProc::CreateSystemVerifyProc(std::move(cert_net_fetcher_))); + } ++ auto remote_cert_verifier = std::make_unique(std::move(temp_verifier)); ++ remote_cert_verifier_ = remote_cert_verifier.get(); ++ cert_verifier = std::make_unique(std::move(remote_cert_verifier)); } - builder.SetCertVerifier(IgnoreErrorsCertVerifier::MaybeWrapCertVerifier( @@ -139,10 +158,10 @@ index 647fd0d633e868c5f06bbb97f03333e863075f06..b9a8a12b8abb386fec548a26e4686af2 std::unique_ptr network_delegate = std::make_unique( diff --git a/services/network/network_context.h b/services/network/network_context.h -index 98c968120b4ccedcdf321a365db038954095ae7a..e98063a71435ee8828f3b5a21b569325965f7c58 100644 +index 419191ac79fe3fd9bf481aa05d75f8e23dfa5393..ffa991ec6020841a33df7870326b52f0ae943846 100644 --- a/services/network/network_context.h +++ b/services/network/network_context.h -@@ -81,6 +81,7 @@ class DomainReliabilityMonitor; +@@ -84,6 +84,7 @@ class DomainReliabilityMonitor; namespace network { class CertVerifierWithTrustAnchors; @@ -150,16 +169,16 @@ index 98c968120b4ccedcdf321a365db038954095ae7a..e98063a71435ee8828f3b5a21b569325 class CookieManager; class ExpectCTReporter; class HostResolver; -@@ -170,6 +171,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext - // mojom::NetworkContext implementation: - void SetClient( - mojo::PendingRemote client) override; +@@ -180,6 +181,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext + void CreateURLLoaderFactory( + mojo::PendingReceiver receiver, + mojom::URLLoaderFactoryParamsPtr params) override; + void SetCertVerifierClient( + mojo::PendingRemote client) override; - void CreateURLLoaderFactory(mojom::URLLoaderFactoryRequest request, - mojom::URLLoaderFactoryParamsPtr params) override; void ResetURLLoaderFactories() override; -@@ -564,6 +567,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext + void GetCookieManager( + mojo::PendingReceiver receiver) override; +@@ -607,6 +610,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext std::unique_ptr nss_temp_certs_cache_; #endif @@ -167,13 +186,13 @@ index 98c968120b4ccedcdf321a365db038954095ae7a..e98063a71435ee8828f3b5a21b569325 + // CertNetFetcher used by the context's CertVerifier. May be nullptr if // CertNetFetcher is not used by the current platform. - scoped_refptr cert_net_fetcher_; + scoped_refptr cert_net_fetcher_; diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom -index 322670fb32403266f25c9f49079a25ef90dc01e2..4691fde53fd7d6ff6e89c55a7fec88d77b39701c 100644 +index 662496d972418f367bf608ba43233526d86363fc..1ba183b6e6caf064fd003d1cf5e8af4ebc4ba869 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom -@@ -168,6 +168,17 @@ interface TrustedURLLoaderHeaderClient { - pending_receiver header_client); +@@ -190,6 +190,17 @@ struct HttpAuthStaticNetworkContextParams { + = DefaultCredentials.ALLOW_DEFAULT_CREDENTIALS; }; +interface CertVerifierClient { @@ -190,7 +209,7 @@ index 322670fb32403266f25c9f49079a25ef90dc01e2..4691fde53fd7d6ff6e89c55a7fec88d7 // Parameters for constructing a network context. struct NetworkContextParams { // Name used by memory tools to identify the context. -@@ -740,6 +751,9 @@ interface NetworkContext { +@@ -799,6 +810,9 @@ interface NetworkContext { // Sets a client for this network context. SetClient(pending_remote client); @@ -198,5 +217,5 @@ index 322670fb32403266f25c9f49079a25ef90dc01e2..4691fde53fd7d6ff6e89c55a7fec88d7 + SetCertVerifierClient(pending_remote? client); + // Creates a new URLLoaderFactory with the given |params|. - CreateURLLoaderFactory(URLLoaderFactory& url_loader_factory, + CreateURLLoaderFactory(pending_receiver url_loader_factory, URLLoaderFactoryParams params); diff --git a/patches/chromium/never_let_a_non-zero-size_pixel_snap_to_zero_size.patch b/patches/chromium/never_let_a_non-zero-size_pixel_snap_to_zero_size.patch new file mode 100644 index 0000000000000..6943b0d69a362 --- /dev/null +++ b/patches/chromium/never_let_a_non-zero-size_pixel_snap_to_zero_size.patch @@ -0,0 +1,144 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Wed, 29 Apr 2020 13:36:00 -0700 +Subject: Never let a non-zero-size pixel snap to zero size + +The logic for LayoutUnit::SnapSizeToPixel maps the size to +the closest pixel align edge given a location. When a size of +width less than 1 happens to straddle a pixel aligned edge this +forces the size to zero. + +Force the size to always be non-zero if the input size is non-zero, +and change PhysicalRect to use the LayoutRect snapping to get +correct cull rects. + +Bug: 793785 +Change-Id: Ia4c30d10c389fb4677006441aac9ee380a7c2f41 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1948057 +Commit-Queue: Stephen Chenney +Reviewed-by: Xianzhu Wang +Cr-Commit-Position: refs/heads/master@{#726516} + +diff --git a/third_party/blink/renderer/platform/geometry/layout_unit.h b/third_party/blink/renderer/platform/geometry/layout_unit.h +index eaaff017795237c83b9eb06e8cb70f8598ec5664..8ffe3e11501c005edb233130b231c33e6e79000b 100644 +--- a/third_party/blink/renderer/platform/geometry/layout_unit.h ++++ b/third_party/blink/renderer/platform/geometry/layout_unit.h +@@ -723,7 +723,12 @@ inline float& operator/=(float& a, const LayoutUnit& b) { + + inline int SnapSizeToPixel(LayoutUnit size, LayoutUnit location) { + LayoutUnit fraction = location.Fraction(); +- return (fraction + size).Round() - fraction.Round(); ++ int result = (fraction + size).Round() - fraction.Round(); ++ if (UNLIKELY(result == 0 && ++ std::abs(size.ToFloat()) > LayoutUnit::Epsilon() * 4)) { ++ return size > 0 ? 1 : -1; ++ } ++ return result; + } + + inline int RoundToInt(LayoutUnit value) { +diff --git a/third_party/blink/renderer/platform/geometry/layout_unit_test.cc b/third_party/blink/renderer/platform/geometry/layout_unit_test.cc +index db1fa1f610f195eb7933ab044c26c3b2bae84120..05d4a2f762ede01f712470363ed118f7b37925a4 100644 +--- a/third_party/blink/renderer/platform/geometry/layout_unit_test.cc ++++ b/third_party/blink/renderer/platform/geometry/layout_unit_test.cc +@@ -155,8 +155,20 @@ TEST(LayoutUnitTest, LayoutUnitSnapSizeToPixel) { + EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.99))); + EXPECT_EQ(2, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1))); + +- EXPECT_EQ(0, SnapSizeToPixel(LayoutUnit(0.5), LayoutUnit(1.5))); +- EXPECT_EQ(0, SnapSizeToPixel(LayoutUnit(0.99), LayoutUnit(1.5))); ++ // 0.046875 is 3/64, lower than 4 * LayoutUnit::Epsilon() ++ EXPECT_EQ(0, SnapSizeToPixel(LayoutUnit(0.046875), LayoutUnit(0))); ++ // 0.078125 is 5/64, higher than 4 * LayoutUnit::Epsilon() ++ EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(0.078125), LayoutUnit(0))); ++ ++ // Negative versions ++ EXPECT_EQ(0, SnapSizeToPixel(LayoutUnit(-0.046875), LayoutUnit(0))); ++ EXPECT_EQ(-1, SnapSizeToPixel(LayoutUnit(-0.078125), LayoutUnit(0))); ++ ++ // The next 2 would snap to zero but for the requirement that we not snap ++ // sizes greater than 4 * LayoutUnit::Epsilon() to 0. ++ EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(0.5), LayoutUnit(1.5))); ++ EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(0.99), LayoutUnit(1.5))); ++ + EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.0), LayoutUnit(1.5))); + EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.49), LayoutUnit(1.5))); + EXPECT_EQ(1, SnapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1.5))); +diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/thin-element-render-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/thin-element-render-ref.html +new file mode 100644 +index 0000000000000000000000000000000000000000..0d5851d5544f9692d0761dc92c23b6b2b546b4d3 +--- /dev/null ++++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/thin-element-render-ref.html +@@ -0,0 +1,31 @@ ++ ++Reference: Thin elements should paint even at small size ++ ++ ++ ++ ++ ++ ++
++
++
++ ++ +\ No newline at end of file +diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/thin-element-render.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/thin-element-render.html +new file mode 100644 +index 0000000000000000000000000000000000000000..fa587360a6d2625c8f02cd7f0eba54b3bb09a1f1 +--- /dev/null ++++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/thin-element-render.html +@@ -0,0 +1,33 @@ ++ ++Thin elements should still paint even at small size. ++ ++ ++ ++ ++ ++ ++ ++ ++
++
++
++ ++ +\ No newline at end of file diff --git a/patches/chromium/no_cache_storage_check.patch b/patches/chromium/no_cache_storage_check.patch index c490084a853e4..b1cd24c4eebbf 100644 --- a/patches/chromium/no_cache_storage_check.patch +++ b/patches/chromium/no_cache_storage_check.patch @@ -7,7 +7,7 @@ Do not check for unique origin in CacheStorage, in Electron we may have scripts running without an origin. diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage.cc b/content/browser/cache_storage/legacy/legacy_cache_storage.cc -index b1a4d62c9230fd11e74d4fa959b0d89f30a27e83..225c31ec025d73eb581dee9a05cb17cd70ee4be8 100644 +index a855f74d2b966ec8d92fd8f3134070b80ea3bd0c..43703c742904d5b594b0ee827c94a0f3564e7d46 100644 --- a/content/browser/cache_storage/legacy/legacy_cache_storage.cc +++ b/content/browser/cache_storage/legacy/legacy_cache_storage.cc @@ -104,7 +104,7 @@ class LegacyCacheStorage::CacheLoader { diff --git a/patches/chromium/notification_provenance.patch b/patches/chromium/notification_provenance.patch index 6e0865ab2dcb0..f1711f8a3a402 100644 --- a/patches/chromium/notification_provenance.patch +++ b/patches/chromium/notification_provenance.patch @@ -55,10 +55,10 @@ index 5253f6be778cc78571b3df0a33d364a9b1e6ef52..dc5307e6500b0bfb5da83e8d8ff8886b scoped_refptr service_worker_context_; diff --git a/content/browser/notifications/blink_notification_service_impl_unittest.cc b/content/browser/notifications/blink_notification_service_impl_unittest.cc -index ce997528ce2973b54dbbf965220cc30bcb19a094..e84b0b62d853b044e590a0c764f1b21ad3210886 100644 +index 951dc88b44e76b6736aa0ff30d548022486f0f6a..ea7f72b7aa05c31972e4abbdc6529ab044117b0b 100644 --- a/content/browser/notifications/blink_notification_service_impl_unittest.cc +++ b/content/browser/notifications/blink_notification_service_impl_unittest.cc -@@ -138,7 +138,7 @@ class BlinkNotificationServiceImplTest : public ::testing::Test { +@@ -139,7 +139,7 @@ class BlinkNotificationServiceImplTest : public ::testing::Test { notification_service_ = std::make_unique( notification_context_.get(), &browser_context_, @@ -68,10 +68,10 @@ index ce997528ce2973b54dbbf965220cc30bcb19a094..e84b0b62d853b044e590a0c764f1b21a notification_service_remote_.BindNewPipeAndPassReceiver()); diff --git a/content/browser/notifications/platform_notification_context_impl.cc b/content/browser/notifications/platform_notification_context_impl.cc -index 2a447b73d763b7c91359f1b4550a002ff9e5e715..bd434393cf35605a8cc05d6fb96215cf76b5417a 100644 +index d68765bcd17e93cdbad875311375153133ee45d9..cefaac6dded65e57dced955ae9bfa2ff65bc4e66 100644 --- a/content/browser/notifications/platform_notification_context_impl.cc +++ b/content/browser/notifications/platform_notification_context_impl.cc -@@ -207,12 +207,13 @@ void PlatformNotificationContextImpl::Shutdown() { +@@ -208,12 +208,13 @@ void PlatformNotificationContextImpl::Shutdown() { } void PlatformNotificationContextImpl::CreateService( @@ -107,19 +107,19 @@ index 4bf25bf1fa69f7d3869369172d375e2e489e62a1..f80ef2cecc8b111dc54e109646573a59 const url::Origin& origin, mojo::PendingReceiver receiver); -diff --git a/content/browser/renderer_interface_binders.cc b/content/browser/renderer_interface_binders.cc -index 8030fd5e66fab694d3f3a54dcb4688c14dcba473..18f259bdd9b5340a50494311983646d9b9244469 100644 ---- a/content/browser/renderer_interface_binders.cc -+++ b/content/browser/renderer_interface_binders.cc -@@ -135,7 +135,7 @@ void RendererInterfaceBinders::InitializeParameterizedBinderRegistry() { - RenderProcessHost* host, const url::Origin& origin) { - static_cast(host->GetStoragePartition()) - ->GetPlatformNotificationContext() -- ->CreateService(origin, std::move(request)); -+ ->CreateService(host, origin, std::move(request)); - })); - parameterized_binder_registry_.AddInterface( - base::BindRepeating(&QuotaDispatcherHost::CreateForWorker)); +diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc +index f16ee02a53137b75f7540e06040317a68332c721..845e95c571391e04700415aa63439fb785212f0f 100644 +--- a/content/browser/renderer_host/render_process_host_impl.cc ++++ b/content/browser/renderer_host/render_process_host_impl.cc +@@ -2010,7 +2010,7 @@ void RenderProcessHostImpl::CreateNotificationService( + mojo::PendingReceiver receiver) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + storage_partition_impl_->GetPlatformNotificationContext()->CreateService( +- origin, std::move(receiver)); ++ this, origin, std::move(receiver)); + } + + void RenderProcessHostImpl::CreateWebSocketConnector( diff --git a/content/public/browser/platform_notification_service.h b/content/public/browser/platform_notification_service.h index ca61088079c4150fcf389504ddcf26bcf6bf69cd..d9c034c39890eef1fe3d95c6d7c0ae68eb711a89 100644 --- a/content/public/browser/platform_notification_service.h diff --git a/patches/chromium/os_metrics_mac.patch b/patches/chromium/os_metrics_mac.patch new file mode 100644 index 0000000000000..39f523697202b --- /dev/null +++ b/patches/chromium/os_metrics_mac.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cheng Zhao +Date: Thu, 4 Oct 2018 14:57:02 -0700 +Subject: fix: make os_metrics_mac work with 10.15 SDK + +This patch fixes the memory_instrumentation module returning 0. + +Backport https://chromium-review.googlesource.com/c/chromium/src/+/1967436. + +diff --git a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_mac.cc b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_mac.cc +index a20418240d52d65a0b905df7ff8754c50d38c0fc..487336bbd48440edde753d03fc8c79b2a4dbc299 100644 +--- a/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_mac.cc ++++ b/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_mac.cc +@@ -50,8 +50,34 @@ struct ChromeTaskVMInfo { + #else + using ChromeTaskVMInfo = task_vm_info; + #endif // MAC_OS_X_VERSION_10_11 +-mach_msg_type_number_t ChromeTaskVMInfoCount = +- sizeof(ChromeTaskVMInfo) / sizeof(natural_t); ++ ++// Don't simply use sizeof(task_vm_info) / sizeof(natural_t): ++// In the 10.15 SDK, this structure is 87 32-bit words long, and in ++// mach_types.defs: ++// ++// type task_info_t = array[*:87] of integer_t; ++// ++// However in the 10.14 SDK, this structure is 42 32-bit words, and in ++// mach_types.defs: ++// ++// type task_info_t = array[*:52] of integer_t; ++// ++// As a result, the 10.15 SDK's task_vm_info won't fit inside the 10.14 SDK's ++// task_info_t, so the *rest of the system* (on 10.14 and earlier) can't handle ++// calls that request the full 10.15 structure. We have to request a prefix of ++// it that 10.14 and earlier can handle by limiting the length we request. The ++// rest of the fields just get ignored, but we don't use them anyway. ++ ++constexpr mach_msg_type_number_t ChromeTaskVMInfoCount = ++ TASK_VM_INFO_REV2_COUNT; ++ ++// The count field is in units of natural_t, which is the machine's word size ++// (64 bits on all modern machines), but the task_info_t array is in units of ++// integer_t, which is 32 bits. ++constexpr mach_msg_type_number_t MAX_MIG_SIZE_FOR_1014 = ++ 52 / (sizeof(natural_t) / sizeof(integer_t)); ++static_assert(ChromeTaskVMInfoCount <= MAX_MIG_SIZE_FOR_1014, ++ "task_vm_info must be small enough for 10.14 MIG interfaces"); + + using VMRegion = mojom::VmRegion; + diff --git a/patches/chromium/out_of_process_instance.patch b/patches/chromium/out_of_process_instance.patch index 372a92ee455fb..a394815cae487 100644 --- a/patches/chromium/out_of_process_instance.patch +++ b/patches/chromium/out_of_process_instance.patch @@ -5,10 +5,10 @@ Subject: out_of_process_instance.patch diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc -index a3786a8397536bdcb247c6a0a0ea6db4b174eae8..ab7f2276f4254d1d4cd608fa9815345185737665 100644 +index 9c3c5396a6fa22f46913afec75c845e75d893e58..157742ccd39ee324067b8bdb46cc3ea88d5a8232 100644 --- a/pdf/out_of_process_instance.cc +++ b/pdf/out_of_process_instance.cc -@@ -479,7 +479,9 @@ bool OutOfProcessInstance::Init(uint32_t argc, +@@ -486,7 +486,9 @@ bool OutOfProcessInstance::Init(uint32_t argc, std::string document_url = document_url_var.AsString(); base::StringPiece document_url_piece(document_url); is_print_preview_ = IsPrintPreviewUrl(document_url_piece); diff --git a/patches/chromium/pepper_flash.patch b/patches/chromium/pepper_flash.patch index 11b181781e628..78f102aec3bc3 100644 --- a/patches/chromium/pepper_flash.patch +++ b/patches/chromium/pepper_flash.patch @@ -28,7 +28,7 @@ index 735da93c3cabb8c6139971295740ba14a30d1b69..533f53fbc42397608e3762e370cc935c // Non-owning pointer to the filter must outlive this class. explicit ChromeBrowserPepperHostFactory(content::BrowserPpapiHost* host); diff --git a/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc b/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc -index 37245c48cacbc5db164c61e4499f34ee83e0fce7..0fef12892e965e319e45f5dd1a9c5c9fc606aa9a 100644 +index 4bb88c55a82a83cef8330ec8157b49d5adb097ce..48f4e385a57eb0e00c90488fbf052900b6884775 100644 --- a/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc +++ b/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc @@ -7,10 +7,12 @@ @@ -52,8 +52,8 @@ index 37245c48cacbc5db164c61e4499f34ee83e0fce7..0fef12892e965e319e45f5dd1a9c5c9f Profile* profile = Profile::FromBrowserContext(render_process_host->GetBrowserContext()); HostContentSettingsMap* content_settings = -@@ -70,5 +73,6 @@ int32_t PepperBrokerMessageFilter::OnIsAllowed( - std::string()); +@@ -68,5 +71,6 @@ int32_t PepperBrokerMessageFilter::OnIsAllowed( + std::string()); if (setting == CONTENT_SETTING_ALLOW) return PP_OK; - return PP_ERROR_FAILED; @@ -61,7 +61,7 @@ index 37245c48cacbc5db164c61e4499f34ee83e0fce7..0fef12892e965e319e45f5dd1a9c5c9f + return PP_OK; } diff --git a/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc b/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc -index 91c1562d9894e57fd5c3abfde157127144b31a91..8f4caca848d649db9bd26a4ce37509b29865f3c1 100644 +index 860c5738d5731c901aeb9074074f3337d7fc4053..27435c3c91440e8c83e209097aae2b2231061eee 100644 --- a/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc +++ b/chrome/browser/renderer_host/pepper/pepper_flash_browser_host.cc @@ -8,9 +8,11 @@ @@ -76,7 +76,7 @@ index 91c1562d9894e57fd5c3abfde157127144b31a91..8f4caca848d649db9bd26a4ce37509b2 #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_ppapi_host.h" #include "content/public/browser/browser_task_traits.h" -@@ -42,6 +44,7 @@ using content::RenderProcessHost; +@@ -43,6 +45,7 @@ using content::RenderProcessHost; namespace { @@ -84,15 +84,15 @@ index 91c1562d9894e57fd5c3abfde157127144b31a91..8f4caca848d649db9bd26a4ce37509b2 // Get the CookieSettings on the UI thread for the given render process ID. scoped_refptr GetCookieSettings( int render_process_id) { -@@ -55,6 +58,7 @@ scoped_refptr GetCookieSettings( +@@ -56,6 +59,7 @@ scoped_refptr GetCookieSettings( } - return NULL; + return nullptr; } +#endif - void PepperBindConnectorRequest( - service_manager::mojom::ConnectorRequest connector_request) { -@@ -70,7 +74,9 @@ PepperFlashBrowserHost::PepperFlashBrowserHost(BrowserPpapiHost* host, + void PepperBindConnectorReceiver( + mojo::PendingReceiver +@@ -72,7 +76,9 @@ PepperFlashBrowserHost::PepperFlashBrowserHost(BrowserPpapiHost* host, PP_Instance instance, PP_Resource resource) : ResourceHost(host->GetPpapiHost(), instance, resource), @@ -102,7 +102,7 @@ index 91c1562d9894e57fd5c3abfde157127144b31a91..8f4caca848d649db9bd26a4ce37509b2 delay_timer_(FROM_HERE, base::TimeDelta::FromSeconds(45), this, -@@ -123,6 +129,7 @@ int32_t PepperFlashBrowserHost::OnGetLocalTimeZoneOffset( +@@ -125,6 +131,7 @@ int32_t PepperFlashBrowserHost::OnGetLocalTimeZoneOffset( int32_t PepperFlashBrowserHost::OnGetLocalDataRestrictions( ppapi::host::HostMessageContext* context) { @@ -110,7 +110,7 @@ index 91c1562d9894e57fd5c3abfde157127144b31a91..8f4caca848d649db9bd26a4ce37509b2 // Getting the Flash LSO settings requires using the CookieSettings which // belong to the profile which lives on the UI thread. We lazily initialize // |cookie_settings_| by grabbing the reference from the UI thread and then -@@ -143,9 +150,11 @@ int32_t PepperFlashBrowserHost::OnGetLocalDataRestrictions( +@@ -145,9 +152,11 @@ int32_t PepperFlashBrowserHost::OnGetLocalDataRestrictions( context->MakeReplyMessageContext(), document_url, plugin_url)); } @@ -123,7 +123,7 @@ index 91c1562d9894e57fd5c3abfde157127144b31a91..8f4caca848d649db9bd26a4ce37509b2 void PepperFlashBrowserHost::GetLocalDataRestrictions( ppapi::host::ReplyMessageContext reply_context, const GURL& document_url, -@@ -174,6 +183,7 @@ void PepperFlashBrowserHost::GetLocalDataRestrictions( +@@ -176,6 +185,7 @@ void PepperFlashBrowserHost::GetLocalDataRestrictions( PpapiPluginMsg_Flash_GetLocalDataRestrictionsReply( static_cast(restrictions))); } @@ -258,7 +258,7 @@ index b9d2ce7f7f6837ee7b304daac82d21a3147c6faf..4d4b023d62d12b4d119acbfdee64f415 base::WeakPtrFactory weak_factory_{this}; diff --git a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc -index 00db4e2fa2ac89d89ecd31174e192dcb17f3e912..df5a53c162059270ad673fffebbd432f6df0d212 100644 +index 0fa0fa341e93bd089ef2a22f74450af55db69f5b..9a6d8e5740d6ccede998efd3a96496acd9efabfb 100644 --- a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc +++ b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.cc @@ -8,17 +8,21 @@ @@ -285,7 +285,7 @@ index 00db4e2fa2ac89d89ecd31174e192dcb17f3e912..df5a53c162059270ad673fffebbd432f #include "ppapi/host/host_message_context.h" @@ -27,12 +31,11 @@ #include "ppapi/shared_impl/file_system_util.h" - #include "storage/browser/fileapi/isolated_context.h" + #include "storage/browser/file_system/isolated_context.h" -#if BUILDFLAG(ENABLE_EXTENSIONS) +#if 0 @@ -358,11 +358,11 @@ index 00db4e2fa2ac89d89ecd31174e192dcb17f3e912..df5a53c162059270ad673fffebbd432f int32_t PepperIsolatedFileSystemMessageFilter::OpenPluginPrivateFileSystem( ppapi::host::HostMessageContext* context) { diff --git a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h -index 1d5ff1f6aa14812f2a6552f601900b03f3e3c8e7..5b6d0297c874146c21af37b983b2d8ee387b31ce 100644 +index b36248debee4f5e0503e7611f7d1c47d59650f5b..7af31ee0d2cbb4c8fc7cd41c356305d6476815c5 100644 --- a/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h +++ b/chrome/browser/renderer_host/pepper/pepper_isolated_file_system_message_filter.h @@ -20,7 +20,9 @@ - #include "storage/browser/fileapi/isolated_context.h" + #include "storage/browser/file_system/isolated_context.h" #include "url/gurl.h" +#if 0 diff --git a/patches/chromium/port_aria_tree_fix_for_macos_voiceover.patch b/patches/chromium/port_aria_tree_fix_for_macos_voiceover.patch new file mode 100644 index 0000000000000..3863a125544de --- /dev/null +++ b/patches/chromium/port_aria_tree_fix_for_macos_voiceover.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Andy Locascio +Date: Mon, 24 Feb 2020 14:52:25 -0800 +Subject: port ARIA tree fix for macOS VoiceOver + +Port a CL that fixes the ARIA tree implementation so that voiceover can +correctly announce tree items. + +CL: https://chromium-review.googlesource.com/c/chromium/src/+/2062913 + +diff --git a/content/browser/accessibility/browser_accessibility_cocoa.h b/content/browser/accessibility/browser_accessibility_cocoa.h +index a5529ac8841b79f230f0fa8eae2b3cb226beb7d7..2e9ca33ccce50e7ca2552991e55878ca33e59bac 100644 +--- a/content/browser/accessibility/browser_accessibility_cocoa.h ++++ b/content/browser/accessibility/browser_accessibility_cocoa.h +@@ -75,6 +75,8 @@ struct AXTextEdit { + // left). + - (NSRect)rectInScreen:(gfx::Rect)rect; + ++- (void)getTreeItemDescendants:(NSMutableArray*)tree_items; ++ + // Return the method name for the given attribute. For testing only. + - (NSString*)methodNameForAttribute:(NSString*)attribute; + +diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm +index 917660269257bb51716e902d5ad70578793a4bb1..225d333884302310c8c4c0289043d8caad406c84 100644 +--- a/content/browser/accessibility/browser_accessibility_cocoa.mm ++++ b/content/browser/accessibility/browser_accessibility_cocoa.mm +@@ -2090,7 +2090,9 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; + return nil; + NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease]; + +- if (ui::IsTableLike(owner_->GetRole())) { ++ if (owner_->GetRole() == ax::mojom::Role::kTree) { ++ [self getTreeItemDescendants:ret]; ++ } else if (ui::IsTableLike(owner_->GetRole())) { + for (BrowserAccessibilityCocoa* child in [self children]) { + if ([[child role] isEqualToString:NSAccessibilityRowRole]) + [ret addObject:child]; +@@ -2509,6 +2511,19 @@ NSString* const NSAccessibilityRequiredAttributeChrome = @"AXRequired"; + return manager->GetWindow(); + } + ++- (void)getTreeItemDescendants:(NSMutableArray*)tree_items { ++ for (auto it = owner_->PlatformChildrenBegin(); ++ it != owner_->PlatformChildrenEnd(); ++it) { ++ const BrowserAccessibilityCocoa* child = ++ ToBrowserAccessibilityCocoa(it.get()); ++ ++ if ([child internalRole] == ax::mojom::Role::kTreeItem) { ++ [tree_items addObject:child]; ++ } ++ [child getTreeItemDescendants:tree_items]; ++ } ++} ++ + - (NSString*)methodNameForAttribute:(NSString*)attribute { + return [attributeToMethodNameMap objectForKey:attribute]; + } diff --git a/patches/chromium/preconnect_feature.patch b/patches/chromium/preconnect_feature.patch index 9e066889f0fed..34e0c58601f4a 100644 --- a/patches/chromium/preconnect_feature.patch +++ b/patches/chromium/preconnect_feature.patch @@ -8,10 +8,10 @@ this patch removes that dependency so we can reuse it. Ideally we would change this class in upstream to not depend on Profile. diff --git a/chrome/browser/predictors/preconnect_manager.cc b/chrome/browser/predictors/preconnect_manager.cc -index c3c7d4220f7ea612e5b86645854e4f426066e18c..969dd6659bfdc0fa3cf3edf494f1623ba10134a5 100644 +index bbaf654681992f7031dc4e8b37eeefa40815d870..d176b2783d3ee19455f2fe2734574e477ce22045 100644 --- a/chrome/browser/predictors/preconnect_manager.cc +++ b/chrome/browser/predictors/preconnect_manager.cc -@@ -70,7 +70,7 @@ PreresolveJob::PreresolveJob(PreresolveJob&& other) = default; +@@ -69,7 +69,7 @@ PreresolveJob::PreresolveJob(PreresolveJob&& other) = default; PreresolveJob::~PreresolveJob() = default; PreconnectManager::PreconnectManager(base::WeakPtr delegate, @@ -20,7 +20,7 @@ index c3c7d4220f7ea612e5b86645854e4f426066e18c..969dd6659bfdc0fa3cf3edf494f1623b : delegate_(std::move(delegate)), profile_(profile), inflight_preresolves_count_(0) { -@@ -317,11 +317,13 @@ network::mojom::NetworkContext* PreconnectManager::GetNetworkContext() const { +@@ -326,11 +326,13 @@ network::mojom::NetworkContext* PreconnectManager::GetNetworkContext() const { if (network_context_) return network_context_; @@ -35,7 +35,7 @@ index c3c7d4220f7ea612e5b86645854e4f426066e18c..969dd6659bfdc0fa3cf3edf494f1623b return content::BrowserContext::GetDefaultStoragePartition(profile_) ->GetNetworkContext(); diff --git a/chrome/browser/predictors/preconnect_manager.h b/chrome/browser/predictors/preconnect_manager.h -index e35163576b55d772098ae2a2138c4c1cf8438eac..70cad725f9476175b31859867c640ca8c66816a8 100644 +index c675b7cd44f22d84cdcc62fc6b833bd70514022f..cda68af1e32609539326467beeb5f60c4c29b055 100644 --- a/chrome/browser/predictors/preconnect_manager.h +++ b/chrome/browser/predictors/preconnect_manager.h @@ -22,6 +22,10 @@ @@ -49,7 +49,7 @@ index e35163576b55d772098ae2a2138c4c1cf8438eac..70cad725f9476175b31859867c640ca8 namespace network { namespace mojom { class NetworkContext; -@@ -138,7 +142,7 @@ class PreconnectManager { +@@ -140,7 +144,7 @@ class PreconnectManager { static const size_t kMaxInflightPreresolves = 3; @@ -58,7 +58,7 @@ index e35163576b55d772098ae2a2138c4c1cf8438eac..70cad725f9476175b31859867c640ca8 virtual ~PreconnectManager(); // Starts preconnect and preresolve jobs keyed by |url|. -@@ -196,7 +200,7 @@ class PreconnectManager { +@@ -205,7 +209,7 @@ class PreconnectManager { network::mojom::NetworkContext* GetNetworkContext() const; base::WeakPtr delegate_; diff --git a/patches/chromium/printing.patch b/patches/chromium/printing.patch index a5ce8e91b1c49..5909c94cd0b8c 100644 --- a/patches/chromium/printing.patch +++ b/patches/chromium/printing.patch @@ -11,27 +11,29 @@ majority of changes originally come from these PRs: This patch also fixes callback for manual user cancellation and success. diff --git a/chrome/browser/printing/print_job_worker.cc b/chrome/browser/printing/print_job_worker.cc -index 13f9d7af3ae796ecec3a9189aa59f4b20171fd7a..9c35b294340cce070a9f428dac6aa587c93ccc6c 100644 +index aacffd44658cac701c9683fcb3b20135a64c18bd..01cd31eb174e198724cab082e50564445ee5c6fd 100644 --- a/chrome/browser/printing/print_job_worker.cc +++ b/chrome/browser/printing/print_job_worker.cc -@@ -21,12 +21,12 @@ +@@ -21,7 +21,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/printing/print_job.h" -#include "chrome/grit/generated_resources.h" + #include "components/crash/core/common/crash_keys.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" - #include "content/public/browser/notification_service.h" +@@ -29,6 +28,7 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" + #include "printing/backend/print_backend.h" +#include "electron/grit/electron_resources.h" #include "printing/print_job_constants.h" #include "printing/printed_document.h" #include "printing/printing_utils.h" -@@ -203,9 +203,14 @@ void PrintJobWorker::SetSettingsFromPOD( - void PrintJobWorker::UpdatePrintSettings(base::Value new_settings, - SettingsCallback callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); +@@ -221,9 +221,14 @@ void PrintJobWorker::UpdatePrintSettings(base::Value new_settings, + print_backend->GetPrinterDriverInfo(printer_name)); + } + - PrintingContext::Result result = - printing_context_->UpdatePrintSettings(std::move(new_settings)); - GetSettingsDone(std::move(callback), result); @@ -46,7 +48,7 @@ index 13f9d7af3ae796ecec3a9189aa59f4b20171fd7a..9c35b294340cce070a9f428dac6aa587 } #if defined(OS_CHROMEOS) -@@ -221,6 +226,13 @@ void PrintJobWorker::UpdatePrintSettingsFromPOD( +@@ -239,6 +244,13 @@ void PrintJobWorker::UpdatePrintSettingsFromPOD( void PrintJobWorker::GetSettingsDone(SettingsCallback callback, PrintingContext::Result result) { @@ -61,7 +63,7 @@ index 13f9d7af3ae796ecec3a9189aa59f4b20171fd7a..9c35b294340cce070a9f428dac6aa587 } diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc -index 7d1e56b3b1a8691477598b0103e2c69c171ec2c8..980971e2b8dd143fde3376f17352f672f2c3a8ad 100644 +index 3d026cd11aa2c0b009812317995cd4e02161251e..f2f6ded6051a0659808ec848ea4d8566449243f6 100644 --- a/chrome/browser/printing/print_view_manager_base.cc +++ b/chrome/browser/printing/print_view_manager_base.cc @@ -27,10 +27,7 @@ @@ -115,25 +117,35 @@ index 7d1e56b3b1a8691477598b0103e2c69c171ec2c8..980971e2b8dd143fde3376f17352f672 } PrintViewManagerBase::~PrintViewManagerBase() { -@@ -123,12 +126,14 @@ PrintViewManagerBase::~PrintViewManagerBase() { +@@ -123,7 +126,10 @@ PrintViewManagerBase::~PrintViewManagerBase() { DisconnectFromCurrentPrintJob(); } -bool PrintViewManagerBase::PrintNow(content::RenderFrameHost* rfh) { +bool PrintViewManagerBase::PrintNow(content::RenderFrameHost* rfh, -+ std::unique_ptr message, -+ CompletionCallback callback) { ++ bool silent, ++ base::Value settings, ++ CompletionCallback callback) { DisconnectFromCurrentPrintJob(); + // Don't print / print preview interstitials or crashed tabs. +@@ -131,7 +137,14 @@ bool PrintViewManagerBase::PrintNow(content::RenderFrameHost* rfh) { + return false; + SetPrintingRFH(rfh); -- int32_t id = rfh->GetRoutingID(); -- return PrintNowInternal(rfh, std::make_unique(id)); +- GetPrintRenderFrame(rfh)->PrintRequestedPages(); + callback_ = std::move(callback); -+ return PrintNowInternal(rfh, std::move(message)); ++ ++ if (!callback_.is_null()) { ++ registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, ++ content::NotificationService::AllSources()); ++ } ++ ++ GetPrintRenderFrame(rfh)->PrintRequestedPages(silent, std::move(settings)); + return true; } - #if BUILDFLAG(ENABLE_PRINT_PREVIEW) -@@ -242,9 +247,9 @@ void PrintViewManagerBase::StartLocalPrintJob( +@@ -246,9 +259,9 @@ void PrintViewManagerBase::StartLocalPrintJob( void PrintViewManagerBase::UpdatePrintingEnabled() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // The Unretained() is safe because ForEachFrame() is synchronous. @@ -146,7 +158,7 @@ index 7d1e56b3b1a8691477598b0103e2c69c171ec2c8..980971e2b8dd143fde3376f17352f672 } void PrintViewManagerBase::NavigationStopped() { -@@ -347,7 +352,7 @@ void PrintViewManagerBase::OnPrintingFailed(int cookie) { +@@ -351,7 +364,7 @@ void PrintViewManagerBase::OnPrintingFailed(int cookie) { PrintManager::OnPrintingFailed(cookie); #if BUILDFLAG(ENABLE_PRINT_PREVIEW) @@ -155,7 +167,7 @@ index 7d1e56b3b1a8691477598b0103e2c69c171ec2c8..980971e2b8dd143fde3376f17352f672 #endif ReleasePrinterQuery(); -@@ -445,9 +450,13 @@ void PrintViewManagerBase::OnNotifyPrintJobEvent( +@@ -451,9 +464,13 @@ void PrintViewManagerBase::OnNotifyPrintJobEvent( content::NotificationService::NoDetails()); break; } @@ -171,16 +183,20 @@ index 7d1e56b3b1a8691477598b0103e2c69c171ec2c8..980971e2b8dd143fde3376f17352f672 NOTREACHED(); break; } -@@ -542,8 +551,6 @@ bool PrintViewManagerBase::CreateNewPrintJob( +@@ -548,8 +565,10 @@ bool PrintViewManagerBase::CreateNewPrintJob( DCHECK(!quit_inner_loop_); DCHECK(query); - // Disconnect the current |print_job_|. - DisconnectFromCurrentPrintJob(); ++ if (callback_.is_null()) { ++ // Disconnect the current |print_job_| only when calling window.print() ++ DisconnectFromCurrentPrintJob(); ++ } // We can't print if there is no renderer. if (!web_contents()->GetRenderViewHost() || -@@ -558,8 +565,6 @@ bool PrintViewManagerBase::CreateNewPrintJob( +@@ -564,8 +583,6 @@ bool PrintViewManagerBase::CreateNewPrintJob( print_job_->SetSource(PrintJob::Source::PRINT_PREVIEW, /*source_id=*/""); #endif // defined(OS_CHROMEOS) @@ -189,7 +205,7 @@ index 7d1e56b3b1a8691477598b0103e2c69c171ec2c8..980971e2b8dd143fde3376f17352f672 printing_succeeded_ = false; return true; } -@@ -608,6 +613,13 @@ void PrintViewManagerBase::ReleasePrintJob() { +@@ -614,14 +631,22 @@ void PrintViewManagerBase::ReleasePrintJob() { content::RenderFrameHost* rfh = printing_rfh_; printing_rfh_ = nullptr; @@ -203,9 +219,8 @@ index 7d1e56b3b1a8691477598b0103e2c69c171ec2c8..980971e2b8dd143fde3376f17352f672 if (!print_job_) return; -@@ -617,8 +629,9 @@ void PrintViewManagerBase::ReleasePrintJob() { - rfh->Send(msg.release()); - } + if (rfh) + GetPrintRenderFrame(rfh)->PrintingDone(printing_succeeded_); - registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, - content::Source(print_job_.get())); @@ -215,18 +230,17 @@ index 7d1e56b3b1a8691477598b0103e2c69c171ec2c8..980971e2b8dd143fde3376f17352f672 // Don't close the worker thread. print_job_ = nullptr; } -@@ -693,6 +706,9 @@ bool PrintViewManagerBase::PrintNowInternal( - // Don't print / print preview interstitials or crashed tabs. - if (IsInterstitialOrCrashed()) - return false; -+ -+ registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, -+ content::NotificationService::AllSources()); - return rfh->Send(message.release()); +@@ -657,7 +682,7 @@ bool PrintViewManagerBase::RunInnerMessageLoop() { } + bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) { +- if (print_job_) ++ if (print_job_ && print_job_->document()) + return true; + + if (!cookie) { diff --git a/chrome/browser/printing/print_view_manager_base.h b/chrome/browser/printing/print_view_manager_base.h -index af49d3e2f8abaf7dc4d82dc3f9beccdf4fbd9f18..dd9eb11d31b4ee3d415b1fca7d97872da132203d 100644 +index af49d3e2f8abaf7dc4d82dc3f9beccdf4fbd9f18..c5ef1a4c1577c509e5fbe0fcf06e6dfdba30c92c 100644 --- a/chrome/browser/printing/print_view_manager_base.h +++ b/chrome/browser/printing/print_view_manager_base.h @@ -33,6 +33,8 @@ class PrintJob; @@ -238,18 +252,19 @@ index af49d3e2f8abaf7dc4d82dc3f9beccdf4fbd9f18..dd9eb11d31b4ee3d415b1fca7d97872d // Base class for managing the print commands for a WebContents. class PrintViewManagerBase : public content::NotificationObserver, public PrintManager { -@@ -42,7 +44,9 @@ class PrintViewManagerBase : public content::NotificationObserver, +@@ -42,7 +44,10 @@ class PrintViewManagerBase : public content::NotificationObserver, // Prints the current document immediately. Since the rendering is // asynchronous, the actual printing will not be completed on the return of // this function. Returns false if printing is impossible at the moment. - virtual bool PrintNow(content::RenderFrameHost* rfh); + virtual bool PrintNow(content::RenderFrameHost* rfh, -+ std::unique_ptr message, ++ bool silent, ++ base::Value settings, + CompletionCallback callback); #if BUILDFLAG(ENABLE_PRINT_PREVIEW) // Prints the document in |print_data| with settings specified in -@@ -206,9 +210,15 @@ class PrintViewManagerBase : public content::NotificationObserver, +@@ -206,9 +211,15 @@ class PrintViewManagerBase : public content::NotificationObserver, // The current RFH that is printing with a system printing dialog. content::RenderFrameHost* printing_rfh_; @@ -377,26 +392,24 @@ index 9fbea6d0a2dbe55b1d600fbc217dee5aa8ae8cd5..de9bd267e408c02fd4da7d903523c0e6 // content::BrowserMessageFilter: bool OnMessageReceived(const IPC::Message& message) override; -diff --git a/components/printing/common/print_messages.h b/components/printing/common/print_messages.h -index f9f5d1c07e4068d0706770ca81bd92ce166c3822..9f0e079b941296fa27b8cf0a66abfa45a6183b42 100644 ---- a/components/printing/common/print_messages.h -+++ b/components/printing/common/print_messages.h -@@ -365,7 +365,9 @@ IPC_MESSAGE_ROUTED0(PrintMsg_PrintNodeUnderContextMenu) - #if BUILDFLAG(ENABLE_PRINTING) - // Tells the RenderFrame to switch the CSS to print media type, renders every - // requested pages and switch back the CSS to display media type. --IPC_MESSAGE_ROUTED0(PrintMsg_PrintPages) -+IPC_MESSAGE_ROUTED2(PrintMsg_PrintPages, -+ bool /* silent print */, -+ base::DictionaryValue /* settings */) - - // Like PrintMsg_PrintPages, but using the print preview document's frame/node. - IPC_MESSAGE_ROUTED0(PrintMsg_PrintForSystemDialog) +diff --git a/components/printing/common/print.mojom b/components/printing/common/print.mojom +index 34e1cda035c042c8c4c55acd7716cf9473856fcf..199267d873f2343cdf401bb9a335dd753e0d4dfd 100644 +--- a/components/printing/common/print.mojom ++++ b/components/printing/common/print.mojom +@@ -24,7 +24,7 @@ interface PrintRenderer { + interface PrintRenderFrame { + // Tells the RenderFrame to switch the CSS to print media type, render every + // requested page, and then switch back the CSS to display media type. +- PrintRequestedPages(); ++ PrintRequestedPages(bool silent, mojo_base.mojom.DictionaryValue settings); + + // Tells the RenderFrame to switch the CSS to print media type, render every + // requested page using the print preview document's frame/node, and then diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc -index 27cf579e4ca10661c34c130097783f02e13f3521..e8f1b610fb8fa700ac3de9df268a7b6a4b449850 100644 +index 41f6c6ba9920c7fe2a0cf91458162afb70762c16..5c23f9fba61c791365d9c69910ebb12154a6f238 100644 --- a/components/printing/renderer/print_render_frame_helper.cc +++ b/components/printing/renderer/print_render_frame_helper.cc -@@ -38,6 +38,7 @@ +@@ -39,6 +39,7 @@ #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "printing/buildflags/buildflags.h" #include "printing/metafile_skia.h" @@ -404,7 +417,7 @@ index 27cf579e4ca10661c34c130097783f02e13f3521..e8f1b610fb8fa700ac3de9df268a7b6a #include "printing/units.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" #include "third_party/blink/public/common/frame/frame_owner_element_type.h" -@@ -1149,7 +1150,8 @@ void PrintRenderFrameHelper::ScriptedPrint(bool user_initiated) { +@@ -1166,7 +1167,8 @@ void PrintRenderFrameHelper::ScriptedPrint(bool user_initiated) { web_frame->DispatchBeforePrintEvent(); if (!weak_this) return; @@ -414,37 +427,34 @@ index 27cf579e4ca10661c34c130097783f02e13f3521..e8f1b610fb8fa700ac3de9df268a7b6a if (weak_this) web_frame->DispatchAfterPrintEvent(); } -@@ -1224,7 +1226,9 @@ void PrintRenderFrameHelper::InitiatePrintPreview( - #endif // BUILDFLAG(ENABLE_PRINT_PREVIEW) +@@ -1202,7 +1204,7 @@ void PrintRenderFrameHelper::BindPrintRenderFrameReceiver( + receivers_.Add(this, std::move(receiver)); } --void PrintRenderFrameHelper::OnPrintPages() { -+void PrintRenderFrameHelper::OnPrintPages( -+ bool silent, -+ const base::DictionaryValue& settings) { +-void PrintRenderFrameHelper::PrintRequestedPages() { ++void PrintRenderFrameHelper::PrintRequestedPages(bool silent, base::Value settings) { + ScopedIPC scoped_ipc(weak_ptr_factory_.GetWeakPtr()); if (ipc_nesting_level_ > 1) return; - -@@ -1237,7 +1241,8 @@ void PrintRenderFrameHelper::OnPrintPages() { +@@ -1216,7 +1218,7 @@ void PrintRenderFrameHelper::PrintRequestedPages() { // If we are printing a PDF extension frame, find the plugin node and print // that instead. auto plugin = delegate_->GetPdfElement(frame); - Print(frame, plugin, PrintRequestType::kRegular); -+ Print(frame, plugin, PrintRequestType::kRegular, -+ silent, settings); - if (weak_this) ++ Print(frame, plugin, PrintRequestType::kRegular, silent, std::move(settings)); + if (!render_frame_gone_) frame->DispatchAfterPrintEvent(); // WARNING: |this| may be gone at this point. Do not do any more work here and -@@ -1254,7 +1259,7 @@ void PrintRenderFrameHelper::OnPrintForSystemDialog() { +@@ -1233,7 +1235,7 @@ void PrintRenderFrameHelper::PrintForSystemDialog() { + return; } - auto weak_this = weak_ptr_factory_.GetWeakPtr(); Print(frame, print_preview_context_.source_node(), - PrintRequestType::kRegular); + PrintRequestType::kRegular, false, base::DictionaryValue()); - if (weak_this) + if (!render_frame_gone_) frame->DispatchAfterPrintEvent(); // WARNING: |this| may be gone at this point. Do not do any more work here and -@@ -1289,6 +1294,8 @@ void PrintRenderFrameHelper::OnPrintPreview( +@@ -1315,6 +1317,8 @@ void PrintRenderFrameHelper::OnPrintPreview( if (ipc_nesting_level_ > 1) return; @@ -452,8 +462,28 @@ index 27cf579e4ca10661c34c130097783f02e13f3521..e8f1b610fb8fa700ac3de9df268a7b6a + print_preview_context_.InitWithFrame(frame); print_preview_context_.OnPrintPreview(); - UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent", -@@ -1662,7 +1669,9 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) { + base::UmaHistogramEnumeration(print_preview_context_.IsForArc() +@@ -1557,13 +1561,13 @@ bool PrintRenderFrameHelper::FinalizePrintReadyDocument() { + print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_CAPTURE_FAILED); + return false; + } +- } else { +- if (!CopyMetafileDataToReadOnlySharedMem(*metafile, ++ } ++ ++ if (!CopyMetafileDataToReadOnlySharedMem(*metafile, + &preview_params.content)) { +- LOG(ERROR) << "CopyMetafileDataToReadOnlySharedMem failed"; +- print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED); +- return false; +- } ++ LOG(ERROR) << "CopyMetafileDataToReadOnlySharedMem failed"; ++ print_preview_context_.set_error(PREVIEW_ERROR_METAFILE_COPY_FAILED); ++ return false; + } + + preview_params.document_cookie = print_pages_params_->params.document_cookie; +@@ -1760,7 +1764,9 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) { auto self = weak_ptr_factory_.GetWeakPtr(); Print(duplicate_node.GetDocument().GetFrame(), duplicate_node, @@ -464,27 +494,27 @@ index 27cf579e4ca10661c34c130097783f02e13f3521..e8f1b610fb8fa700ac3de9df268a7b6a // Check if |this| is still valid. if (!self) return; -@@ -1673,7 +1682,9 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) { +@@ -1771,7 +1777,9 @@ void PrintRenderFrameHelper::PrintNode(const blink::WebNode& node) { void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame, const blink::WebNode& node, - PrintRequestType print_request_type) { + PrintRequestType print_request_type, + bool silent, -+ const base::DictionaryValue& settings) { ++ base::Value settings) { // If still not finished with earlier print request simply ignore. if (prep_frame_view_) return; -@@ -1681,7 +1692,7 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame, +@@ -1779,7 +1787,7 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame, FrameReference frame_ref(frame); int expected_page_count = 0; - if (!CalculateNumberOfPages(frame, node, &expected_page_count)) { -+ if (!CalculateNumberOfPages(frame, node, &expected_page_count, settings)) { ++ if (!CalculateNumberOfPages(frame, node, &expected_page_count, base::Value::AsDictionaryValue(settings))) { DidFinishPrinting(FAIL_PRINT_INIT); return; // Failed to init print page settings. } -@@ -1701,8 +1712,11 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame, +@@ -1799,8 +1807,11 @@ void PrintRenderFrameHelper::Print(blink::WebLocalFrame* frame, PrintMsg_PrintPages_Params print_settings; auto self = weak_ptr_factory_.GetWeakPtr(); @@ -498,7 +528,7 @@ index 27cf579e4ca10661c34c130097783f02e13f3521..e8f1b610fb8fa700ac3de9df268a7b6a // Check if |this| is still valid. if (!self) return; -@@ -1926,10 +1940,23 @@ void PrintRenderFrameHelper::IPCProcessed() { +@@ -2024,10 +2035,23 @@ void PrintRenderFrameHelper::IPCProcessed() { base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); } @@ -525,7 +555,7 @@ index 27cf579e4ca10661c34c130097783f02e13f3521..e8f1b610fb8fa700ac3de9df268a7b6a // Check if the printer returned any settings, if the settings is empty, we // can safely assume there are no printer drivers configured. So we safely // terminate. -@@ -1949,12 +1976,14 @@ bool PrintRenderFrameHelper::InitPrintSettings(bool fit_to_paper_size) { +@@ -2047,12 +2071,14 @@ bool PrintRenderFrameHelper::InitPrintSettings(bool fit_to_paper_size) { return result; } @@ -545,31 +575,30 @@ index 27cf579e4ca10661c34c130097783f02e13f3521..e8f1b610fb8fa700ac3de9df268a7b6a Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id())); return false; diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h -index 00b3e4647a192411262bc5e7c1c0cfac3e3f81bb..5bade84672ef9d8b8acb8e8d503760aca6635cb7 100644 +index a432a1e007111d6c13c76255aa445d05b869e46c..3f66fd39345998fd11d0fd383b4262cca838abf8 100644 --- a/components/printing/renderer/print_render_frame_helper.h +++ b/components/printing/renderer/print_render_frame_helper.h -@@ -204,7 +204,8 @@ class PrintRenderFrameHelper - bool has_selection) override; - - // Message handlers --------------------------------------------------------- -- void OnPrintPages(); -+ void OnPrintPages(bool silent, -+ const base::DictionaryValue& settings); - void OnPrintForSystemDialog(); +@@ -224,7 +224,7 @@ class PrintRenderFrameHelper + mojo::PendingAssociatedReceiver receiver); + + // printing::mojom::PrintRenderFrame: +- void PrintRequestedPages() override; ++ void PrintRequestedPages(bool silent, base::Value settings) override; + void PrintForSystemDialog() override; #if BUILDFLAG(ENABLE_PRINT_PREVIEW) - void OnPrintPreview(const base::DictionaryValue& settings); -@@ -253,7 +254,9 @@ class PrintRenderFrameHelper + void InitiatePrintPreview( +@@ -291,7 +291,9 @@ class PrintRenderFrameHelper // WARNING: |this| may be gone after this method returns. void Print(blink::WebLocalFrame* frame, const blink::WebNode& node, - PrintRequestType print_request_type); + PrintRequestType print_request_type, + bool silent, -+ const base::DictionaryValue& settings); ++ base::Value settings); // Notification when printing is done - signal tear-down/free resources. void DidFinishPrinting(PrintingResult result); -@@ -262,12 +265,14 @@ class PrintRenderFrameHelper +@@ -300,12 +302,14 @@ class PrintRenderFrameHelper // Initialize print page settings with default settings. // Used only for native printing workflow. @@ -586,29 +615,11 @@ index 00b3e4647a192411262bc5e7c1c0cfac3e3f81bb..5bade84672ef9d8b8acb8e8d503760ac #if BUILDFLAG(ENABLE_PRINT_PREVIEW) // Set options for print preset from source PDF document. -diff --git a/printing/print_settings_conversion.cc b/printing/print_settings_conversion.cc -index 7403d3ea6200e2443ca23cb483c2a36fa6ff5027..05fcb8c2ef6d1074cf37b8974830d3fe1dd6e011 100644 ---- a/printing/print_settings_conversion.cc -+++ b/printing/print_settings_conversion.cc -@@ -184,11 +184,12 @@ bool PrintSettingsFromJobSettings(const base::Value& job_settings, - - settings->set_dpi_xy(dpi_horizontal.value(), dpi_vertical.value()); - #endif -+ if (!device_name->empty()) -+ settings->set_device_name(base::UTF8ToUTF16(*device_name)); - - settings->set_collate(collate.value()); - settings->set_copies(copies.value()); - settings->SetOrientation(landscape.value()); -- settings->set_device_name(base::UTF8ToUTF16(*device_name)); - settings->set_duplex_mode(static_cast(duplex_mode.value())); - settings->set_color(static_cast(color.value())); - settings->set_scale_factor(static_cast(scale_factor.value()) / 100.0); diff --git a/printing/printing_context.cc b/printing/printing_context.cc -index cd5c27c87df175676504a06b4e1904f6b836dc90..c4f6acf66bc69f1e7db633aa5b3b03a913ffb666 100644 +index 73940192472b1576a701cad3abbb92f2d72aa77e..bc0d39ccd113306691ae532e9fbc5b64c9aa0a33 100644 --- a/printing/printing_context.cc +++ b/printing/printing_context.cc -@@ -93,8 +93,6 @@ PrintingContext::Result PrintingContext::UsePdfSettings() { +@@ -90,8 +90,6 @@ PrintingContext::Result PrintingContext::UsePdfSettings() { PrintingContext::Result PrintingContext::UpdatePrintSettings( base::Value job_settings) { diff --git a/patches/chromium/proxy_config_monitor.patch b/patches/chromium/proxy_config_monitor.patch index 9d169294b2829..c681b36229112 100644 --- a/patches/chromium/proxy_config_monitor.patch +++ b/patches/chromium/proxy_config_monitor.patch @@ -6,7 +6,7 @@ Subject: proxy_config_monitor.patch Allow monitoring proxy config changes for a pref service. diff --git a/chrome/browser/net/proxy_config_monitor.cc b/chrome/browser/net/proxy_config_monitor.cc -index f1e287553244cfd1054c4949ffbb1acdaccbe1e2..94cbca68cedc314d55993375bc48159c9a9bfb3d 100644 +index 342db7adee575cc49a38bbdfcf1bbfcc0e30cf57..fc8e6b49d96534546dfa97846c2be79cff8a96e5 100644 --- a/chrome/browser/net/proxy_config_monitor.cc +++ b/chrome/browser/net/proxy_config_monitor.cc @@ -10,7 +10,9 @@ @@ -19,7 +19,7 @@ index f1e287553244cfd1054c4949ffbb1acdaccbe1e2..94cbca68cedc314d55993375bc48159c #include "components/proxy_config/pref_proxy_config_tracker_impl.h" #include "content/public/browser/browser_thread.h" #include "mojo/public/cpp/bindings/associated_interface_ptr.h" -@@ -19,12 +21,13 @@ +@@ -21,12 +23,13 @@ #include "chrome/browser/chromeos/profiles/profile_helper.h" #endif // defined(OS_CHROMEOS) @@ -34,7 +34,7 @@ index f1e287553244cfd1054c4949ffbb1acdaccbe1e2..94cbca68cedc314d55993375bc48159c ProxyConfigMonitor::ProxyConfigMonitor(Profile* profile) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK(profile); -@@ -54,6 +57,7 @@ ProxyConfigMonitor::ProxyConfigMonitor(Profile* profile) { +@@ -56,6 +59,7 @@ ProxyConfigMonitor::ProxyConfigMonitor(Profile* profile) { proxy_config_service_->AddObserver(this); } @@ -67,7 +67,7 @@ index f1e287553244cfd1054c4949ffbb1acdaccbe1e2..94cbca68cedc314d55993375bc48159c } #endif diff --git a/chrome/browser/net/proxy_config_monitor.h b/chrome/browser/net/proxy_config_monitor.h -index 0f20947c7819c3be2086a69f5997412652a99915..a88a1e001a4c1b8d7bafdac74fb060a1ee30361d 100644 +index abffb6dbdbf730e2cc4423eaca6a9a8a9f8af582..7fe1badcdebebce9028825ac98d7ab6cfa65dcdd 100644 --- a/chrome/browser/net/proxy_config_monitor.h +++ b/chrome/browser/net/proxy_config_monitor.h @@ -40,11 +40,12 @@ class ProxyConfigMonitor : public net::ProxyConfigService::Observer, @@ -84,11 +84,12 @@ index 0f20947c7819c3be2086a69f5997412652a99915..a88a1e001a4c1b8d7bafdac74fb060a1 // Creates a ProxyConfigMonitor that gets proxy settings from the // |local_state|, for use with NetworkContexts not // associated with a profile. Must be destroyed before |local_state|. -@@ -91,7 +92,6 @@ class ProxyConfigMonitor : public net::ProxyConfigService::Observer, +@@ -91,7 +92,7 @@ class ProxyConfigMonitor : public net::ProxyConfigService::Observer, #if BUILDFLAG(ENABLE_EXTENSIONS) - mojo::BindingSet error_binding_set_; + mojo::ReceiverSet error_receiver_set_; - Profile* profile_ = nullptr; ++ // Profile* profile_ = nullptr; #endif DISALLOW_COPY_AND_ASSIGN(ProxyConfigMonitor); diff --git a/patches/chromium/put_back_deleted_colors_for_autofill.patch b/patches/chromium/put_back_deleted_colors_for_autofill.patch index 61269b60b2f93..0a90ebee93dc0 100644 --- a/patches/chromium/put_back_deleted_colors_for_autofill.patch +++ b/patches/chromium/put_back_deleted_colors_for_autofill.patch @@ -8,14 +8,14 @@ needed in chromium but our autofill implementation uses them. This patch can be our autofill implementation to work like Chromium's. diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk.cc b/chrome/browser/ui/libgtkui/native_theme_gtk.cc -index b77b8a53b0246b05e3c45e74eaf2b5c44c6dad88..7af171f6cd5d76e87e2fca32f5dbcc34d7e0a974 100644 +index 63420c11515cb6b7dce68e005fa969360a4674be..ad4591b5c4412694ce4a53b74fb63b721d5ecb3a 100644 --- a/chrome/browser/ui/libgtkui/native_theme_gtk.cc +++ b/chrome/browser/ui/libgtkui/native_theme_gtk.cc -@@ -279,6 +279,27 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) { +@@ -278,6 +278,27 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) { case ui::NativeTheme::kColorId_TableHeaderSeparator: return GetBorderColor("GtkTreeView#treeview.view GtkButton#button"); -+ // Results Table ++ // Results Table + case ui::NativeTheme::kColorId_ResultsTableNormalBackground: + return SkColorFromColorId( + ui::NativeTheme::kColorId_TextfieldDefaultBackground); @@ -40,10 +40,10 @@ index b77b8a53b0246b05e3c45e74eaf2b5c44c6dad88..7af171f6cd5d76e87e2fca32f5dbcc34 // TODO(thomasanderson): Render GtkSpinner directly. case ui::NativeTheme::kColorId_ThrobberSpinningColor: diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc -index 7fd49a2e26968daaa16ed3322b516f53ff60fb56..f902910ecc8e5b4945187e836655362ebba49204 100644 +index 7e068dbcd68901e637ce6ea09d71d88739b0c8d2..36481b7e138ab90dbbbc886ad58f7e1c9a2605ba 100644 --- a/ui/native_theme/common_theme.cc +++ b/ui/native_theme/common_theme.cc -@@ -56,6 +56,14 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, +@@ -58,6 +58,14 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, case NativeTheme::kColorId_BubbleFooterBackground: return SkColorSetRGB(0x32, 0x36, 0x39); @@ -57,10 +57,10 @@ index 7fd49a2e26968daaa16ed3322b516f53ff60fb56..f902910ecc8e5b4945187e836655362e + // FocusableBorder case NativeTheme::kColorId_FocusedBorderColor: - return SkColorSetA(gfx::kGoogleBlue300, 0x66); -@@ -359,6 +367,18 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, + return SkColorSetA(gfx::kGoogleBlue300, 0x4D); +@@ -370,6 +378,18 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, case NativeTheme::kColorId_UnfocusedBorderColor: - return SkColorSetA(SK_ColorBLACK, 0x66); + return gfx::kGoogleGrey300; + // Results Tables + case NativeTheme::kColorId_ResultsTableNormalBackground: @@ -78,10 +78,10 @@ index 7fd49a2e26968daaa16ed3322b516f53ff60fb56..f902910ecc8e5b4945187e836655362e case NativeTheme::kColorId_ThrobberSpinningColor: return gfx::kGoogleBlue600; diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h -index c1d4ad9a27d895af32fd0311a640e2f62a30635a..ef45da79366c2e3ab242ec140989b407ac70dbb8 100644 +index d78379d48c341dde6a10788919e0eb3bfb05c54e..205181053803813b64497884bc93cfd857254866 100644 --- a/ui/native_theme/native_theme.h +++ b/ui/native_theme/native_theme.h -@@ -389,6 +389,11 @@ class NATIVE_THEME_EXPORT NativeTheme { +@@ -391,6 +391,11 @@ class NATIVE_THEME_EXPORT NativeTheme { kColorId_TableHeaderText, kColorId_TableHeaderBackground, kColorId_TableHeaderSeparator, @@ -94,11 +94,11 @@ index c1d4ad9a27d895af32fd0311a640e2f62a30635a..ef45da79366c2e3ab242ec140989b407 kColorId_ThrobberSpinningColor, kColorId_ThrobberWaitingColor, diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc -index 5b1804538c2e78af23c797004f250369580b4178..97e72bc38a7231ce295ad943e977c69586cc6e07 100644 +index a7c06b8a046bba7096bfb41c84d2073980ffa6d3..cff7681a4e0023a67edda37bf9865ab073b32549 100644 --- a/ui/native_theme/native_theme_win.cc +++ b/ui/native_theme/native_theme_win.cc -@@ -673,6 +673,17 @@ SkColor NativeThemeWin::GetSystemColor(ColorId color_id, - case kColorId_TableGroupingIndicatorColor: +@@ -619,6 +619,18 @@ SkColor NativeThemeWin::GetPlatformHighContrastColor(ColorId color_id) const { + case kColorId_ThrobberWaitingColor: return system_colors_[SystemThemeColor::kGrayText]; + // Results Tables @@ -112,6 +112,7 @@ index 5b1804538c2e78af23c797004f250369580b4178..97e72bc38a7231ce295ad943e977c695 + case kColorId_ResultsTableDimmedText: + return color_utils::AlphaBlend(system_colors_[SystemThemeColor::kWindowText], + system_colors_[SystemThemeColor::kWindow], 0.5f); - default: - break; - } ++ + // Button Background + case kColorId_MenuBackgroundColor: + case kColorId_HighlightedMenuItemBackgroundColor: diff --git a/patches/chromium/remove_usage_of_incognito_apis_in_the_spellchecker.patch b/patches/chromium/remove_usage_of_incognito_apis_in_the_spellchecker.patch new file mode 100644 index 0000000000000..80e1100f31b05 --- /dev/null +++ b/patches/chromium/remove_usage_of_incognito_apis_in_the_spellchecker.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Wed, 23 Oct 2019 11:43:58 -0700 +Subject: remove usage of incognito APIs in the spellchecker + +chrome::GetBrowserContextRedirectedInIncognito is not available in +Electron nor do we want it to be. We could potentially upstream a +change to move more of //chrome spellchecker logic into //components so +that we can further separate our dependency from //chrome. + +diff --git a/chrome/browser/spellchecker/spellcheck_factory.cc b/chrome/browser/spellchecker/spellcheck_factory.cc +index 5253d1f48e188b0339834c876378677b459e718e..48ac0a24efde0cb7d3ba71c8b8bdf5178f606e80 100644 +--- a/chrome/browser/spellchecker/spellcheck_factory.cc ++++ b/chrome/browser/spellchecker/spellcheck_factory.cc +@@ -78,7 +78,10 @@ void SpellcheckServiceFactory::RegisterProfilePrefs( + + content::BrowserContext* SpellcheckServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { ++ return context; ++#if 0 + return chrome::GetBrowserContextRedirectedInIncognito(context); ++#endif + } + + bool SpellcheckServiceFactory::ServiceIsNULLWhileTesting() const { diff --git a/patches/chromium/render_widget_host_view_base.patch b/patches/chromium/render_widget_host_view_base.patch index e5dcab5ffcc87..ce16cf6a92951 100644 --- a/patches/chromium/render_widget_host_view_base.patch +++ b/patches/chromium/render_widget_host_view_base.patch @@ -5,10 +5,10 @@ Subject: render_widget_host_view_base.patch diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc -index 50aad3c8d627ab4e65c1438266c2c1c68113dccf..4a708fdfacfb8fcc2ad07d5f10b475e2169b62ee 100644 +index 394b9ec0b7409465b0f9700a2d4a1eb1cf538e1f..a34dcdd124746b04d116e207b10395fc02649ec1 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.cc +++ b/content/browser/renderer_host/render_widget_host_view_base.cc -@@ -631,6 +631,15 @@ bool RenderWidgetHostViewBase::ScreenRectIsUnstableFor( +@@ -619,6 +619,13 @@ bool RenderWidgetHostViewBase::ScreenRectIsUnstableFor( return false; } @@ -16,16 +16,14 @@ index 50aad3c8d627ab4e65c1438266c2c1c68113dccf..4a708fdfacfb8fcc2ad07d5f10b475e2 + RenderWidgetHost* render_widget_host, + RenderWidgetHost* embedder_render_widget_host, + WebContentsView* web_contents_view) { -+ return web_contents_view->CreateViewForWidget( -+ render_widget_host, -+ !!embedder_render_widget_host); ++ return web_contents_view->CreateViewForWidget(render_widget_host); +} + void RenderWidgetHostViewBase::ProcessMouseEvent( const blink::WebMouseEvent& event, const ui::LatencyInfo& latency) { diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h -index add7df1a4432d8a6e4ebec892ab2708ba6650af5..d681d70e8ca9b2c228295336484d9b68240ce26b 100644 +index 9763b72a68992fc97cf1d1ec10d55c1198ec1771..1ece7c0cdfa2ab519d5ea0ef31465cda305830fd 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.h +++ b/content/browser/renderer_host/render_widget_host_view_base.h @@ -24,9 +24,11 @@ @@ -52,7 +50,7 @@ index add7df1a4432d8a6e4ebec892ab2708ba6650af5..d681d70e8ca9b2c228295336484d9b68 class WebCursor; class DelegatedFrameHost; struct TextInputState; -@@ -133,6 +137,9 @@ class CONTENT_EXPORT RenderWidgetHostViewBase +@@ -132,6 +136,9 @@ class CONTENT_EXPORT RenderWidgetHostViewBase bool destination_is_loaded, bool destination_is_frozen) final; @@ -62,7 +60,7 @@ index add7df1a4432d8a6e4ebec892ab2708ba6650af5..d681d70e8ca9b2c228295336484d9b68 // This only needs to be overridden by RenderWidgetHostViewBase subclasses // that handle content embedded within other RenderWidgetHostViews. gfx::PointF TransformPointToRootCoordSpaceF( -@@ -350,6 +357,11 @@ class CONTENT_EXPORT RenderWidgetHostViewBase +@@ -342,6 +349,11 @@ class CONTENT_EXPORT RenderWidgetHostViewBase virtual void ProcessGestureEvent(const blink::WebGestureEvent& event, const ui::LatencyInfo& latency); diff --git a/patches/chromium/render_widget_host_view_mac.patch b/patches/chromium/render_widget_host_view_mac.patch index 50fe4ffdada08..37d270c1739d7 100644 --- a/patches/chromium/render_widget_host_view_mac.patch +++ b/patches/chromium/render_widget_host_view_mac.patch @@ -5,7 +5,7 @@ Subject: render_widget_host_view_mac.patch diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm -index d8de080eb6a30f16282661941b9a95da4fa41d70..326c5a13f6924643c699ca3cc2f98262a21fd907 100644 +index 320287e7cc5a2f11f135494a11e0a51d3818d41c..e88ed7afb6ea20c7a5cebba6cfe8c0b5fef1a52c 100644 --- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm +++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm @@ -153,6 +153,11 @@ void ExtractUnderlines(NSAttributedString* string, @@ -20,7 +20,7 @@ index d8de080eb6a30f16282661941b9a95da4fa41d70..326c5a13f6924643c699ca3cc2f98262 // These are not documented, so use only after checking -respondsToSelector:. @interface NSApplication (UndocumentedSpeechMethods) - (void)speakString:(NSString*)string; -@@ -572,6 +577,9 @@ - (BOOL)acceptsMouseEventsWhenInactive { +@@ -572,6 +577,9 @@ void ExtractUnderlines(NSAttributedString* string, } - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent { @@ -30,7 +30,7 @@ index d8de080eb6a30f16282661941b9a95da4fa41d70..326c5a13f6924643c699ca3cc2f98262 return [self acceptsMouseEventsWhenInactive]; } -@@ -990,6 +998,10 @@ - (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv { +@@ -990,6 +998,10 @@ void ExtractUnderlines(NSAttributedString* string, eventType == NSKeyDown && !(modifierFlags & NSCommandKeyMask); @@ -41,7 +41,7 @@ index d8de080eb6a30f16282661941b9a95da4fa41d70..326c5a13f6924643c699ca3cc2f98262 // We only handle key down events and just simply forward other events. if (eventType != NSKeyDown) { hostHelper_->ForwardKeyboardEvent(event, latency_info); -@@ -1769,9 +1781,11 @@ - (NSAccessibilityRole)accessibilityRole { +@@ -1769,9 +1781,11 @@ void ExtractUnderlines(NSAttributedString* string, // Since this implementation doesn't have to wait any IPC calls, this doesn't // make any key-typing jank. --hbono 7/23/09 // @@ -53,7 +53,7 @@ index d8de080eb6a30f16282661941b9a95da4fa41d70..326c5a13f6924643c699ca3cc2f98262 - (NSArray*)validAttributesForMarkedText { // This code is just copied from WebKit except renaming variables. -@@ -1780,7 +1794,10 @@ - (NSArray*)validAttributesForMarkedText { +@@ -1780,7 +1794,10 @@ extern NSString* NSTextInputReplacementRangeAttributeName; initWithObjects:NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, NSMarkedClauseSegmentAttributeName, @@ -66,7 +66,7 @@ index d8de080eb6a30f16282661941b9a95da4fa41d70..326c5a13f6924643c699ca3cc2f98262 return validAttributesForMarkedText_.get(); } diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm -index ed9ea51809ec7f06b246b933281dc22b9b0055f7..32f3f2cceb742065db8b52a9f6aa814cfc30f955 100644 +index 275caf58aa73b1a775554753ad1ffe55fd93d45d..b6897c88c2c433e73f092f2ccebf29f097db35b0 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -63,6 +63,7 @@ diff --git a/patches/chromium/resource_file_conflict.patch b/patches/chromium/resource_file_conflict.patch index 5124163d5b78c..8322876117888 100644 --- a/patches/chromium/resource_file_conflict.patch +++ b/patches/chromium/resource_file_conflict.patch @@ -52,10 +52,10 @@ Some alternatives to this patch: None of these options seems like a substantial maintainability win over this patch to me (@nornagon). diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn -index 92fe7cd1cc2181277c4c49ee978762d1b32c8116..858d53eb3f7e5b358860e5ea3b15612e4015b1ed 100644 +index c38d03923119b07ee31ea1665bf2079da5c8c423..f4ab0f5ed445cb498cba8d190e225970dd458711 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn -@@ -1599,7 +1599,7 @@ if (is_chrome_branded && !is_android) { +@@ -1619,7 +1619,7 @@ if (is_chrome_branded && !is_android) { } } @@ -64,7 +64,7 @@ index 92fe7cd1cc2181277c4c49ee978762d1b32c8116..858d53eb3f7e5b358860e5ea3b15612e chrome_paks("packed_resources") { if (is_mac) { output_dir = "$root_gen_dir/repack" -@@ -1623,6 +1623,12 @@ if (!is_android) { +@@ -1643,6 +1643,12 @@ if (!is_android) { } } diff --git a/patches/chromium/revert_cleanup_remove_menu_subtitles_sublabels.patch b/patches/chromium/revert_cleanup_remove_menu_subtitles_sublabels.patch index 0d147f9f61465..8b418b6031c72 100644 --- a/patches/chromium/revert_cleanup_remove_menu_subtitles_sublabels.patch +++ b/patches/chromium/revert_cleanup_remove_menu_subtitles_sublabels.patch @@ -112,7 +112,7 @@ index a258d038da4a2bbfc6ec13c250781166235c1fbc..f6dbaa19cdb8938204c3452622589708 + EXPECT_EQ(7, changed_count()); } diff --git a/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc b/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc -index 654d0bf906c4785321a1fcdad9e12a22dbbbc170..6159eca4a400112f10647defe857c72740ff2779 100644 +index 1d7f5dc3abe95ffaa847454e2be1e9872fedc287..6e67fff6631d2d5339df6a912d917862bce37f3d 100644 --- a/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/menu_item_view_interactive_uitest.cc @@ -91,8 +91,8 @@ class MenuItemViewTestInsert : public MenuTestBase { @@ -180,10 +180,10 @@ index 73f0ab6d84d2cab6732866a6dc4b781faf630c0e..3319d058e8303066e0159d02d27ee2e8 return base::string16(); } diff --git a/ui/base/models/menu_model.h b/ui/base/models/menu_model.h -index b0ae539ba1dc5a4c11390267c3053058e92e7f34..1e28cd2b1bc66d512ea4e0b8ae62bc8ec03d7a1b 100644 +index 5bcc6204c2b579fb2cba47ba1bc156967ec87faf..34e647d56dfe583a3484fb8df0a148064eacb07b 100644 --- a/ui/base/models/menu_model.h +++ b/ui/base/models/menu_model.h -@@ -65,6 +65,10 @@ class UI_BASE_EXPORT MenuModel : public base::SupportsWeakPtr { +@@ -67,6 +67,10 @@ class UI_BASE_EXPORT MenuModel : public base::SupportsWeakPtr { // Returns the label of the item at the specified index. virtual base::string16 GetLabelAt(int index) const = 0; @@ -195,10 +195,10 @@ index b0ae539ba1dc5a4c11390267c3053058e92e7f34..1e28cd2b1bc66d512ea4e0b8ae62bc8e // is rendered to the right of the label and using the font GetLabelFontAt(). virtual base::string16 GetMinorTextAt(int index) const; diff --git a/ui/base/models/simple_menu_model.cc b/ui/base/models/simple_menu_model.cc -index 0aeeb3e47074590834de6d9ca7e3eb7dbf6a7793..5efc0589644e243a095765710302af992d53f5b8 100644 +index 6a55c0f82ce3df3d8b2b31dbb67830bb5464019c..c42dee1fc9ddccf5b5556fff7282a64d14c8333d 100644 --- a/ui/base/models/simple_menu_model.cc +++ b/ui/base/models/simple_menu_model.cc -@@ -42,6 +42,11 @@ base::string16 SimpleMenuModel::Delegate::GetLabelForCommandId( +@@ -46,6 +46,16 @@ base::string16 SimpleMenuModel::Delegate::GetLabelForCommandId( return base::string16(); } @@ -207,22 +207,15 @@ index 0aeeb3e47074590834de6d9ca7e3eb7dbf6a7793..5efc0589644e243a095765710302af99 + return base::string16(); +} + - base::string16 SimpleMenuModel::Delegate::GetMinorTextForCommandId( - int command_id) const { - return base::string16(); -@@ -324,6 +329,11 @@ void SimpleMenuModel::SetLabel(int index, const base::string16& label) { - MenuItemsChanged(); - } - +void SimpleMenuModel::SetSublabel(int index, const base::string16& sublabel) { + items_[ValidateItemIndex(index)].sublabel = sublabel; + MenuItemsChanged(); +} + - void SimpleMenuModel::SetMinorText(int index, - const base::string16& minor_text) { - items_[ValidateItemIndex(index)].minor_text = minor_text; -@@ -398,6 +408,12 @@ base::string16 SimpleMenuModel::GetLabelAt(int index) const { + bool SimpleMenuModel::Delegate::GetIconForCommandId( + int command_id, gfx::Image* image_skia) const { + return false; +@@ -404,6 +414,12 @@ base::string16 SimpleMenuModel::GetLabelAt(int index) const { return items_[ValidateItemIndex(index)].label; } @@ -233,27 +226,21 @@ index 0aeeb3e47074590834de6d9ca7e3eb7dbf6a7793..5efc0589644e243a095765710302af99 +} + base::string16 SimpleMenuModel::GetMinorTextAt(int index) const { - if (IsItemDynamicAt(index)) - return delegate_->GetMinorTextForCommandId(GetCommandIdAt(index)); + return items_[ValidateItemIndex(index)].minor_text; + } diff --git a/ui/base/models/simple_menu_model.h b/ui/base/models/simple_menu_model.h -index 3cc9d686da8f64ddc8bdc66df40b1866bdd20607..dfacbdd4a55b5f5aa4d8c67136c607f75a880278 100644 +index 1ae8909a1404a43b34b9ef585511c7a3de39fa28..0d2a74ab5e9d55201a4992d81aecf9c2a83af5b8 100644 --- a/ui/base/models/simple_menu_model.h +++ b/ui/base/models/simple_menu_model.h -@@ -44,10 +44,11 @@ class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { - // Delegate should return true if |command_id| should be visible. - virtual bool IsCommandIdVisible(int command_id) const; - -- // Some command ids have labels, minor text and icons that change over -- // time. -+ // Some command ids have labels, sublabels, minor text and icons that change -+ // over time. +@@ -47,6 +47,7 @@ class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { + // Some command ids have labels and icons that change over time. virtual bool IsItemForCommandIdDynamic(int command_id) const; virtual base::string16 GetLabelForCommandId(int command_id) const; + virtual base::string16 GetSublabelForCommandId(int command_id) const; - virtual base::string16 GetMinorTextForCommandId(int command_id) const; // Gets the icon for the item with the specified id, returning true if there // is an icon, false otherwise. -@@ -175,6 +176,9 @@ class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { + virtual bool GetIconForCommandId(int command_id, +@@ -174,6 +175,9 @@ class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { // Sets the label for the item at |index|. void SetLabel(int index, const base::string16& label); @@ -263,7 +250,7 @@ index 3cc9d686da8f64ddc8bdc66df40b1866bdd20607..dfacbdd4a55b5f5aa4d8c67136c607f7 // Sets the minor text for the item at |index|. void SetMinorText(int index, const base::string16& minor_text); -@@ -201,6 +205,7 @@ class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { +@@ -200,6 +204,7 @@ class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { ui::MenuSeparatorType GetSeparatorTypeAt(int index) const override; int GetCommandIdAt(int index) const override; base::string16 GetLabelAt(int index) const override; @@ -271,7 +258,7 @@ index 3cc9d686da8f64ddc8bdc66df40b1866bdd20607..dfacbdd4a55b5f5aa4d8c67136c607f7 base::string16 GetMinorTextAt(int index) const override; const gfx::VectorIcon* GetMinorIconAt(int index) const override; bool IsItemDynamicAt(int index) const override; -@@ -236,6 +241,7 @@ class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { +@@ -235,6 +240,7 @@ class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { int command_id = 0; ItemType type = TYPE_COMMAND; base::string16 label; @@ -280,10 +267,10 @@ index 3cc9d686da8f64ddc8bdc66df40b1866bdd20607..dfacbdd4a55b5f5aa4d8c67136c607f7 const gfx::VectorIcon* minor_icon = nullptr; gfx::Image icon; diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc -index 0fcca1e59688eda967c59397ea932637810af27b..35e7e3dc8f18fffa1713d6821c2bef87bdfce181 100644 +index e86941132ce80e262d1961ff3cdad79b44abb878..6e67ffea103e7169d35b37455ded83d8d812d77b 100644 --- a/ui/views/controls/menu/menu_item_view.cc +++ b/ui/views/controls/menu/menu_item_view.cc -@@ -277,6 +277,7 @@ MenuItemView* MenuItemView::AddMenuItemAt( +@@ -278,6 +278,7 @@ MenuItemView* MenuItemView::AddMenuItemAt( int index, int item_id, const base::string16& label, @@ -291,7 +278,7 @@ index 0fcca1e59688eda967c59397ea932637810af27b..35e7e3dc8f18fffa1713d6821c2bef87 const base::string16& minor_text, const gfx::VectorIcon* minor_icon, const gfx::ImageSkia& icon, -@@ -297,6 +298,7 @@ MenuItemView* MenuItemView::AddMenuItemAt( +@@ -298,6 +299,7 @@ MenuItemView* MenuItemView::AddMenuItemAt( item->SetTitle(GetDelegate()->GetLabel(item_id)); else item->SetTitle(label); @@ -299,45 +286,24 @@ index 0fcca1e59688eda967c59397ea932637810af27b..35e7e3dc8f18fffa1713d6821c2bef87 item->SetMinorText(minor_text); item->SetMinorIcon(minor_icon); if (vector_icon) { -@@ -338,21 +340,23 @@ void MenuItemView::RemoveAllMenuItems() { +@@ -339,21 +341,22 @@ void MenuItemView::RemoveAllMenuItems() { MenuItemView* MenuItemView::AppendMenuItem(int item_id, const base::string16& label, - Type type) { -- return AppendMenuItemImpl(item_id, label, base::string16(), nullptr, -- gfx::ImageSkia(), type, ui::NORMAL_SEPARATOR); -+ return AppendMenuItemImpl(item_id, label, base::string16(), base::string16(), -+ nullptr, gfx::ImageSkia(), type, -+ ui::NORMAL_SEPARATOR); + const gfx::ImageSkia& icon) { +- return AppendMenuItemImpl(item_id, label, icon, NORMAL); ++ return AppendMenuItemImpl(item_id, label, base::string16(), icon, NORMAL); } MenuItemView* MenuItemView::AppendSubMenu(int item_id, - const base::string16& label) { -- return AppendMenuItemImpl(item_id, label, base::string16(), nullptr, -- gfx::ImageSkia(), SUBMENU, ui::NORMAL_SEPARATOR); -+ return AppendMenuItemImpl(item_id, label, base::string16(), base::string16(), -+ nullptr, gfx::ImageSkia(), SUBMENU, -+ ui::NORMAL_SEPARATOR); - } - - MenuItemView* MenuItemView::AppendSubMenuWithIcon(int item_id, - const base::string16& label, - const gfx::ImageSkia& icon) { -- return AppendMenuItemImpl(item_id, label, base::string16(), nullptr, icon, -- SUBMENU, ui::NORMAL_SEPARATOR); -+ return AppendMenuItemImpl(item_id, label, base::string16(), base::string16(), -+ nullptr, icon, SUBMENU, ui::NORMAL_SEPARATOR); - } - - MenuItemView* MenuItemView::AppendMenuItemWithLabel( -@@ -366,12 +370,14 @@ MenuItemView* MenuItemView::AppendDelegateMenuItem(int item_id) { + const base::string16& label, + const gfx::ImageSkia& icon) { +- return AppendMenuItemImpl(item_id, label, icon, SUBMENU); ++ return AppendMenuItemImpl(item_id, label, base::string16(), icon, SUBMENU); } void MenuItemView::AppendSeparator() { -- AppendMenuItemImpl(0, base::string16(), base::string16(), nullptr, -- gfx::ImageSkia(), SEPARATOR, ui::NORMAL_SEPARATOR); -+ AppendMenuItemImpl(0, base::string16(), base::string16(), base::string16(), -+ nullptr, gfx::ImageSkia(), SEPARATOR, -+ ui::NORMAL_SEPARATOR); +- AppendMenuItemImpl(0, base::string16(), gfx::ImageSkia(), SEPARATOR); ++ AppendMenuItemImpl(0, base::string16(), base::string16(), gfx::ImageSkia(), SEPARATOR); } void MenuItemView::AddSeparatorAt(int index) { @@ -346,34 +312,20 @@ index 0fcca1e59688eda967c59397ea932637810af27b..35e7e3dc8f18fffa1713d6821c2bef87 /*minor_text=*/base::string16(), /*minor_icon=*/nullptr, /*icon=*/gfx::ImageSkia(), /*vector_icon=*/nullptr, /*type=*/SEPARATOR, -@@ -381,21 +387,22 @@ void MenuItemView::AddSeparatorAt(int index) { - MenuItemView* MenuItemView::AppendMenuItemWithIcon(int item_id, - const base::string16& label, - const gfx::ImageSkia& icon) { -- return AppendMenuItemImpl(item_id, label, base::string16(), nullptr, icon, -- NORMAL, ui::NORMAL_SEPARATOR); -+ return AppendMenuItemImpl(item_id, label, base::string16(), base::string16(), -+ nullptr, icon, NORMAL, ui::NORMAL_SEPARATOR); - } +@@ -362,10 +365,11 @@ void MenuItemView::AddSeparatorAt(int index) { - MenuItemView* MenuItemView::AppendMenuItemImpl( - int item_id, - const base::string16& label, -+ const base::string16& sublabel, - const base::string16& minor_text, - const gfx::VectorIcon* minor_icon, - const gfx::ImageSkia& icon, - Type type, - ui::MenuSeparatorType separator_style) { + MenuItemView* MenuItemView::AppendMenuItemImpl(int item_id, + const base::string16& label, ++ const base::string16& sublabel, + const gfx::ImageSkia& icon, + Type type) { const int index = submenu_ ? int{submenu_->children().size()} : 0; -- return AddMenuItemAt(index, item_id, label, minor_text, minor_icon, icon, -- nullptr, type, separator_style); -+ return AddMenuItemAt(index, item_id, label, sublabel, minor_text, minor_icon, -+ icon, nullptr, type, separator_style); +- return AddMenuItemAt(index, item_id, label, base::string16(), nullptr, icon, ++ return AddMenuItemAt(index, item_id, label, sublabel, base::string16(), nullptr, icon, + nullptr, type, ui::NORMAL_SEPARATOR); } - SubmenuView* MenuItemView::CreateSubmenu() { -@@ -427,6 +434,11 @@ void MenuItemView::SetTitle(const base::string16& title) { +@@ -398,6 +402,11 @@ void MenuItemView::SetTitle(const base::string16& title) { invalidate_dimensions(); // Triggers preferred size recalculation. } @@ -385,7 +337,7 @@ index 0fcca1e59688eda967c59397ea932637810af27b..35e7e3dc8f18fffa1713d6821c2bef87 void MenuItemView::SetMinorText(const base::string16& minor_text) { minor_text_ = minor_text; invalidate_dimensions(); // Triggers preferred size recalculation. -@@ -1016,13 +1028,23 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { +@@ -987,13 +996,23 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { (!delegate || delegate->ShouldReserveSpaceForSubmenuIndicator() ? item_right_margin_ : config.arrow_to_edge_padding); @@ -410,7 +362,7 @@ index 0fcca1e59688eda967c59397ea932637810af27b..35e7e3dc8f18fffa1713d6821c2bef87 PaintMinorIconAndText(canvas, style); // Set the submenu indicator (arrow) image and color. -@@ -1274,6 +1296,11 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const { +@@ -1244,6 +1263,11 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const { // Determine the length of the label text. int string_width = gfx::GetStringWidth(title_, style.font_list); @@ -422,7 +374,7 @@ index 0fcca1e59688eda967c59397ea932637810af27b..35e7e3dc8f18fffa1713d6821c2bef87 dimensions.standard_width = string_width + label_start + item_right_margin_; // Determine the length of the right-side text. -@@ -1281,9 +1308,10 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const { +@@ -1251,9 +1275,10 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const { minor_text.empty() ? 0 : gfx::GetStringWidth(minor_text, style.font_list); // Determine the height to use. @@ -437,10 +389,10 @@ index 0fcca1e59688eda967c59397ea932637810af27b..35e7e3dc8f18fffa1713d6821c2bef87 std::max(dimensions.height, MenuConfig::instance().item_min_height); diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h -index e129bed9de80df5a6aeaca8e22e7d12a949ab759..d7cfa2a5d93af7b865ab01f2cc004c4215fc9986 100644 +index c453320c3fbd8ad5581a78b0b63d7a06cda4d542..b92470f17446698515ae2e973e51628023d075de 100644 --- a/ui/views/controls/menu/menu_item_view.h +++ b/ui/views/controls/menu/menu_item_view.h -@@ -151,6 +151,7 @@ class VIEWS_EXPORT MenuItemView : public View { +@@ -152,6 +152,7 @@ class VIEWS_EXPORT MenuItemView : public View { MenuItemView* AddMenuItemAt(int index, int item_id, const base::string16& label, @@ -448,15 +400,15 @@ index e129bed9de80df5a6aeaca8e22e7d12a949ab759..d7cfa2a5d93af7b865ab01f2cc004c42 const base::string16& minor_text, const gfx::VectorIcon* minor_icon, const gfx::ImageSkia& icon, -@@ -214,6 +215,7 @@ class VIEWS_EXPORT MenuItemView : public View { +@@ -194,6 +195,7 @@ class VIEWS_EXPORT MenuItemView : public View { // All the AppendXXX methods funnel into this. MenuItemView* AppendMenuItemImpl(int item_id, const base::string16& label, + const base::string16& sublabel, - const base::string16& minor_text, - const gfx::VectorIcon* minor_icon, const gfx::ImageSkia& icon, -@@ -241,6 +243,9 @@ class VIEWS_EXPORT MenuItemView : public View { + Type type); + +@@ -218,6 +220,9 @@ class VIEWS_EXPORT MenuItemView : public View { void SetTitle(const base::string16& title); const base::string16& title() const { return title_; } @@ -466,7 +418,7 @@ index e129bed9de80df5a6aeaca8e22e7d12a949ab759..d7cfa2a5d93af7b865ab01f2cc004c42 // Sets the minor text. void SetMinorText(const base::string16& minor_text); -@@ -451,7 +456,7 @@ class VIEWS_EXPORT MenuItemView : public View { +@@ -428,7 +433,7 @@ class VIEWS_EXPORT MenuItemView : public View { void DestroyAllMenuHosts(); // Returns the text that should be displayed on the end (right) of the menu @@ -475,7 +427,7 @@ index e129bed9de80df5a6aeaca8e22e7d12a949ab759..d7cfa2a5d93af7b865ab01f2cc004c42 base::string16 GetMinorText() const; // Returns the icon that should be displayed to the left of the minor text. -@@ -542,6 +547,9 @@ class VIEWS_EXPORT MenuItemView : public View { +@@ -519,6 +524,9 @@ class VIEWS_EXPORT MenuItemView : public View { // Title. base::string16 title_; @@ -486,10 +438,10 @@ index e129bed9de80df5a6aeaca8e22e7d12a949ab759..d7cfa2a5d93af7b865ab01f2cc004c42 base::string16 minor_text_; diff --git a/ui/views/controls/menu/menu_item_view_unittest.cc b/ui/views/controls/menu/menu_item_view_unittest.cc -index f51bd9d85dad7771f18fc535b55b30a855eac48f..63aa8eddf51cb4821517902564e94813f6a42c02 100644 +index 27d189a5be20d001ac6c457f4540e5e5430dde37..5c749133714d40595ef804d56951eed35abee91e 100644 --- a/ui/views/controls/menu/menu_item_view_unittest.cc +++ b/ui/views/controls/menu/menu_item_view_unittest.cc -@@ -324,10 +324,10 @@ class MenuItemViewPaintUnitTest : public ViewsTestBase { +@@ -322,10 +322,10 @@ class MenuItemViewPaintUnitTest : public ViewsTestBase { // Provides assertion coverage for painting minor text and icons. TEST_F(MenuItemViewPaintUnitTest, MinorTextAndIconAssertionCoverage) { auto AddItem = [this](auto label, auto minor_label, auto minor_icon) { @@ -505,10 +457,10 @@ index f51bd9d85dad7771f18fc535b55b30a855eac48f..63aa8eddf51cb4821517902564e94813 AddItem("No minor content", base::string16(), nullptr); AddItem("Minor text only", base::ASCIIToUTF16("minor text"), nullptr); diff --git a/ui/views/controls/menu/menu_model_adapter.cc b/ui/views/controls/menu/menu_model_adapter.cc -index 09b72733e66d1e13182730e475b781ffefe649c0..e45e249f2a89e1bfd31cc82341a65341571ffc21 100644 +index beac9986b2c6d13cde9cb26310eefc01cfbbbe09..ab9ac14fcd346ce5490c916cf3eceb534510fb69 100644 --- a/ui/views/controls/menu/menu_model_adapter.cc +++ b/ui/views/controls/menu/menu_model_adapter.cc -@@ -96,8 +96,8 @@ MenuItemView* MenuModelAdapter::AddMenuItemFromModelAt(ui::MenuModel* model, +@@ -99,8 +99,8 @@ MenuItemView* MenuModelAdapter::AddMenuItemFromModelAt(ui::MenuModel* model, if (*type == MenuItemView::SEPARATOR) { return menu->AddMenuItemAt(menu_index, item_id, base::string16(), @@ -519,7 +471,7 @@ index 09b72733e66d1e13182730e475b781ffefe649c0..e45e249f2a89e1bfd31cc82341a65341 model->GetSeparatorTypeAt(model_index)); } -@@ -105,7 +105,8 @@ MenuItemView* MenuModelAdapter::AddMenuItemFromModelAt(ui::MenuModel* model, +@@ -108,7 +108,8 @@ MenuItemView* MenuModelAdapter::AddMenuItemFromModelAt(ui::MenuModel* model, model->GetIconAt(model_index, &icon); return menu->AddMenuItemAt( menu_index, item_id, model->GetLabelAt(model_index), diff --git a/patches/chromium/revert_remove_contentrendererclient_shouldfork.patch b/patches/chromium/revert_remove_contentrendererclient_shouldfork.patch new file mode 100644 index 0000000000000..d8fe0b7f1b505 --- /dev/null +++ b/patches/chromium/revert_remove_contentrendererclient_shouldfork.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: deepak1556 +Date: Wed, 23 Oct 2019 15:30:18 -0700 +Subject: Revert "Remove ContentRendererClient::ShouldFork." + +This reverts commit 6b068eb8ca4a3c7350bdafa22fc0cf0636ef8b74. + +diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc +index ffde51cd276a96ab46c5e47d825d26f5606f9a5e..4fdafb295478963247c9453ec0833c412110cffb 100644 +--- a/chrome/renderer/chrome_content_renderer_client.cc ++++ b/chrome/renderer/chrome_content_renderer_client.cc +@@ -1305,6 +1305,17 @@ bool ChromeContentRendererClient::ShouldFork(WebLocalFrame* frame, + return true; + #endif // BUILDFLAG(ENABLE_EXTENSIONS) + ++ DCHECK(!frame->Parent()); ++ ++ // If |url| matches one of the prerendered URLs, stop this navigation and try ++ // to swap in the prerendered page on the browser process. If the prerendered ++ // page no longer exists by the time the OpenURL IPC is handled, a normal ++ // navigation is attempted. ++ if (prerender_dispatcher_.get() && ++ prerender_dispatcher_->IsPrerenderURL(url)) { ++ return true; ++ } ++ + return false; + } + +diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc +index 2880c65106122954fd065ab48f9526d8358cf616..88ec67a1eebebdecedbb124e902fba4d4dfadde2 100644 +--- a/content/renderer/render_view_browsertest.cc ++++ b/content/renderer/render_view_browsertest.cc +@@ -989,6 +989,73 @@ TEST_F(RenderViewImplTest, BeginNavigationForWebUI) { + FrameHostMsg_OpenURL::ID)); + } + ++class AlwaysForkingRenderViewTest : public RenderViewImplTest { ++ public: ++ ContentRendererClient* CreateContentRendererClient() override { ++ return new TestContentRendererClient; ++ } ++ ++ private: ++ class TestContentRendererClient : public ContentRendererClient { ++ public: ++ bool ShouldFork(blink::WebLocalFrame* frame, ++ const GURL& url, ++ const std::string& http_method, ++ bool is_initial_navigation, ++ bool is_server_redirect) override { ++ return true; ++ } ++ }; ++}; ++ ++TEST_F(AlwaysForkingRenderViewTest, BeginNavigationDoesNotForkEmptyUrl) { ++ GURL example_url("http://example.com"); ++ GURL empty_url(""); ++ ++ LoadHTMLWithUrlOverride("GetWebFrame()->GetDocumentLoader()->GetUrl())); ++ ++ // Empty url should never fork. ++ blink::WebURLRequest request(empty_url); ++ request.SetMode(network::mojom::RequestMode::kNavigate); ++ request.SetRedirectMode(network::mojom::RedirectMode::kManual); ++ request.SetRequestContext(blink::mojom::RequestContextType::INTERNAL); ++ request.SetRequestorOrigin(blink::WebSecurityOrigin::Create(example_url)); ++ auto navigation_info = std::make_unique(); ++ navigation_info->url_request = request; ++ navigation_info->frame_type = ++ network::mojom::RequestContextFrameType::kTopLevel; ++ navigation_info->navigation_policy = blink::kWebNavigationPolicyCurrentTab; ++ frame()->BeginNavigation(std::move(navigation_info)); ++ EXPECT_FALSE(render_thread_->sink().GetUniqueMessageMatching( ++ FrameHostMsg_OpenURL::ID)); ++} ++ ++TEST_F(AlwaysForkingRenderViewTest, BeginNavigationDoesNotForkAboutBlank) { ++ GURL example_url("http://example.com"); ++ GURL blank_url(url::kAboutBlankURL); ++ ++ LoadHTMLWithUrlOverride("GetWebFrame()->GetDocumentLoader()->GetUrl())); ++ ++ // about:blank should never fork. ++ blink::WebURLRequest request(blank_url); ++ request.SetMode(network::mojom::RequestMode::kNavigate); ++ request.SetRedirectMode(network::mojom::RedirectMode::kManual); ++ request.SetRequestContext(blink::mojom::RequestContextType::INTERNAL); ++ request.SetRequestorOrigin(blink::WebSecurityOrigin::Create(example_url)); ++ auto navigation_info = std::make_unique(); ++ navigation_info->url_request = request; ++ navigation_info->frame_type = ++ network::mojom::RequestContextFrameType::kTopLevel; ++ navigation_info->navigation_policy = blink::kWebNavigationPolicyCurrentTab; ++ frame()->BeginNavigation(std::move(navigation_info)); ++ EXPECT_FALSE(render_thread_->sink().GetUniqueMessageMatching( ++ FrameHostMsg_OpenURL::ID)); ++} ++ + // This test verifies that when device emulation is enabled, RenderFrameProxy + // continues to receive the original ScreenInfo and not the emualted + // ScreenInfo. diff --git a/patches/chromium/scroll_bounce_flag.patch b/patches/chromium/scroll_bounce_flag.patch index 7fddae77b0bdb..963d1682898bb 100644 --- a/patches/chromium/scroll_bounce_flag.patch +++ b/patches/chromium/scroll_bounce_flag.patch @@ -6,10 +6,10 @@ Subject: scroll_bounce_flag.patch Patch to make scrollBounce option work. diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc -index 81aceae802b32bc21f639996cb197e62cf52af1d..8bd95d5053f7911074041a62581097c8eb2322aa 100644 +index 078c895fe5cd087bab485eec415bb78463df73c4..0cb978a91a457b20ae209d4cbbbd31070981ab6f 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc -@@ -1523,7 +1523,7 @@ bool RenderThreadImpl::IsGpuMemoryBufferCompositorResourcesEnabled() { +@@ -1330,7 +1330,7 @@ bool RenderThreadImpl::IsGpuMemoryBufferCompositorResourcesEnabled() { } bool RenderThreadImpl::IsElasticOverscrollEnabled() { diff --git a/patches/chromium/ssl_security_state_tab_helper.patch b/patches/chromium/ssl_security_state_tab_helper.patch index d8cc170bbbc68..d20eb9bc27b0c 100644 --- a/patches/chromium/ssl_security_state_tab_helper.patch +++ b/patches/chromium/ssl_security_state_tab_helper.patch @@ -6,19 +6,20 @@ Subject: ssl_security_state_tab_helper.patch Allows populating security tab info for devtools in Electron. diff --git a/chrome/browser/ssl/security_state_tab_helper.cc b/chrome/browser/ssl/security_state_tab_helper.cc -index e971e3506864865d1b05218d42fa5d54a72cc742..a1aa2036c405dbbc310b12ac6c956579cf1aa1d9 100644 +index 1b0605f8757c854da5b785b77dbcea219f8c30c7..0dd8e6917d3cb4ff04197491d305f7188c8703ba 100644 --- a/chrome/browser/ssl/security_state_tab_helper.cc +++ b/chrome/browser/ssl/security_state_tab_helper.cc -@@ -13,21 +13,25 @@ +@@ -13,22 +13,26 @@ #include "base/strings/pattern.h" #include "base/strings/string_util.h" #include "build/build_config.h" +#if 0 #include "chrome/browser/browser_process.h" - #include "chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h" #include "chrome/browser/profiles/profile.h" + #include "chrome/browser/reputation/reputation_web_contents_observer.h" #include "chrome/browser/safe_browsing/safe_browsing_service.h" #include "chrome/browser/safe_browsing/ui_manager.h" + #include "chrome/browser/ssl/known_interception_disclosure_infobar_delegate.h" +#endif #include "chrome/browser/ssl/tls_deprecation_config.h" #include "chrome/common/chrome_features.h" @@ -34,9 +35,9 @@ index e971e3506864865d1b05218d42fa5d54a72cc742..a1aa2036c405dbbc310b12ac6c956579 +#endif +#include "components/prefs/pref_service.h" #include "components/security_state/content/content_utils.h" + #include "components/security_state/core/security_state_pref_names.h" #include "content/public/browser/browser_context.h" - #include "content/public/browser/navigation_entry.h" -@@ -50,7 +54,7 @@ +@@ -52,7 +56,7 @@ #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h" #endif // defined(OS_CHROMEOS) @@ -45,7 +46,7 @@ index e971e3506864865d1b05218d42fa5d54a72cc742..a1aa2036c405dbbc310b12ac6c956579 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h" #endif -@@ -94,9 +98,12 @@ bool IsLegacyTLS(GURL url, int connection_status) { +@@ -96,9 +100,12 @@ bool IsLegacyTLS(GURL url, int connection_status) { // default we treat TLS < 1.2 as Legacy, unless the "SSLVersionMin" policy is // set. std::string ssl_version_min_str = switches::kSSLVersionTLSv12; @@ -61,7 +62,7 @@ index e971e3506864865d1b05218d42fa5d54a72cc742..a1aa2036c405dbbc310b12ac6c956579 } // Convert the pref string to an SSLVersion, if it is valid. Otherwise use the -@@ -113,8 +120,9 @@ bool IsLegacyTLS(GURL url, int connection_status) { +@@ -118,8 +125,9 @@ bool IsLegacyTLS(GURL url, int connection_status) { } // namespace @@ -72,23 +73,34 @@ index e971e3506864865d1b05218d42fa5d54a72cc742..a1aa2036c405dbbc310b12ac6c956579 SecurityStateTabHelper::SecurityStateTabHelper( content::WebContents* web_contents) -@@ -145,6 +153,7 @@ SecurityStateTabHelper::GetVisibleSecurityState() const { +@@ -163,6 +171,7 @@ SecurityStateTabHelper::GetVisibleSecurityState() { // information is still being initialized, thus no need to check for that. state->malicious_content_status = GetMaliciousContentStatus(); +#if 0 - safety_tips::ReputationWebContentsObserver* reputation_web_contents_observer = - safety_tips::ReputationWebContentsObserver::FromWebContents( - web_contents()); -@@ -153,6 +162,7 @@ SecurityStateTabHelper::GetVisibleSecurityState() const { - ? reputation_web_contents_observer - ->GetSafetyTipStatusForVisibleNavigation() - : security_state::SafetyTipStatus::kUnknown; + ReputationWebContentsObserver* reputation_web_contents_observer = + ReputationWebContentsObserver::FromWebContents(web_contents()); + state->safety_tip_info = +@@ -180,6 +189,7 @@ SecurityStateTabHelper::GetVisibleSecurityState() { + security_state::prefs::kStricterMixedContentTreatmentEnabled)) { + state->should_suppress_mixed_content_warning = true; + } +#endif + return state; } +@@ -230,8 +240,10 @@ void SecurityStateTabHelper::DidFinishNavigation( + UMA_HISTOGRAM_BOOLEAN("interstitial.ssl.visited_site_after_warning", true); + } + ++#if 0 + MaybeShowKnownInterceptionDisclosureDialog( + web_contents(), visible_security_state->cert_status); ++#endif + } -@@ -220,6 +230,7 @@ SecurityStateTabHelper::GetMaliciousContentStatus() const { + void SecurityStateTabHelper::DidChangeVisibleSecurityState() { +@@ -255,6 +267,7 @@ SecurityStateTabHelper::GetMaliciousContentStatus() const { web_contents()->GetController().GetVisibleEntry(); if (!entry) return security_state::MALICIOUS_CONTENT_STATUS_NONE; @@ -96,7 +108,7 @@ index e971e3506864865d1b05218d42fa5d54a72cc742..a1aa2036c405dbbc310b12ac6c956579 safe_browsing::SafeBrowsingService* sb_service = g_browser_process->safe_browsing_service(); if (!sb_service) -@@ -298,6 +309,7 @@ SecurityStateTabHelper::GetMaliciousContentStatus() const { +@@ -337,6 +350,7 @@ SecurityStateTabHelper::GetMaliciousContentStatus() const { break; } } diff --git a/patches/chromium/support_mixed_sandbox_with_zygote.patch b/patches/chromium/support_mixed_sandbox_with_zygote.patch index 002897bfd514f..541aa14509bb6 100644 --- a/patches/chromium/support_mixed_sandbox_with_zygote.patch +++ b/patches/chromium/support_mixed_sandbox_with_zygote.patch @@ -22,10 +22,10 @@ However, the patch would need to be reviewed by the security team, as it does touch a security-sensitive class. diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index 3e85adafd4e5173d5ea9680a28e41fd8f4efbdfd..0408a35db18ecdcad1521e4123aed4bd12bcf2c8 100644 +index 845e95c571391e04700415aa63439fb785212f0f..f942380764aef7e86a0de112e9b70aed960acade 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc -@@ -400,6 +400,11 @@ class RendererSandboxedProcessLauncherDelegate +@@ -415,6 +415,11 @@ class RendererSandboxedProcessLauncherDelegate { } @@ -37,7 +37,7 @@ index 3e85adafd4e5173d5ea9680a28e41fd8f4efbdfd..0408a35db18ecdcad1521e4123aed4bd ~RendererSandboxedProcessLauncherDelegate() override {} #if defined(OS_WIN) -@@ -421,6 +426,9 @@ class RendererSandboxedProcessLauncherDelegate +@@ -436,6 +441,9 @@ class RendererSandboxedProcessLauncherDelegate #if BUILDFLAG(USE_ZYGOTE_HANDLE) service_manager::ZygoteHandle GetZygote() override { @@ -47,7 +47,7 @@ index 3e85adafd4e5173d5ea9680a28e41fd8f4efbdfd..0408a35db18ecdcad1521e4123aed4bd const base::CommandLine& browser_command_line = *base::CommandLine::ForCurrentProcess(); base::CommandLine::StringType renderer_prefix = -@@ -435,10 +443,13 @@ class RendererSandboxedProcessLauncherDelegate +@@ -450,10 +458,13 @@ class RendererSandboxedProcessLauncherDelegate return service_manager::SANDBOX_TYPE_RENDERER; } @@ -62,7 +62,7 @@ index 3e85adafd4e5173d5ea9680a28e41fd8f4efbdfd..0408a35db18ecdcad1521e4123aed4bd }; const char kSessionStorageHolderKey[] = "kSessionStorageHolderKey"; -@@ -1734,11 +1745,18 @@ bool RenderProcessHostImpl::Init() { +@@ -1723,11 +1734,18 @@ bool RenderProcessHostImpl::Init() { cmd_line->PrependWrapper(renderer_prefix); AppendRendererCommandLine(cmd_line.get()); @@ -80,5 +80,5 @@ index 3e85adafd4e5173d5ea9680a28e41fd8f4efbdfd..0408a35db18ecdcad1521e4123aed4bd - std::make_unique(), + std::move(delegate), std::move(cmd_line), GetID(), this, std::move(mojo_invitation_), - base::BindRepeating(&RenderProcessHostImpl::OnMojoError, id_)); - channel_->Pause(); + base::BindRepeating(&RenderProcessHostImpl::OnMojoError, id_), + GetV8SnapshotFilesToPreload()); diff --git a/patches/chromium/ui_views_fix_jumbo_build.patch b/patches/chromium/ui_views_fix_jumbo_build.patch deleted file mode 100644 index 12904febe82e3..0000000000000 --- a/patches/chromium/ui_views_fix_jumbo_build.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Deepak Mohan -Date: Wed, 11 Sep 2019 16:09:34 -0700 -Subject: ui/views: fix namespace qualification with jumbo_build - -In file included from gen/ui/views/views_jumbo_4.cc:45: -.\../../ui/views/widget/widget_hwnd_utils.cc(112,11): error: no member named 'IsFormControlsRefreshEnabled' -in namespace 'views::features'; did you mean '::features::IsFormControlsRefreshEnabled'? - if (features::IsFormControlsRefreshEnabled() && - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ::features::IsFormControlsRefreshEnabled -../..\ui/base/ui_base_features.h(72,41): note: '::features::IsFormControlsRefreshEnabled' declared here -COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsFormControlsRefreshEnabled(); - ^ -1 error generated. -ninja: build stopped: subcommand failed. - -diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn -index b92fe5397a0f6223ef0c171755cc6e473c225b59..951ba8fedf20dd836adf48f70fa68f2f4128d88a 100644 ---- a/ui/views/BUILD.gn -+++ b/ui/views/BUILD.gn -@@ -592,6 +592,7 @@ jumbo_component("views") { - "win/scoped_enable_unadjusted_mouse_events_win.cc", - "win/scoped_fullscreen_visibility.cc", - ] -+ jumbo_excluded_sources = [ "widget/widget_hwnd_utils.cc" ] - libs = [ - "dwmapi.lib", - "imm32.lib", diff --git a/patches/chromium/unsandboxed_ppapi_processes_skip_zygote.patch b/patches/chromium/unsandboxed_ppapi_processes_skip_zygote.patch index 857e53e38ad77..f4a4cbf42106f 100644 --- a/patches/chromium/unsandboxed_ppapi_processes_skip_zygote.patch +++ b/patches/chromium/unsandboxed_ppapi_processes_skip_zygote.patch @@ -6,10 +6,10 @@ Subject: unsandboxed_ppapi_processes_skip_zygote.patch Unsandboxed ppapi processes should skip zygote. diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc -index 86c23036817bc69ed44331acb5257582a3c66ff9..f3e184daf801dacb6a1a57ace82d22734ba770c5 100644 +index 0489292bf356d858e7f048e561f794b9c68df87f..cbcb0b806406d4e136fd5ae6a785fd0ce1cc2425 100644 --- a/content/browser/ppapi_plugin_process_host.cc +++ b/content/browser/ppapi_plugin_process_host.cc -@@ -128,6 +128,9 @@ class PpapiPluginSandboxedProcessLauncherDelegate +@@ -123,6 +123,9 @@ class PpapiPluginSandboxedProcessLauncherDelegate service_manager::ZygoteHandle GetZygote() override { const base::CommandLine& browser_command_line = *base::CommandLine::ForCurrentProcess(); diff --git a/patches/chromium/use_keepselfalive_on_audiocontext_to_keep_it_alive_until_rendering.patch b/patches/chromium/use_keepselfalive_on_audiocontext_to_keep_it_alive_until_rendering.patch new file mode 100644 index 0000000000000..d658c7b056f9f --- /dev/null +++ b/patches/chromium/use_keepselfalive_on_audiocontext_to_keep_it_alive_until_rendering.patch @@ -0,0 +1,132 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Hongchan Choi +Date: Tue, 18 Feb 2020 22:39:05 +0000 +Subject: Use KeepSelfAlive on AudioContext to keep it alive until rendering + stops + +When an ExecutionContext is abruptly/unexpectedly destroyed (e.g. +shutting down of document or iframe), an AudioContext can also +go away. This type of shutdown can be problematic because the render +thread still might be touching resources in the AudioContext allocated +by the main thread. + +This CL introduces a self-referencing pointer to the AudioContext, +and it is cleared after the underlying render thread is stopped. In +that way, the destruction of AudioContext can be done safely. + +Test: Locally confirmed the repro case doesn't crash (UAP) after 1hr. +Bug: 1043446 +Change-Id: I2e40b7d58ca9d647eed8a5971fc69dc87ee3d1fe +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2049912 +Reviewed-by: Raymond Toy +Reviewed-by: Michael Lippautz +Commit-Queue: Hongchan Choi +Cr-Commit-Position: refs/heads/master@{#742338} + +diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc +index f544a4658f31632b85cea8f3f2939e3760b0dfb5..6618c8d74bdeaa8f470085956c30f5992497013a 100644 +--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc ++++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc +@@ -132,7 +132,8 @@ AudioContext::AudioContext(Document& document, + const WebAudioLatencyHint& latency_hint, + base::Optional sample_rate) + : BaseAudioContext(&document, kRealtimeContext), +- context_id_(g_context_id++) { ++ context_id_(g_context_id++), ++ keep_alive_(PERSISTENT_FROM_HERE, this) { + destination_node_ = + RealtimeAudioDestinationNode::Create(this, latency_hint, sample_rate); + +@@ -169,13 +170,14 @@ AudioContext::AudioContext(Document& document, + destination()->GetAudioDestinationHandler()); + base_latency_ = destination_handler.GetFramesPerBuffer() / + static_cast(sampleRate()); ++ + } + + void AudioContext::Uninitialize() { + DCHECK(IsMainThread()); + DCHECK_NE(g_hardware_context_count, 0u); + --g_hardware_context_count; +- ++ StopRendering(); + DidClose(); + RecordAutoplayMetrics(); + BaseAudioContext::Uninitialize(); +@@ -358,14 +360,26 @@ bool AudioContext::IsContextClosed() const { + return close_resolver_ || BaseAudioContext::IsContextClosed(); + } + ++void AudioContext::StartRendering() { ++ DCHECK(IsMainThread()); ++ ++ if (!keep_alive_) ++ keep_alive_ = this; ++ BaseAudioContext::StartRendering(); ++} ++ + void AudioContext::StopRendering() { + DCHECK(IsMainThread()); + DCHECK(destination()); + +- if (ContextState() == kRunning) { ++ // It is okay to perform the following on a suspended AudioContext because ++ // this method gets called from ExecutionContext::ContextDestroyed() meaning ++ // the AudioContext is already unreachable from the user code. ++ if (ContextState() != kClosed) { + destination()->GetAudioDestinationHandler().StopRendering(); + SetContextState(kClosed); + GetDeferredTaskHandler().ClearHandlersToBeDeleted(); ++ keep_alive_.Clear(); + } + } + +diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.h b/third_party/blink/renderer/modules/webaudio/audio_context.h +index 6e3455921f5a1b81fe8a43d44beecfbd9aa93dc1..d3e521f1291a2f90963199cadba02ae38400858e 100644 +--- a/third_party/blink/renderer/modules/webaudio/audio_context.h ++++ b/third_party/blink/renderer/modules/webaudio/audio_context.h +@@ -13,6 +13,7 @@ + #include "third_party/blink/renderer/modules/webaudio/audio_context_options.h" + #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h" + #include "third_party/blink/renderer/platform/heap/handle.h" ++#include "third_party/blink/renderer/platform/heap/self_keep_alive.h" + + namespace blink { + +@@ -133,8 +134,13 @@ class MODULES_EXPORT AudioContext : public BaseAudioContext { + // Record the current autoplay metrics. + void RecordAutoplayMetrics(); + ++ // Starts rendering via AudioDestinationNode. This sets the self-referencing ++ // pointer to this object. ++ void StartRendering() override; ++ + // Called when the context is being closed to stop rendering audio and clean +- // up handlers. ++ // up handlers. This clears the self-referencing pointer, making this object ++ // available for the potential GC. + void StopRendering(); + + // Called when suspending the context to stop reundering audio, but don't +@@ -196,6 +202,8 @@ class MODULES_EXPORT AudioContext : public BaseAudioContext { + // determine audibility on render quantum boundaries, so counting quanta is + // all that's needed. + size_t total_audible_renders_ = 0; ++ ++ SelfKeepAlive keep_alive_; + }; + + } // namespace blink +diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.h b/third_party/blink/renderer/modules/webaudio/base_audio_context.h +index d6e4fe5e25ffbaa5273a7aa6d4042cc474ba1c06..af0a27d0cb49faa34de540df5c25ae4f1d6f8734 100644 +--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.h ++++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.h +@@ -282,7 +282,7 @@ class MODULES_EXPORT BaseAudioContext + + DEFINE_ATTRIBUTE_EVENT_LISTENER(statechange, kStatechange) + +- void StartRendering(); ++ virtual void StartRendering(); + + void NotifyStateChange(); + diff --git a/patches/chromium/v8_context_snapshot_generator.patch b/patches/chromium/v8_context_snapshot_generator.patch index 8291f7b43449e..00f7d9dfdd8ba 100644 --- a/patches/chromium/v8_context_snapshot_generator.patch +++ b/patches/chromium/v8_context_snapshot_generator.patch @@ -7,10 +7,10 @@ v8_context_snapshot_generator is a build time executable. The patch adds the config. diff --git a/tools/v8_context_snapshot/BUILD.gn b/tools/v8_context_snapshot/BUILD.gn -index 2d7b357cfc2cd60e6e880a8c93b32166376540b5..002e9e67f8009e780182150705417c500a01c5cd 100644 +index 7cd1597a83dc2546ba960b09f7e1f2f7181a683e..fd0f83fc7ee30a7f7d939369a97ac66aad2883ab 100644 --- a/tools/v8_context_snapshot/BUILD.gn +++ b/tools/v8_context_snapshot/BUILD.gn -@@ -109,6 +109,7 @@ if (use_v8_context_snapshot) { +@@ -117,6 +117,7 @@ if (use_v8_context_snapshot) { configs += [ "//v8:external_startup_data", ":disable_icf", diff --git a/patches/chromium/web_contents.patch b/patches/chromium/web_contents.patch index 3aa605bbda0c8..b212b047d6837 100644 --- a/patches/chromium/web_contents.patch +++ b/patches/chromium/web_contents.patch @@ -5,10 +5,10 @@ Subject: web_contents.patch diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc -index 3eea51d39eb9239e0eb5d706497d84c8bb1b156f..8586f71224f1cae6a5e9b4005a6acfef9ff64d4d 100644 +index 08c2fda816c1c2c983849b5172946662515ed4d9..c2a579ed267f4081f44e9f2da2cfc90ae08bf750 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc -@@ -2135,6 +2135,12 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params) { +@@ -2068,6 +2068,12 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params) { std::string unique_name; frame_tree_.root()->SetFrameName(params.main_frame_name, unique_name); @@ -21,78 +21,16 @@ index 3eea51d39eb9239e0eb5d706497d84c8bb1b156f..8586f71224f1cae6a5e9b4005a6acfef WebContentsViewDelegate* delegate = GetContentClient()->browser()->GetWebContentsViewDelegate(this); -@@ -2150,6 +2156,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params) { - &render_view_host_delegate_view_); - } +@@ -2078,6 +2084,7 @@ void WebContentsImpl::Init(const WebContents::CreateParams& params) { + view_.reset(CreateWebContentsView(this, delegate, + &render_view_host_delegate_view_)); } + } // !view_ CHECK(render_view_host_delegate_view_); CHECK(view_.get()); -diff --git a/content/browser/web_contents/web_contents_view_guest.cc b/content/browser/web_contents/web_contents_view_guest.cc -index c45581fd22b7ba28b76bdebfffdc872b820798c8..f22ac0e611cc79aa1bc7f85836d69e3a8e326a6e 100644 ---- a/content/browser/web_contents/web_contents_view_guest.cc -+++ b/content/browser/web_contents/web_contents_view_guest.cc -@@ -68,19 +68,27 @@ gfx::NativeWindow WebContentsViewGuest::GetTopLevelNativeWindow() const { - - void WebContentsViewGuest::OnGuestAttached(WebContentsView* parent_view) { - #if defined(USE_AURA) -+ if (!platform_view_->GetNativeView()) -+ return; - // In aura, ScreenPositionClient doesn't work properly if we do - // not have the native view associated with this WebContentsViewGuest in the - // view hierarchy. We add this view as embedder's child here. - // This would go in WebContentsViewGuest::CreateView, but that is too early to - // access embedder_web_contents(). Therefore, we do it here. -- parent_view->GetNativeView()->AddChild(platform_view_->GetNativeView()); -+ if (parent_view->GetNativeView() != platform_view_->GetNativeView()) { -+ parent_view->GetNativeView()->AddChild(platform_view_->GetNativeView()); -+ } - #endif // defined(USE_AURA) - } - - void WebContentsViewGuest::OnGuestDetached(WebContentsView* old_parent_view) { - #if defined(USE_AURA) -- old_parent_view->GetNativeView()->RemoveChild( -- platform_view_->GetNativeView()); -+ if (!platform_view_->GetNativeView()) -+ return; -+ if (old_parent_view->GetNativeView() != platform_view_->GetNativeView()) { -+ old_parent_view->GetNativeView()->RemoveChild( -+ platform_view_->GetNativeView()); -+ } - #endif // defined(USE_AURA) - } - -@@ -130,11 +138,22 @@ RenderWidgetHostViewBase* WebContentsViewGuest::CreateViewForWidget( - render_widget_host->GetView()); - } - -+ RenderWidgetHost* embedder_render_widget_host = -+ guest_->embedder_web_contents()->GetRenderViewHost()->GetWidget(); -+ RenderWidgetHostViewBase* embedder_render_widget_host_view = -+ static_cast( -+ embedder_render_widget_host->GetView()); - RenderWidgetHostViewBase* platform_widget = -- platform_view_->CreateViewForWidget(render_widget_host, true); -- -- return RenderWidgetHostViewGuest::Create(render_widget_host, guest_, -- platform_widget->GetWeakPtr()); -+ embedder_render_widget_host_view->CreateViewForWidget( -+ render_widget_host, -+ embedder_render_widget_host, -+ platform_view_.get()); -+ RenderWidgetHostViewGuest* guest_view = RenderWidgetHostViewGuest::Create( -+ render_widget_host, guest_, platform_widget->GetWeakPtr()); -+ platform_widget->InitAsGuest(embedder_render_widget_host->GetView(), -+ guest_view); -+ -+ return guest_view; - } - - RenderWidgetHostViewBase* WebContentsViewGuest::CreateViewForChildWidget( diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h -index 6708e254b314efc2366b86e0dcd5b312ec77038e..94a5b87ca72998729f1fca14f247914bb81d60d1 100644 +index ecee31b744ed0871a9f1052d5419075582a90baa..0ce547c74610e13f7f723da997fb0a9799c96efb 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h @@ -79,9 +79,12 @@ class BrowserPluginGuestDelegate; diff --git a/patches/chromium/webview_cross_drag.patch b/patches/chromium/webview_cross_drag.patch index b7d18dcf26c50..0977987b54966 100644 --- a/patches/chromium/webview_cross_drag.patch +++ b/patches/chromium/webview_cross_drag.patch @@ -5,10 +5,10 @@ Subject: webview_cross_drag.patch diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc -index fbefaf207527d4e9be49f7eb455d8c1461dea409..c5d81b68fba3b0deef5018a499728595bf459dcd 100644 +index 75972bb7f6ccfe5cf7d1bd78e154f7835312fda1..5368aafddd91cc75075700c8f93e3d7ea8f6435b 100644 --- a/content/browser/web_contents/web_contents_view_aura.cc +++ b/content/browser/web_contents/web_contents_view_aura.cc -@@ -800,6 +800,7 @@ gfx::NativeView WebContentsViewAura::GetRenderWidgetHostViewParent() const { +@@ -796,6 +796,7 @@ gfx::NativeView WebContentsViewAura::GetRenderWidgetHostViewParent() const { bool WebContentsViewAura::IsValidDragTarget( RenderWidgetHostImpl* target_rwh) const { @@ -20,7 +20,7 @@ diff --git a/content/browser/web_contents/web_drag_dest_mac.mm b/content/browser index c4638ac6e86d6a816848cdbbdfa3e70d45086d90..18bd2fb8171568745549d490ee93887418890157 100644 --- a/content/browser/web_contents/web_drag_dest_mac.mm +++ b/content/browser/web_contents/web_drag_dest_mac.mm -@@ -336,6 +336,7 @@ - (void)setDragStartTrackersForProcess:(int)processID { +@@ -336,6 +336,7 @@ content::GlobalRoutingID GetRenderViewHostID(content::RenderViewHost* rvh) { } - (bool)isValidDragTarget:(content::RenderWidgetHostImpl*)targetRWH { diff --git a/patches/chromium/when_suspending_context_don_t_clear_handlers.patch b/patches/chromium/when_suspending_context_don_t_clear_handlers.patch new file mode 100644 index 0000000000000..371f73e68ff9b --- /dev/null +++ b/patches/chromium/when_suspending_context_don_t_clear_handlers.patch @@ -0,0 +1,80 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Raymond Toy +Date: Wed, 11 Dec 2019 00:33:00 +0000 +Subject: When suspending context, don't clear handlers + +AudioContext.suspend() would call StopRendering(). This stops the audio +thread from pulling the graph (eventually) but it also clears out any +handlers, including those associated with automatic pull nodes for any +AnalyserNode that isn't connected to the destination. When the context +is resumed, the AnalyserNode isn't pulled anymore, so the output never +changes. + +Add a SuspendRendering() method to handle AudioContext.suspend() which +doesn't clear the handlers. Then when the context is resumed, +AnalyserNodes will get pulled again. Then StopRendering() is used only +for AudioContext.close() where it is ok to clear out the handlers since +we can't resume a closed context. + +Bug: 1018499 +Change-Id: I4b4ccf688b37e6b81d310d2596cfff9603048876 +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1903894 +Reviewed-by: Hongchan Choi +Commit-Queue: Raymond Toy +Cr-Commit-Position: refs/heads/master@{#723609} + +diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc +index 063475274c1686ddef524fc48f7d73e6987e12ac..f544a4658f31632b85cea8f3f2939e3760b0dfb5 100644 +--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc ++++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc +@@ -214,7 +214,7 @@ ScriptPromise AudioContext::suspendContext(ScriptState* script_state) { + + // Stop rendering now. + if (destination()) +- StopRendering(); ++ SuspendRendering(); + + // Since we don't have any way of knowing when the hardware actually stops, + // we'll just resolve the promise now. +@@ -364,11 +364,21 @@ void AudioContext::StopRendering() { + + if (ContextState() == kRunning) { + destination()->GetAudioDestinationHandler().StopRendering(); +- SetContextState(kSuspended); ++ SetContextState(kClosed); + GetDeferredTaskHandler().ClearHandlersToBeDeleted(); + } + } + ++void AudioContext::SuspendRendering() { ++ DCHECK(IsMainThread()); ++ DCHECK(destination()); ++ ++ if (ContextState() == kRunning) { ++ destination()->GetAudioDestinationHandler().StopRendering(); ++ SetContextState(kSuspended); ++ } ++} ++ + double AudioContext::baseLatency() const { + DCHECK(IsMainThread()); + DCHECK(destination()); +diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.h b/third_party/blink/renderer/modules/webaudio/audio_context.h +index 013eee567252753863de1ecaa1664e8051941f8c..6e3455921f5a1b81fe8a43d44beecfbd9aa93dc1 100644 +--- a/third_party/blink/renderer/modules/webaudio/audio_context.h ++++ b/third_party/blink/renderer/modules/webaudio/audio_context.h +@@ -133,8 +133,14 @@ class MODULES_EXPORT AudioContext : public BaseAudioContext { + // Record the current autoplay metrics. + void RecordAutoplayMetrics(); + ++ // Called when the context is being closed to stop rendering audio and clean ++ // up handlers. + void StopRendering(); + ++ // Called when suspending the context to stop reundering audio, but don't ++ // clean up handlers because we expect to be resuming where we left off. ++ void SuspendRendering(); ++ + void DidClose(); + + // Called by the audio thread to handle Promises for resume() and suspend(), diff --git a/patches/chromium/worker_context_will_destroy.patch b/patches/chromium/worker_context_will_destroy.patch index 939a409828bc8..19e803b1f24a5 100644 --- a/patches/chromium/worker_context_will_destroy.patch +++ b/patches/chromium/worker_context_will_destroy.patch @@ -5,10 +5,10 @@ Subject: worker_context_will_destroy.patch diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h -index 902bd023e6d2e26a5a89ed441651edd5f364f009..906920393c34043d19183064a2a4506c0a03e80d 100644 +index 6002ce70bb173f441f269ea2dc6e61716c424196..2c372d68dace9c546eae3bdbd4994ec261a6fa22 100644 --- a/content/public/renderer/content_renderer_client.h +++ b/content/public/renderer/content_renderer_client.h -@@ -383,6 +383,11 @@ class CONTENT_EXPORT ContentRendererClient { +@@ -396,6 +396,11 @@ class CONTENT_EXPORT ContentRendererClient { virtual void DidInitializeWorkerContextOnWorkerThread( v8::Local context) {} @@ -21,10 +21,10 @@ index 902bd023e6d2e26a5a89ed441651edd5f364f009..906920393c34043d19183064a2a4506c // An empty URL is returned if the URL is not overriden. virtual GURL OverrideFlashEmbedWithHTML(const GURL& url); diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc -index 875a9079d0d5e97d04ca9870cb1d35dbe39fb24b..08cdba51792cbc73eb55ca7bb1d0ca1711ca97e8 100644 +index 17e84479f922cc0eca4ace413a1aea5fb87cb41c..a1fd4ca3d56476246c1a560bfbb7299f1134abf3 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc -@@ -994,6 +994,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { +@@ -883,6 +883,12 @@ void RendererBlinkPlatformImpl::WillStopWorkerThread() { WorkerThreadRegistry::Instance()->WillStopCurrentWorkerThread(); } @@ -38,10 +38,10 @@ index 875a9079d0d5e97d04ca9870cb1d35dbe39fb24b..08cdba51792cbc73eb55ca7bb1d0ca17 const v8::Local& worker) { GetContentClient()->renderer()->DidInitializeWorkerContextOnWorkerThread( diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h -index b183977096477563043b288b33d90d3ce9fbd8de..87bd2530eb060c8e68a5a2ca090ce80cd63b942e 100644 +index edbf232175e4555d4f810b6ef03c74f793e32bdf..1995663c3ee97c51a81de076c9a7fe05ba0e73fc 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h -@@ -200,6 +200,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { +@@ -181,6 +181,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { void DidStartWorkerThread() override; void WillStopWorkerThread() override; void WorkerContextCreated(const v8::Local& worker) override; @@ -50,10 +50,10 @@ index b183977096477563043b288b33d90d3ce9fbd8de..87bd2530eb060c8e68a5a2ca090ce80c const blink::WebString& header_name) override; diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h -index 83b3a264c0731acb567401ce64085560470a457f..70cab2fd7d3f5a30683ad2c8da7dcd453f939502 100644 +index 915af795cbf3a1001e6deb572cf93913775237fe..e9f082fbe34022b165aeca1a37fc0f0fe5e6024a 100644 --- a/third_party/blink/public/platform/platform.h +++ b/third_party/blink/public/platform/platform.h -@@ -692,6 +692,7 @@ class BLINK_PLATFORM_EXPORT Platform { +@@ -623,6 +623,7 @@ class BLINK_PLATFORM_EXPORT Platform { virtual void DidStartWorkerThread() {} virtual void WillStopWorkerThread() {} virtual void WorkerContextCreated(const v8::Local& worker) {} @@ -62,10 +62,10 @@ index 83b3a264c0731acb567401ce64085560470a457f..70cab2fd7d3f5a30683ad2c8da7dcd45 const WebSecurityOrigin& script_origin) { return false; diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc -index d16b0ea72e4d0c0371bbb145c4f4c26b4d5c19e2..bd4af3ae2df62968aaf9001db8eda006a96d307b 100644 +index e579547da621956c0dcee3c838318a8a9098ae9c..c1702a4d4e1649ffff611e840a217b81474d6e9c 100644 --- a/third_party/blink/renderer/core/workers/worker_thread.cc +++ b/third_party/blink/renderer/core/workers/worker_thread.cc -@@ -680,6 +680,12 @@ void WorkerThread::PrepareForShutdownOnWorkerThread() { +@@ -681,6 +681,12 @@ void WorkerThread::PrepareForShutdownOnWorkerThread() { nested_runner_->QuitNow(); } diff --git a/patches/config.json b/patches/config.json index b01321a75f1f6..8253840524853 100644 --- a/patches/config.json +++ b/patches/config.json @@ -1,11 +1,13 @@ { "src/electron/patches/chromium": "src", - "src/electron/patches/quiche": "src/net/third_party/quiche/src", + "src/electron/patches/angle": "src/third_party/angle", "src/electron/patches/boringssl": "src/third_party/boringssl/src", "src/electron/patches/v8": "src/v8", - "src/electron/patches/node": "src/third_party/electron_node" + "src/electron/patches/node": "src/third_party/electron_node", + + "src/electron/patches/usrsctp": "src/third_party/usrsctp/usrsctplib" } diff --git a/patches/node/.patches b/patches/node/.patches index b5771492e6ab0..9b81972e10a16 100644 --- a/patches/node/.patches +++ b/patches/node/.patches @@ -25,7 +25,6 @@ src_disable_node_use_v8_platform_in_node_options.patch build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch refactor_allow_embedder_overriding_of_internal_fs_calls.patch chore_prevent_warn_non_context-aware_native_modules_being_loaded.patch -chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch inherit_electron_crashpad_pipe_name_in_child_process.patch fixme_revert_crypto_add_support_for_rsa-pss_keys.patch fix_extern_the_nativemoduleenv_and_options_parser_for_debug_builds.patch @@ -41,3 +40,4 @@ fix_windows_compilation_on_libuv_setsockopt.patch fix_don_t_use_node-controlled_preparestacktrace.patch fix_remove_uses_of_node_use_v8_platform.patch fix_call_initializecontextruntime_in_initializecontext.patch +refactor_transferrablemodule_is_deprecated_use_compiledwasmmodule.patch diff --git a/patches/node/build_add_gn_build_files.patch b/patches/node/build_add_gn_build_files.patch index 246a76cebcee6..1b0a5fd448df5 100644 --- a/patches/node/build_add_gn_build_files.patch +++ b/patches/node/build_add_gn_build_files.patch @@ -6,7 +6,7 @@ Subject: build: add GN build files diff --git a/BUILD.gn b/BUILD.gn new file mode 100644 -index 0000000000000000000000000000000000000000..5bc9ff072414a4c3bae896a08b9771984944f087 +index 0000000000000000000000000000000000000000..ec06e14dd327cdf89dc6fd584b6972ae64311ea0 --- /dev/null +++ b/BUILD.gn @@ -0,0 +1,370 @@ @@ -227,7 +227,7 @@ index 0000000000000000000000000000000000000000..5bc9ff072414a4c3bae896a08b977198 + ] + public_deps = [ + "deps/uv", -+ "//electron:atom_js2c", ++ "//electron:electron_js2c", + "//v8", + ] + configs += [ ":node_internal_config" ] @@ -252,7 +252,7 @@ index 0000000000000000000000000000000000000000..5bc9ff072414a4c3bae896a08b977198 + + sources = node_files.node_sources + sources += [ -+ "$root_gen_dir/atom_natives.cc", ++ "$root_gen_dir/electron_natives.cc", + "$target_gen_dir/node_javascript.cc", + "src/node_code_cache_stub.cc", + "src/node_snapshot_stub.cc", @@ -1539,7 +1539,7 @@ index 0000000000000000000000000000000000000000..f3c5c798c0aefcb8cf9b1570a7b4817c + args = rebase_path(inputs + outputs, root_build_dir) +} diff --git a/src/node_version.h b/src/node_version.h -index a61a186edd3c92d6f0bd760a9990b5c8ed57e6fa..e4963fdfd9b93a6f50a4f1035f63e95432dd8d8e 100644 +index b974af56853d28d2154f314f5ca9a38d19efc84c..57c846c81d511051cc56d3827609b12993e70240 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -89,7 +89,10 @@ diff --git a/patches/node/build_bring_back_node_with_ltcg_configuration.patch b/patches/node/build_bring_back_node_with_ltcg_configuration.patch index 24fb0cd6e28d0..ff48f6394a2f7 100644 --- a/patches/node/build_bring_back_node_with_ltcg_configuration.patch +++ b/patches/node/build_bring_back_node_with_ltcg_configuration.patch @@ -10,10 +10,19 @@ THe fix for this should land in node-gyp as discussed in above issue, landing this as temporary patch. diff --git a/common.gypi b/common.gypi -index f07e65f719a1a5939997dfcae7bc787ee6391f4d..69b5439a5c19230e5568450c3aca9ce27661d77c 100644 +index bde7d7300f44596abe5cdfac0639ecb1bb4d885f..412f613e7cfcf563fa6a000b932723166ab567da 100644 --- a/common.gypi +++ b/common.gypi -@@ -180,6 +180,26 @@ +@@ -19,7 +19,7 @@ + 'node_use_v8_platform%': 'true', + 'node_use_bundled_v8%': 'true', + 'node_module_version%': '', +- 'node_with_ltcg%': '', ++ 'node_with_ltcg%': 'true', + 'node_shared_openssl%': 'false', + + 'node_tag%': '', +@@ -240,6 +240,26 @@ 'cflags': [ '-fPIE' ], 'ldflags': [ '-fPIE', '-pie' ] }], diff --git a/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch b/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch index 1360dac788fee..a977d06e2ea2c 100644 --- a/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch +++ b/patches/node/build_modify_js2c_py_to_allow_injection_of_original-fs_and_custom_embedder_js.patch @@ -38,7 +38,7 @@ index fabaea75686161f488a03349e07049a513b98fad..5a6b01dc12fb77d5f8c26a1153ead2a1 bool Exists(const char* id); diff --git a/tools/js2c.py b/tools/js2c.py -index 7b3e90af88b873c32f8ed51712f7dfdff3c1c706..586d0f72dcb4215ce73c23b401bce7238ed5bb0d 100755 +index 752344d68c3f63b4c5e491b33d4576ed48f8b74f..a6f0805048e3c3f4dd81ce6e90b684c48e52b67c 100755 --- a/tools/js2c.py +++ b/tools/js2c.py @@ -187,13 +187,15 @@ namespace native_module {{ @@ -79,7 +79,7 @@ index 7b3e90af88b873c32f8ed51712f7dfdff3c1c706..586d0f72dcb4215ce73c23b401bce723 if split[0] == 'deps': split = ['internal'] + split else: # `lib/**/*.js` so drop the 'lib' part -@@ -273,9 +275,9 @@ def NormalizeFileName(filename): +@@ -274,9 +276,9 @@ def NormalizeFileName(filename): return os.path.splitext(filename)[0] @@ -91,7 +91,7 @@ index 7b3e90af88b873c32f8ed51712f7dfdff3c1c706..586d0f72dcb4215ce73c23b401bce723 # Build source code lines definitions = [] -@@ -283,14 +285,26 @@ def JS2C(source_files, target): +@@ -284,14 +286,26 @@ def JS2C(source_files, target): for filename in source_files['.js']: AddModule(filename, consts, macros, definitions, initializers) @@ -122,7 +122,7 @@ index 7b3e90af88b873c32f8ed51712f7dfdff3c1c706..586d0f72dcb4215ce73c23b401bce723 write_if_chaged(out, target) -@@ -343,17 +357,21 @@ def main(): +@@ -344,17 +358,21 @@ def main(): ) parser.add_argument('--target', help='output file') parser.add_argument('--verbose', action='store_true', help='output file') diff --git a/patches/node/call_process_log_from_fallback_stream_on_windows.patch b/patches/node/call_process_log_from_fallback_stream_on_windows.patch index 8cbfe2f16c480..1ca5519a9d0b7 100644 --- a/patches/node/call_process_log_from_fallback_stream_on_windows.patch +++ b/patches/node/call_process_log_from_fallback_stream_on_windows.patch @@ -6,7 +6,7 @@ Subject: Call process.log from fallback stream on Windows (cherry picked from commit d31e629b4f2daf3500a485caab2b2990a41e3ad4) diff --git a/lib/internal/process/stdio.js b/lib/internal/process/stdio.js -index 61892165999d92704e49d0748c5e1d3d4d9582b7..0ea488c944b8fc8e21064476180c2b417c6e1125 100644 +index 08781547c489526834609861c5a906ef5183936b..e60ad6b0d4c634a8b0990da6ca090ea48715b830 100644 --- a/lib/internal/process/stdio.js +++ b/lib/internal/process/stdio.js @@ -192,6 +192,11 @@ function createWritableStdioStream(fd) { diff --git a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch b/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch deleted file mode 100644 index 600daed90ad7d..0000000000000 --- a/patches/node/chore_allow_the_node_entrypoint_to_be_a_builtin_module.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Samuel Attard -Date: Mon, 3 Jun 2019 01:05:58 -0700 -Subject: chore: allow the node entrypoint to be a builtin module - -This floats two patches onto the node 12 branch that I don't think we can upstream. - -The default behavior of node is to `path.resolve(firstArg)` to figure out what JS file to load. Issue here is that we use that for `browser/init.js` which now doesn't exist on disk. This adds an exception that won't affect user code to allow node to boot-up internal modules (in this case anything in the `electron/js2c` scope. - -Similar to the above, the loader uses `process.argv[1]` to figure out when to break for `--inspect-brk` this updates the logic to use an Electron provided `process._firstFileName` - -diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js -index 072e614fca05197fb2b259914711033981f6e2f2..dd7b6496e23efe0b73c6c9919c3780abeecdcec3 100644 ---- a/lib/internal/bootstrap/pre_execution.js -+++ b/lib/internal/bootstrap/pre_execution.js -@@ -71,8 +71,10 @@ function patchProcessObject(expandArgv1) { - - if (expandArgv1 && process.argv[1] && !process.argv[1].startsWith('-')) { - // Expand process.argv[1] into a full path. -+ if (!process.argv[1] || !process.argv[1].startsWith('electron/js2c')) { - const path = require('path'); - process.argv[1] = path.resolve(process.argv[1]); -+ } - } - - // TODO(joyeecheung): most of these should be deprecated and removed, -diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js -index 2a7ffbff213f23536b94664c3ecffa18eb812849..6e73d0b57a11620b1fcec8b9b716b66673438864 100644 ---- a/lib/internal/modules/cjs/loader.js -+++ b/lib/internal/modules/cjs/loader.js -@@ -845,6 +845,13 @@ Module.prototype._compile = function(content, filename) { - if (getOptionValue('--inspect-brk') && process._eval == null) { - if (!resolvedArgv) { - // We enter the repl if we're not given a filename argument. -+ // process._firstFileName is used by Embedders to tell node what -+ // the first "real" file is when they use themselves as the entry -+ // point -+ if (process._firstFileName) { -+ resolvedArgv = process._firstFileName -+ delete process._firstFileName -+ } else - if (process.argv[1]) { - resolvedArgv = Module._resolveFilename(process.argv[1], null, false); - } else { diff --git a/patches/node/chore_handle_default_configuration_not_being_set_in_the_electron_env.patch b/patches/node/chore_handle_default_configuration_not_being_set_in_the_electron_env.patch index a18c6655391c6..bfd6a01b13a7c 100644 --- a/patches/node/chore_handle_default_configuration_not_being_set_in_the_electron_env.patch +++ b/patches/node/chore_handle_default_configuration_not_being_set_in_the_electron_env.patch @@ -5,7 +5,7 @@ Subject: chore: handle default_configuration not being set in the electron env diff --git a/test/common/index.js b/test/common/index.js -index 13604d06e14a3612e4d161ebbdda10912a46d3b0..0f1398ed665b66d4fe7778a33ceab5176c086ff4 100644 +index 00ebd283a0c3e960de2a7c4e3748561c499eb820..2ebec24f9e1f53306882e827a17925201cad0ea6 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -118,7 +118,7 @@ const enoughTestCpu = Array.isArray(cpus) && diff --git a/patches/node/chore_prevent_warn_non_context-aware_native_modules_being_loaded.patch b/patches/node/chore_prevent_warn_non_context-aware_native_modules_being_loaded.patch index db19474c2c89c..8e9e215d3c2f5 100644 --- a/patches/node/chore_prevent_warn_non_context-aware_native_modules_being_loaded.patch +++ b/patches/node/chore_prevent_warn_non_context-aware_native_modules_being_loaded.patch @@ -7,11 +7,44 @@ This should be updated to take advantage of https://github.com/nodejs/node/pull/ once we stop warning and begin to unilaterally prevent non-context aware modules from being loaded. +diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js +index 113d2e58a9cecf233bf6a60138cdfc60f338b28c..f4e21997054dc86e21ed093a7bc227b0c5d150cd 100644 +--- a/lib/internal/bootstrap/pre_execution.js ++++ b/lib/internal/bootstrap/pre_execution.js +@@ -80,8 +80,10 @@ function patchProcessObject(expandArgv1) { + + if (expandArgv1 && process.argv[1] && !process.argv[1].startsWith('-')) { + // Expand process.argv[1] into a full path. ++ if (!process.argv[1] || !process.argv[1].startsWith('electron/js2c')) { + const path = require('path'); + process.argv[1] = path.resolve(process.argv[1]); ++ } + } + + // TODO(joyeecheung): most of these should be deprecated and removed, +diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js +index 18fdf148ccca5d92f10b78144eee39bdff7919d2..df995ecc75a0c7cf509d2f4d75051c68cd507d5a 100644 +--- a/lib/internal/modules/cjs/loader.js ++++ b/lib/internal/modules/cjs/loader.js +@@ -933,6 +933,13 @@ Module.prototype._compile = function(content, filename) { + if (getOptionValue('--inspect-brk') && process._eval == null) { + if (!resolvedArgv) { + // We enter the repl if we're not given a filename argument. ++ // process._firstFileName is used by Embedders to tell node what ++ // the first "real" file is when they use themselves as the entry ++ // point ++ if (process._firstFileName) { ++ resolvedArgv = process._firstFileName ++ delete process._firstFileName ++ } else + if (process.argv[1]) { + resolvedArgv = Module._resolveFilename(process.argv[1], null, false); + } else { diff --git a/src/env.h b/src/env.h -index 2dd6bdb75f02e2ccb07129996d16df99c0c4ccf2..ecb845803776f5ff6e7eec712ec7a5ebea2e8d0a 100644 +index 6240aaf1a078a26b37adfe90023651559582f92d..b83eee0dc5b791748643c4918baf0b4db1efa75c 100644 --- a/src/env.h +++ b/src/env.h -@@ -884,6 +884,15 @@ class Environment : public MemoryRetainer { +@@ -912,6 +912,15 @@ class Environment : public MemoryRetainer { uint64_t thread_id = kNoThreadId); ~Environment(); @@ -27,7 +60,7 @@ index 2dd6bdb75f02e2ccb07129996d16df99c0c4ccf2..ecb845803776f5ff6e7eec712ec7a5eb void InitializeLibuv(bool start_profiler_idle_notifier); inline const std::vector& exec_argv(); inline const std::vector& argv(); -@@ -1234,6 +1243,9 @@ class Environment : public MemoryRetainer { +@@ -1266,6 +1275,9 @@ class Environment : public MemoryRetainer { inline void ThrowError(v8::Local (*fun)(v8::Local), const char* errmsg); @@ -38,10 +71,10 @@ index 2dd6bdb75f02e2ccb07129996d16df99c0c4ccf2..ecb845803776f5ff6e7eec712ec7a5eb v8::Isolate* const isolate_; IsolateData* const isolate_data_; diff --git a/src/node_binding.cc b/src/node_binding.cc -index c51a892e1bcd3df6ea8af952c8200975c7f4ac7e..9a4a7780f4ac1bcc0855cc9e3eed904d5a0b4254 100644 +index 3a5ea646fc68d88cb045380c3401812dff590496..875ec8ce2831730e5041a3057326080c5917f932 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc -@@ -2,6 +2,7 @@ +@@ -3,6 +3,7 @@ #include #include "env-inl.h" #include "node_native_module_env.h" @@ -49,7 +82,7 @@ index c51a892e1bcd3df6ea8af952c8200975c7f4ac7e..9a4a7780f4ac1bcc0855cc9e3eed904d #include "util.h" #if HAVE_OPENSSL -@@ -466,10 +466,12 @@ void DLOpen(const FunctionCallbackInfo& args) { +@@ -466,10 +467,22 @@ void DLOpen(const FunctionCallbackInfo& args) { if (mp != nullptr) { if (mp->nm_context_register_func == nullptr) { @@ -57,11 +90,20 @@ index c51a892e1bcd3df6ea8af952c8200975c7f4ac7e..9a4a7780f4ac1bcc0855cc9e3eed904d + if (env->force_context_aware()) { dlib->Close(); - THROW_ERR_NON_CONTEXT_AWARE_DISABLED(env); -+ env->ThrowError("Loading non context-aware native modules has been disabled in this process. This means you have loaded a non context-aware native module with app.allowRendererProcessReuse set to true. See https://github.com/electron/electron/issues/18397 for more information"); ++ char errmsg[1024]; ++ snprintf(errmsg, ++ sizeof(errmsg), ++ "Loading non-context-aware native module in renderer: '%s', but app.allowRendererProcessReuse is true. See https://github.com/electron/electron/issues/18397.", ++ *filename); ++ env->ThrowError(errmsg); return false; + } else if (env->warn_non_context_aware()) { -+ ProcessEmitWarningGeneric(env, "Loading non context-aware native modules in the renderer process is deprecated and will stop working at some point in the future, please see https://github.com/electron/electron/issues/18397 for more information", "Electron"); ++ char errmsg[1024]; ++ snprintf(errmsg, ++ sizeof(errmsg), ++ "Loading non-context-aware native module in renderer: '%s'. This is deprecated, see https://github.com/electron/electron/issues/18397.", ++ *filename); ++ ProcessEmitWarningGeneric(env, errmsg, "Electron"); } } mp->nm_dso_handle = dlib->handle_; - \ No newline at end of file diff --git a/patches/node/chore_read_nobrowserglobals_from_global_not_process.patch b/patches/node/chore_read_nobrowserglobals_from_global_not_process.patch index c00ef82b54524..749911e0d8a6b 100644 --- a/patches/node/chore_read_nobrowserglobals_from_global_not_process.patch +++ b/patches/node/chore_read_nobrowserglobals_from_global_not_process.patch @@ -7,10 +7,10 @@ This is used so that we can modify the flag at runtime where config can only be set at compile time. diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js -index 48a38a6b5054c6bf14df82a8cb3c41d4bfb7bf68..a68e91171928ac3248a097bd6c58c9ae8c350f1a 100644 +index 0f36697ce5ed0b5bb6df74ceca219574da7eea53..a7fc05a247bfcd2b6da048c9def3b78025bd43fe 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js -@@ -169,7 +169,7 @@ const { +@@ -167,7 +167,7 @@ const { queueMicrotask } = require('internal/process/task_queues'); diff --git a/patches/node/chore_split_createenvironment_into_createenvironment_and.patch b/patches/node/chore_split_createenvironment_into_createenvironment_and.patch index efe5d335e29e5..74063c09831cd 100644 --- a/patches/node/chore_split_createenvironment_into_createenvironment_and.patch +++ b/patches/node/chore_split_createenvironment_into_createenvironment_and.patch @@ -8,7 +8,7 @@ This allows us to run operations on a created but not yet bootstrapped environment such as setting up an InspectorAgent diff --git a/src/api/environment.cc b/src/api/environment.cc -index 443f49320b2e6720f932fcfcefdbe6e539594964..4d79af9ec2a7b476c5f86f3882f4fb63afafc53e 100644 +index ae26cb7e9ef55fc0b965e28de4686aec87f42522..50886f4a998f1e7f346a6b7fad91ce49c3a7cdff 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -263,7 +263,8 @@ Environment* CreateEnvironment(IsolateData* isolate_data, @@ -53,10 +53,10 @@ index 443f49320b2e6720f932fcfcefdbe6e539594964..4d79af9ec2a7b476c5f86f3882f4fb63 void FreeEnvironment(Environment* env) { diff --git a/src/node.h b/src/node.h -index c51fb1a6a6e497a091e2ba0b147e3d7b6a4d685c..211f00cca65eeba317a03af36411a19a6befae18 100644 +index 80a27dc734a81a7ca8d888d1d55fc8d24a536280..9c6dcbf7014f7cf87f7f66886cbf255978c244fa 100644 --- a/src/node.h +++ b/src/node.h -@@ -330,7 +330,9 @@ NODE_EXTERN Environment* CreateEnvironment(IsolateData* isolate_data, +@@ -326,7 +326,9 @@ NODE_EXTERN Environment* CreateEnvironment(IsolateData* isolate_data, int argc, const char* const* argv, int exec_argc, diff --git a/patches/node/export_environment_knodecontexttagptr.patch b/patches/node/export_environment_knodecontexttagptr.patch index 18b111cdefd1d..44c9372b05aff 100644 --- a/patches/node/export_environment_knodecontexttagptr.patch +++ b/patches/node/export_environment_knodecontexttagptr.patch @@ -5,10 +5,10 @@ Subject: export Environment::kNodeContextTagPtr diff --git a/src/env.h b/src/env.h -index 82ed066f9cde4851fa3375d0faffe4bfcf2bf411..2dd6bdb75f02e2ccb07129996d16df99c0c4ccf2 100644 +index 8e12764ea3a9a3d4441b276fdf9db9fce7109f72..6240aaf1a078a26b37adfe90023651559582f92d 100644 --- a/src/env.h +++ b/src/env.h -@@ -1304,7 +1304,7 @@ class Environment : public MemoryRetainer { +@@ -1336,7 +1336,7 @@ class Environment : public MemoryRetainer { uint64_t thread_id_; std::unordered_set sub_worker_contexts_; diff --git a/patches/node/feat_add_flags_for_low-level_hooks_and_exceptions.patch b/patches/node/feat_add_flags_for_low-level_hooks_and_exceptions.patch index 9f7891131775a..fc91404d4c654 100644 --- a/patches/node/feat_add_flags_for_low-level_hooks_and_exceptions.patch +++ b/patches/node/feat_add_flags_for_low-level_hooks_and_exceptions.patch @@ -24,7 +24,7 @@ Environment on the V8 context of blink, so no new V8 context is created. As a result, a renderer process may have multiple Node Environments in it. diff --git a/src/env.cc b/src/env.cc -index 257bf78519f32d690ea9cd5b9e21d3d863801c33..89e940cdc478df8a3be7738106a1c739542a7430 100644 +index 2400785ea82fe4e975d222214161a417d2e6c002..069cd137b35e748dd45e5c720ec71d4fe274562b 100644 --- a/src/env.cc +++ b/src/env.cc @@ -383,6 +383,12 @@ Environment::Environment(IsolateData* isolate_data, @@ -107,7 +107,7 @@ index 572490730da5854ad1a79670564ad9705c86747b..9b43d7676e5e106318b58bfe443651cc // We should set node_is_initialized here instead of in node::Start, diff --git a/src/node.h b/src/node.h -index f78c76023bb667031b46e5fac80028bc5fd5374c..42ad1a5aba3fb57e27a07689768fd0dc8dd37d6d 100644 +index 13d71d41398545963139e61488474e23fa0ebeea..2baf51c3181fe2d390246f655ad35c3aac51f229 100644 --- a/src/node.h +++ b/src/node.h @@ -203,6 +203,9 @@ namespace node { diff --git a/patches/node/feat_add_uv_loop_watcher_queue_code.patch b/patches/node/feat_add_uv_loop_watcher_queue_code.patch index 7b5a1b337d6a0..157e40716b18e 100644 --- a/patches/node/feat_add_uv_loop_watcher_queue_code.patch +++ b/patches/node/feat_add_uv_loop_watcher_queue_code.patch @@ -6,10 +6,10 @@ Subject: feat: add uv_loop watcher_queue code Electron's Node Integration works by listening to Node's backend file descriptor in a separate thread; when an event is ready the backend file descriptor will trigger a new event for it, and the main thread will then iterate the libuv loop. For certain operations (ex. adding a timeout task) the backend file descriptor isn't informed, & as a result the main thread doesn't know it needs to iterate the libuv loop so the timeout task will never execute until something else trigger a new event. This commit should be removed when https://github.com/libuv/libuv/pull/1921 is merged diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h -index f97801cec2f41b104fae591277ffbb94c3b2f299..da648b3efd5948843c485d65035ae29c79eebc69 100644 +index ee45bcaefce1d3684df90c0342b1ff9f89c94544..a73396b2e74eecb01e6abed755875dde6760d3d6 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h -@@ -1657,6 +1657,8 @@ union uv_any_req { +@@ -1693,6 +1693,8 @@ union uv_any_req { struct uv_loop_s { /* User data - use this for whatever. */ void* data; @@ -19,10 +19,10 @@ index f97801cec2f41b104fae591277ffbb94c3b2f299..da648b3efd5948843c485d65035ae29c unsigned int active_handles; void* handle_queue[2]; diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c -index 202c75bbb5e94cb2e8a588b09d4fbfdfe1ccfe3a..041b1838c629ec828f2160ac735953f1bf986c01 100644 +index 366c43c2ab08437a6af6bf15f24563bcc6cb40e1..517ca26c39fbcb63f17bca5a0658bda18bc15921 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c -@@ -892,8 +892,11 @@ void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +@@ -897,8 +897,11 @@ void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { return; #endif @@ -35,7 +35,7 @@ index 202c75bbb5e94cb2e8a588b09d4fbfdfe1ccfe3a..041b1838c629ec828f2160ac735953f1 if (loop->watchers[w->fd] == NULL) { loop->watchers[w->fd] = w; -@@ -929,8 +932,11 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { +@@ -934,8 +937,11 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { w->events = 0; } } @@ -48,7 +48,7 @@ index 202c75bbb5e94cb2e8a588b09d4fbfdfe1ccfe3a..041b1838c629ec828f2160ac735953f1 } -@@ -947,6 +953,8 @@ void uv__io_close(uv_loop_t* loop, uv__io_t* w) { +@@ -952,6 +958,8 @@ void uv__io_close(uv_loop_t* loop, uv__io_t* w) { void uv__io_feed(uv_loop_t* loop, uv__io_t* w) { if (QUEUE_EMPTY(&w->pending_queue)) QUEUE_INSERT_TAIL(&loop->pending_queue, &w->pending_queue); diff --git a/patches/node/feat_initialize_asar_support.patch b/patches/node/feat_initialize_asar_support.patch index 3d0bee7bbff9d..12d11de3d72c7 100644 --- a/patches/node/feat_initialize_asar_support.patch +++ b/patches/node/feat_initialize_asar_support.patch @@ -6,10 +6,10 @@ Subject: feat: initialize asar support This patch initializies asar support in Node.js. diff --git a/lib/internal/bootstrap/loaders.js b/lib/internal/bootstrap/loaders.js -index 2889df4812bb031f1a0debe1eb0b75bd6c846407..0b3b92b33490fe754788a94dedce7533de528acf 100644 +index 0cad5209c4ff4e68d7c400af1d2b4c234210ddb4..918bc8c0c4e78bad5a3372e8a5522da00aa9073e 100644 --- a/lib/internal/bootstrap/loaders.js +++ b/lib/internal/bootstrap/loaders.js -@@ -189,6 +189,8 @@ function nativeModuleRequire(id) { +@@ -192,6 +192,8 @@ function nativeModuleRequire(id) { return mod.compile(); } @@ -19,10 +19,10 @@ index 2889df4812bb031f1a0debe1eb0b75bd6c846407..0b3b92b33490fe754788a94dedce7533 return NativeModule.map.has(id); }; diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js -index 158e9b73fa..8773e37c5c 100644 +index f959310156141623c2acc9e4d8bdad433d401925..113d2e58a9cecf233bf6a60138cdfc60f338b28c 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js -@@ -52,6 +52,7 @@ function prepareMainThreadExecution(expandArgv1 = false) { +@@ -61,6 +61,7 @@ function prepareMainThreadExecution(expandArgv1 = false) { initializeESMLoader(); loadPreloadModules(); initializeFrozenIntrinsics(); @@ -30,7 +30,7 @@ index 158e9b73fa..8773e37c5c 100644 } function patchProcessObject(expandArgv1) { -@@ -420,6 +421,10 @@ function loadPreloadModules() { +@@ -444,6 +445,10 @@ function loadPreloadModules() { } } diff --git a/patches/node/fix_add_default_values_for_enable_lto_and_build_v8_with_gn_in.patch b/patches/node/fix_add_default_values_for_enable_lto_and_build_v8_with_gn_in.patch index fe018e7ad4c60..81fe381573bc4 100644 --- a/patches/node/fix_add_default_values_for_enable_lto_and_build_v8_with_gn_in.patch +++ b/patches/node/fix_add_default_values_for_enable_lto_and_build_v8_with_gn_in.patch @@ -8,7 +8,7 @@ common.gypi is a file that's included in the node header bundle, despite the fact that we do not build node with gyp. diff --git a/common.gypi b/common.gypi -index b86e5e05d7df9ad472f16735448a53f433620eef..f07e65f719a1a5939997dfcae7bc787ee6391f4d 100644 +index 6501f78796fd17503f5ff9cd3a52cbfa688c2814..bde7d7300f44596abe5cdfac0639ecb1bb4d885f 100644 --- a/common.gypi +++ b/common.gypi @@ -77,6 +77,22 @@ diff --git a/patches/node/fix_build_and_expose_inspector_agent.patch b/patches/node/fix_build_and_expose_inspector_agent.patch index d5814ce4be916..3b1735de8f339 100644 --- a/patches/node/fix_build_and_expose_inspector_agent.patch +++ b/patches/node/fix_build_and_expose_inspector_agent.patch @@ -6,7 +6,7 @@ Subject: fix: build and expose inspector agent Node inspector initialization happens in a different start-up function in node.cc, which we don't call in Electron. This allows for us to use the inspector agent in electron/atom/browser/node_debugger.cc diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc -index 2965483dea6244e7123c08c104f399fc023a84e0..b6624300e96daf6041a296fdc1ceeaf579e215cd 100644 +index cf0110144a535dcc7e691650d015da6715179d7e..edb27e4650b1e98b5a5f7765c60e0dd45e7956f0 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -217,7 +217,7 @@ const int CONTEXT_GROUP_ID = 1; @@ -28,7 +28,7 @@ index 2965483dea6244e7123c08c104f399fc023a84e0..b6624300e96daf6041a296fdc1ceeaf5 info.is_default = true; contextCreated(env->context(), info); diff --git a/src/inspector_agent.h b/src/inspector_agent.h -index 4fb544f85bd1453d0705088dfb5511ae32bacad2..ac9352e3fa953e2f4ac0a032c2aacd7353c2cf71 100644 +index d5088a1b5469044a3f694522c547c901d7cd91f8..19bdf7e6e7e8df944eb76e52e63957dc4cffdab8 100644 --- a/src/inspector_agent.h +++ b/src/inspector_agent.h @@ -6,7 +6,9 @@ diff --git a/patches/node/fix_do_not_define_debugoptions_s_constructors_in_header.patch b/patches/node/fix_do_not_define_debugoptions_s_constructors_in_header.patch index fe4cea6e5a2cf..8df36da657831 100644 --- a/patches/node/fix_do_not_define_debugoptions_s_constructors_in_header.patch +++ b/patches/node/fix_do_not_define_debugoptions_s_constructors_in_header.patch @@ -15,7 +15,7 @@ By putting the definitions of constructors into the implementation file, we can avoid this problem. diff --git a/src/node_options.cc b/src/node_options.cc -index f4dc3d7df5a33ce92ca52e901283db5aa9f143b4..b9d6108c5db800465e6c7156ad1c28002c33261b 100644 +index c0c15a88c028d9c70081aa75fabf63d1d78014e6..03919d450b3373983d647165ee93c006eb5f2902 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -26,6 +26,12 @@ Mutex cli_options_mutex; @@ -32,7 +32,7 @@ index f4dc3d7df5a33ce92ca52e901283db5aa9f143b4..b9d6108c5db800465e6c7156ad1c2800 #if !NODE_USE_V8_PLATFORM if (inspector_enabled) { diff --git a/src/node_options.h b/src/node_options.h -index 5fb23cfda7aebca507174555b78c9a73e837dda0..ce86827024126c6b0d8462c811360057c298ec5b 100644 +index 40c19ea6ff4d98a1a1da59bca76087209445af81..4ce5551284bb5b1b4194905a9fe619f852933405 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -61,11 +61,11 @@ struct InspectPublishUid { diff --git a/patches/node/fix_don_t_create_console_window_when_creating_process.patch b/patches/node/fix_don_t_create_console_window_when_creating_process.patch index b5242c42f8e4c..3f8dba9c35402 100644 --- a/patches/node/fix_don_t_create_console_window_when_creating_process.patch +++ b/patches/node/fix_don_t_create_console_window_when_creating_process.patch @@ -6,7 +6,7 @@ Subject: fix: don't create console window when creating process This commit prevents console windows from being spawned when creating processes to better align with what Windows users expect and should be removed when upgrading to a version that includes https://github.com/nodejs/node/pull/21316 diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c -index f9c53de0af007961b0d269651d368fd83717976a..fa1a76a2e6626e15bdd0681626fc82c9ca6907fe 100644 +index 9b7fdc1dc1b4e2b9e16e053d1aca36ef5221c460..e1010d1248a95a3927d6ed1a1affbb545c1d7201 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -1099,6 +1099,9 @@ int uv_spawn(uv_loop_t* loop, diff --git a/patches/node/fix_export_debugoptions.patch b/patches/node/fix_export_debugoptions.patch index 9618fa25af66d..4544aedadb960 100644 --- a/patches/node/fix_export_debugoptions.patch +++ b/patches/node/fix_export_debugoptions.patch @@ -5,10 +5,10 @@ Subject: fix: export DebugOptions diff --git a/src/node_options.cc b/src/node_options.cc -index e2a39626b69fd3380febc258fb25efc0cea2a08e..f4dc3d7df5a33ce92ca52e901283db5aa9f143b4 100644 +index d53ab2a4bfe87321e899b431eac262223e532335..c0c15a88c028d9c70081aa75fabf63d1d78014e6 100644 --- a/src/node_options.cc +++ b/src/node_options.cc -@@ -204,11 +204,6 @@ void EnvironmentOptions::CheckOptions(std::vector* errors) { +@@ -210,11 +210,6 @@ void EnvironmentOptions::CheckOptions(std::vector* errors) { namespace options_parser { @@ -21,7 +21,7 @@ index e2a39626b69fd3380febc258fb25efc0cea2a08e..f4dc3d7df5a33ce92ca52e901283db5a public: EnvironmentOptionsParser(); diff --git a/src/node_options.h b/src/node_options.h -index dbd85b2d584be57619fc5c8d019140f67514f427..5fb23cfda7aebca507174555b78c9a73e837dda0 100644 +index 8937bfd9011e4795d22e232886e18183d698b8d4..40c19ea6ff4d98a1a1da59bca76087209445af81 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -59,7 +59,7 @@ struct InspectPublishUid { @@ -33,7 +33,7 @@ index dbd85b2d584be57619fc5c8d019140f67514f427..5fb23cfda7aebca507174555b78c9a73 public: DebugOptions() = default; DebugOptions(const DebugOptions&) = default; -@@ -240,7 +240,7 @@ class PerProcessOptions : public Options { +@@ -244,7 +244,7 @@ class PerProcessOptions : public Options { namespace options_parser { @@ -42,7 +42,7 @@ index dbd85b2d584be57619fc5c8d019140f67514f427..5fb23cfda7aebca507174555b78c9a73 std::vector* errors); void GetOptions(const v8::FunctionCallbackInfo& args); -@@ -433,6 +433,11 @@ class OptionsParser { +@@ -437,6 +437,11 @@ class OptionsParser { friend void GetOptions(const v8::FunctionCallbackInfo& args); }; diff --git a/patches/node/fix_export_node_abort_and_assert.patch b/patches/node/fix_export_node_abort_and_assert.patch index 2c71b2886131a..d4d2db8266fb8 100644 --- a/patches/node/fix_export_node_abort_and_assert.patch +++ b/patches/node/fix_export_node_abort_and_assert.patch @@ -5,7 +5,7 @@ Subject: fix: Export node::Abort and Assert diff --git a/src/node_errors.cc b/src/node_errors.cc -index 0214521144c607c084cabc941f006780bcd3cfa2..ea4113aa3b2b431c6d9bbfbf4b20f8ff59cd9182 100644 +index d3a409b1ab455a018768982f24f644dbcddb72db..66da1c58340d7ad39a667d8dcb35dcdf4a165996 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -242,13 +242,13 @@ void AppendExceptionLine(Environment* env, diff --git a/patches/node/fix_expose_internalcallbackscope.patch b/patches/node/fix_expose_internalcallbackscope.patch index b4ab8ef01c9d3..73ffa3c248443 100644 --- a/patches/node/fix_expose_internalcallbackscope.patch +++ b/patches/node/fix_expose_internalcallbackscope.patch @@ -6,10 +6,10 @@ Subject: fix: expose InternalCallbackScope This commit exposes InternalCallbackScope in order to allow us access to kAllowEmptyResource for usage https://github.com/electron/electron/blob/master/atom/common/api/atom_bindings.cc\#L108. We should look to accomplish this another way so we no longer need to do this, as in verbose mode the regular CallBack Scope doesn't swallow errors and so we can otherwise use it. diff --git a/src/node_internals.h b/src/node_internals.h -index 621ed0225ba674bc31656ec628ee2b376a4f900c..2311036beadf7efaac4999ceef985566fe41c763 100644 +index 85d2c5c1f18db01d64f8285720f6624f15aa2436..fb1188a6bbce37b2bbaba0a815f2826dca60cf3b 100644 --- a/src/node_internals.h +++ b/src/node_internals.h -@@ -198,7 +198,7 @@ v8::MaybeLocal InternalMakeCallback( +@@ -200,7 +200,7 @@ v8::MaybeLocal InternalMakeCallback( v8::Local argv[], async_context asyncContext); diff --git a/patches/node/fix_expose_tracing_agent_and_use_tracing_tracingcontroller_instead.patch b/patches/node/fix_expose_tracing_agent_and_use_tracing_tracingcontroller_instead.patch index d8eb0979ce471..2dd5bbfba8b26 100644 --- a/patches/node/fix_expose_tracing_agent_and_use_tracing_tracingcontroller_instead.patch +++ b/patches/node/fix_expose_tracing_agent_and_use_tracing_tracingcontroller_instead.patch @@ -5,7 +5,7 @@ Subject: fix: expose tracing::Agent and use tracing::TracingController instead of v8::TracingController diff --git a/src/api/environment.cc b/src/api/environment.cc -index ac1e513967310aefa50100843c643c788ab5157c..35e8666ef771c56a1bcd441c99d4483a0d3e90bf 100644 +index 2c0fe1306319b25c378d0aae73e4045560a9b683..ae26cb7e9ef55fc0b965e28de4686aec87f42522 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -314,6 +314,10 @@ MultiIsolatePlatform* GetMainThreadMultiIsolatePlatform() { @@ -20,7 +20,7 @@ index ac1e513967310aefa50100843c643c788ab5157c..35e8666ef771c56a1bcd441c99d4483a int thread_pool_size, node::tracing::TracingController* tracing_controller) { diff --git a/src/node.h b/src/node.h -index 42ad1a5aba3fb57e27a07689768fd0dc8dd37d6d..a2b47d1cf984ec36994b477b371cdf262191ab9c 100644 +index 2baf51c3181fe2d390246f655ad35c3aac51f229..80a27dc734a81a7ca8d888d1d55fc8d24a536280 100644 --- a/src/node.h +++ b/src/node.h @@ -108,6 +108,7 @@ namespace node { @@ -31,7 +31,7 @@ index 42ad1a5aba3fb57e27a07689768fd0dc8dd37d6d..a2b47d1cf984ec36994b477b371cdf26 class TracingController; } -@@ -334,6 +335,8 @@ NODE_EXTERN Environment* GetCurrentEnvironment(v8::Local context); +@@ -338,6 +339,8 @@ NODE_EXTERN Environment* GetCurrentEnvironment(v8::Local context); // it returns nullptr. NODE_EXTERN MultiIsolatePlatform* GetMainThreadMultiIsolatePlatform(); diff --git a/patches/node/fix_extern_the_nativemoduleenv_and_options_parser_for_debug_builds.patch b/patches/node/fix_extern_the_nativemoduleenv_and_options_parser_for_debug_builds.patch index 55e030cd70273..14440bed84a4d 100644 --- a/patches/node/fix_extern_the_nativemoduleenv_and_options_parser_for_debug_builds.patch +++ b/patches/node/fix_extern_the_nativemoduleenv_and_options_parser_for_debug_builds.patch @@ -5,7 +5,7 @@ Subject: fix: extern the NativeModuleEnv and options parser for debug builds diff --git a/src/node_native_module_env.h b/src/node_native_module_env.h -index b91a5059cd1f19d87e5876c372f3ded60681a5df..f81cf8dfb892a8ddd9084c5d9477b17867cae82d 100644 +index f662c67be50d404ee5b6cf6e2b8dd5991c59e723..da2d458344fb1006ac11c5b19c6ac5fbbca66a08 100644 --- a/src/node_native_module_env.h +++ b/src/node_native_module_env.h @@ -12,7 +12,7 @@ namespace native_module { @@ -17,8 +17,21 @@ index b91a5059cd1f19d87e5876c372f3ded60681a5df..f81cf8dfb892a8ddd9084c5d9477b178 public: static void Initialize(v8::Local target, v8::Local unused, +diff --git a/src/node_options.cc b/src/node_options.cc +index b9db121250c77e1bc8c35af336361ee444c271e4..a8e287279b31cba12d18300d08f0fd3856069a03 100644 +--- a/src/node_options.cc ++++ b/src/node_options.cc +@@ -243,7 +243,7 @@ const EnvironmentOptionsParser _eop_instance{_dop_instance}; + + // This Parse is not dead code. It is used by embedders (e.g., Electron). + template <> +-void Parse( ++void NODE_EXTERN Parse( + StringVector* const args, StringVector* const exec_args, + StringVector* const v8_args, + DebugOptions* const options, diff --git a/src/node_options.h b/src/node_options.h -index ce86827024126c6b0d8462c811360057c298ec5b..d53307f9589d4487c220526b8e49b6c7fbd29daf 100644 +index 4ce5551284bb5b1b4194905a9fe619f852933405..07f18b5a91c7c42da9f780408e077235a4985f57 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -44,7 +44,7 @@ class HostPort { @@ -30,7 +43,7 @@ index ce86827024126c6b0d8462c811360057c298ec5b..d53307f9589d4487c220526b8e49b6c7 public: virtual void CheckOptions(std::vector* errors) {} virtual ~Options() = default; -@@ -440,7 +440,7 @@ class NODE_EXTERN DebugOptionsParser : public OptionsParser { +@@ -444,7 +444,7 @@ class NODE_EXTERN DebugOptionsParser : public OptionsParser { using StringVector = std::vector; template @@ -39,16 +52,3 @@ index ce86827024126c6b0d8462c811360057c298ec5b..d53307f9589d4487c220526b8e49b6c7 StringVector* const args, StringVector* const exec_args, StringVector* const v8_args, OptionsType* const options, OptionEnvvarSettings required_env_settings, StringVector* const errors); -diff --git a/src/node_options.cc b/src/node_options.cc -index 01dddb979042c098326a8b1ff37397642733f49b..f91a23c1d7ee8a141d349c486b7520c175094b8b 100644 ---- a/src/node_options.cc -+++ b/src/node_options.cc -@@ -237,7 +237,7 @@ const EnvironmentOptionsParser _eop_instance{_dop_instance}; - - // This Parse is not dead code. It is used by embedders (e.g., Electron). - template <> --void Parse( -+void NODE_EXTERN Parse( - StringVector* const args, StringVector* const exec_args, - StringVector* const v8_args, - DebugOptions* const options, diff --git a/patches/node/fix_key_gen_apis_are_not_available_in_boringssl.patch b/patches/node/fix_key_gen_apis_are_not_available_in_boringssl.patch index e7a0c299438a2..5eae38d197a75 100644 --- a/patches/node/fix_key_gen_apis_are_not_available_in_boringssl.patch +++ b/patches/node/fix_key_gen_apis_are_not_available_in_boringssl.patch @@ -6,7 +6,7 @@ Subject: fix: key gen APIs are not available in BoringSSL This will make Node's key pair generation APIs fail. diff --git a/src/node_crypto.cc b/src/node_crypto.cc -index bca482cc9724fef680196a192a6bbd7b4d2e4fde..1cc2372fe2e385e9a9db27e38f0da61d62de2e81 100644 +index 2d539094a0589e9945bf2f27fa8a8afce47031b0..2c702d2f051d6b8f59ac53a4e2729ee02ba98a47 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -290,24 +290,14 @@ Maybe Decorate(Environment* env, Local obj, @@ -34,7 +34,7 @@ index bca482cc9724fef680196a192a6bbd7b4d2e4fde..1cc2372fe2e385e9a9db27e38f0da61d V(USER) \ #define V(name) case ERR_LIB_##name: lib = #name "_"; break; -@@ -2487,8 +2477,11 @@ void SSLWrap::GetEphemeralKeyInfo( +@@ -2557,8 +2547,11 @@ void SSLWrap::GetEphemeralKeyInfo( .Check(); break; case EVP_PKEY_EC: @@ -46,7 +46,7 @@ index bca482cc9724fef680196a192a6bbd7b4d2e4fde..1cc2372fe2e385e9a9db27e38f0da61d { const char* curve_name; if (kid == EVP_PKEY_EC) { -@@ -3742,12 +3735,15 @@ Local KeyObject::GetAsymmetricKeyType() const { +@@ -3894,12 +3887,15 @@ Local KeyObject::GetAsymmetricKeyType() const { return env()->crypto_ec_string(); case EVP_PKEY_ED25519: return env()->crypto_ed25519_string(); @@ -62,7 +62,7 @@ index bca482cc9724fef680196a192a6bbd7b4d2e4fde..1cc2372fe2e385e9a9db27e38f0da61d default: return Undefined(env()->isolate()); } -@@ -6240,6 +6236,8 @@ class DSAKeyPairGenerationConfig : public KeyPairGenerationConfig { +@@ -6402,6 +6398,8 @@ class DSAKeyPairGenerationConfig : public KeyPairGenerationConfig { if (EVP_PKEY_paramgen_init(param_ctx.get()) <= 0) return nullptr; @@ -71,7 +71,7 @@ index bca482cc9724fef680196a192a6bbd7b4d2e4fde..1cc2372fe2e385e9a9db27e38f0da61d if (EVP_PKEY_CTX_set_dsa_paramgen_bits(param_ctx.get(), modulus_bits_) <= 0) return nullptr; -@@ -6259,6 +6257,8 @@ class DSAKeyPairGenerationConfig : public KeyPairGenerationConfig { +@@ -6421,6 +6419,8 @@ class DSAKeyPairGenerationConfig : public KeyPairGenerationConfig { EVPKeyCtxPointer key_ctx(EVP_PKEY_CTX_new(params.get(), nullptr)); return key_ctx; @@ -80,7 +80,7 @@ index bca482cc9724fef680196a192a6bbd7b4d2e4fde..1cc2372fe2e385e9a9db27e38f0da61d } private: -@@ -6950,9 +6950,12 @@ void Initialize(Local target, +@@ -7112,9 +7112,12 @@ void Initialize(Local target, env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC); env->SetMethod(target, "generateKeyPairNid", GenerateKeyPairNid); NODE_DEFINE_CONSTANT(target, EVP_PKEY_ED25519); diff --git a/patches/node/fix_windows_compilation_on_libuv_setsockopt.patch b/patches/node/fix_windows_compilation_on_libuv_setsockopt.patch index 30c327a49dc3f..a500e99b131e8 100644 --- a/patches/node/fix_windows_compilation_on_libuv_setsockopt.patch +++ b/patches/node/fix_windows_compilation_on_libuv_setsockopt.patch @@ -7,7 +7,7 @@ Upstreamed in https://github.com/libuv/libuv/pull/2520. This patch should be removed when Node.js rolls onto libuv v1.33.0. diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c -index 81e48136a3b9ef13b1b95d87a68ab3ba98f9aeb9..41f8fcbe8361dc7c7a145b38094745cb0fcfa350 100644 +index 81e48136a3b9ef13b1b95d87a68ab3ba98f9aeb9..fd34c623d8c543a01b70a17184b09bb4e29081eb 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -556,7 +556,7 @@ int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) { diff --git a/patches/node/fixme_comment_trace_event_macro.patch b/patches/node/fixme_comment_trace_event_macro.patch index 8c980544c8b0d..69bde9869351e 100644 --- a/patches/node/fixme_comment_trace_event_macro.patch +++ b/patches/node/fixme_comment_trace_event_macro.patch @@ -5,10 +5,10 @@ Subject: fixme: Comment trace event macro diff --git a/src/node_internals.h b/src/node_internals.h -index 2311036beadf7efaac4999ceef985566fe41c763..10fbbc1d8cf43f76248efdac9a2539a8c8287d71 100644 +index fb1188a6bbce37b2bbaba0a815f2826dca60cf3b..843cbf1d253022c9601ceaf74022873a48ac0a5f 100644 --- a/src/node_internals.h +++ b/src/node_internals.h -@@ -348,10 +348,11 @@ class TraceEventScope { +@@ -350,10 +350,11 @@ class TraceEventScope { TraceEventScope(const char* category, const char* name, void* id) : category_(category), name_(name), id_(id) { diff --git a/patches/node/fixme_remove_async_id_assertion_check.patch b/patches/node/fixme_remove_async_id_assertion_check.patch index b4ea0a56e3a5d..74e7cef6d8202 100644 --- a/patches/node/fixme_remove_async_id_assertion_check.patch +++ b/patches/node/fixme_remove_async_id_assertion_check.patch @@ -5,10 +5,10 @@ Subject: FIXME: remove async_id assertion check diff --git a/src/api/callback.cc b/src/api/callback.cc -index 52a8da35b671d196331b858ba46be04aecf1e0be..43ccfafd9f2c85e23a9ea6277e88e4864e287905 100644 +index e6098d5921a038063bf8255f8af6f000becf76a0..3c518870c9c8d92f3dfcd6c270f5e023e3b69633 100644 --- a/src/api/callback.cc +++ b/src/api/callback.cc -@@ -103,12 +103,14 @@ void InternalCallbackScope::Close() { +@@ -104,12 +104,14 @@ void InternalCallbackScope::Close() { MicrotasksScope::PerformCheckpoint(env_->isolate()); } diff --git a/patches/node/fixme_revert_crypto_add_support_for_rsa-pss_keys.patch b/patches/node/fixme_revert_crypto_add_support_for_rsa-pss_keys.patch index 759c53fdabe90..923d52810fa02 100644 --- a/patches/node/fixme_revert_crypto_add_support_for_rsa-pss_keys.patch +++ b/patches/node/fixme_revert_crypto_add_support_for_rsa-pss_keys.patch @@ -68,7 +68,7 @@ index 0a4bde77fa369b788bd7e2976441358254b13955..37835db411f7577d39e5e2af10590b6f if (options) { diff --git a/lib/internal/crypto/sig.js b/lib/internal/crypto/sig.js -index b6a3376e735094f970ef61a52ef9bb6d6101647a..bb70c2305fd44cfea46a8df5e65d1af5a86d40db 100644 +index 9b9c32e59c8fd03a0e9170337ae705fd82443507..09084a3cdb878ec3fe9dcdfc2e0c497df726b72f 100644 --- a/lib/internal/crypto/sig.js +++ b/lib/internal/crypto/sig.js @@ -14,6 +14,10 @@ const { @@ -82,7 +82,7 @@ index b6a3376e735094f970ef61a52ef9bb6d6101647a..bb70c2305fd44cfea46a8df5e65d1af5 const { getDefaultEncoding, kHandle, -@@ -54,14 +58,14 @@ Sign.prototype.update = function update(data, encoding) { +@@ -52,14 +56,14 @@ Sign.prototype.update = function update(data, encoding) { }; function getPadding(options) { @@ -100,7 +100,7 @@ index b6a3376e735094f970ef61a52ef9bb6d6101647a..bb70c2305fd44cfea46a8df5e65d1af5 const value = options[name]; if (value !== undefined) { if (value === value >> 0) { -@@ -70,7 +74,7 @@ function getIntOption(name, options) { +@@ -68,7 +72,7 @@ function getIntOption(name, options) { throw new ERR_INVALID_OPT_VALUE(name, value); } } @@ -110,10 +110,10 @@ index b6a3376e735094f970ef61a52ef9bb6d6101647a..bb70c2305fd44cfea46a8df5e65d1af5 Sign.prototype.sign = function sign(options, encoding) { diff --git a/src/env.h b/src/env.h -index ecb845803776f5ff6e7eec712ec7a5ebea2e8d0a..1516d77be8514ce55d8d21b78a7cd85ead2cb95f 100644 +index b83eee0dc5b791748643c4918baf0b4db1efa75c..5b8465c8ba5335c8706b54dcf6e4ed46af31bcf0 100644 --- a/src/env.h +++ b/src/env.h -@@ -173,7 +173,6 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2; +@@ -195,7 +195,6 @@ constexpr size_t kFsStatsBufferLength = V(crypto_x25519_string, "x25519") \ V(crypto_x448_string, "x448") \ V(crypto_rsa_string, "rsa") \ @@ -122,10 +122,10 @@ index ecb845803776f5ff6e7eec712ec7a5ebea2e8d0a..1516d77be8514ce55d8d21b78a7cd85e V(data_string, "data") \ V(dest_string, "dest") \ diff --git a/src/node_crypto.cc b/src/node_crypto.cc -index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61eeb6bc60bf 100644 +index 2c702d2f051d6b8f59ac53a4e2729ee02ba98a47..9250246a453a89f29bb607ebcc6c9d07642c3561 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc -@@ -3727,8 +3727,6 @@ Local KeyObject::GetAsymmetricKeyType() const { +@@ -3879,8 +3879,6 @@ Local KeyObject::GetAsymmetricKeyType() const { switch (EVP_PKEY_id(this->asymmetric_key_.get())) { case EVP_PKEY_RSA: return env()->crypto_rsa_string(); @@ -134,7 +134,7 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee case EVP_PKEY_DSA: return env()->crypto_dsa_string(); case EVP_PKEY_EC: -@@ -4775,14 +4773,13 @@ void SignBase::CheckThrow(SignBase::Error error) { +@@ -4926,14 +4924,13 @@ void SignBase::CheckThrow(SignBase::Error error) { static bool ApplyRSAOptions(const ManagedEVPPKey& pkey, EVP_PKEY_CTX* pkctx, int padding, @@ -153,7 +153,7 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee return false; } } -@@ -4833,16 +4830,11 @@ void Sign::SignUpdate(const FunctionCallbackInfo& args) { +@@ -4984,16 +4981,11 @@ void Sign::SignUpdate(const FunctionCallbackInfo& args) { sign->CheckThrow(err); } @@ -171,7 +171,7 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee unsigned char m[EVP_MAX_MD_SIZE]; unsigned int m_len; -@@ -4875,7 +4867,7 @@ static AllocatedBuffer Node_SignFinal(Environment* env, +@@ -5048,7 +5040,7 @@ static inline bool ValidateDSAParameters(EVP_PKEY* key) { Sign::SignResult Sign::SignFinal( const ManagedEVPPKey& pkey, int padding, @@ -180,7 +180,7 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee if (!mdctx_) return SignResult(kSignNotInitialised); -@@ -4926,17 +4918,11 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { +@@ -5075,17 +5067,11 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { if (!key) return; @@ -202,7 +202,7 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee SignResult ret = sign->SignFinal( key, -@@ -4997,17 +4983,11 @@ void SignOneShot(const FunctionCallbackInfo& args) { +@@ -5122,17 +5108,11 @@ void SignOneShot(const FunctionCallbackInfo& args) { return CheckThrow(env, SignBase::Error::kSignUnknownDigest); } @@ -224,7 +224,7 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee EVP_PKEY_CTX* pkctx = nullptr; EVPMDPointer mdctx(EVP_MD_CTX_new()); -@@ -5085,7 +5065,7 @@ SignBase::Error Verify::VerifyFinal(const ManagedEVPPKey& pkey, +@@ -5210,7 +5190,7 @@ SignBase::Error Verify::VerifyFinal(const ManagedEVPPKey& pkey, const char* sig, int siglen, int padding, @@ -233,7 +233,7 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee bool* verify_result) { if (!mdctx_) return kSignNotInitialised; -@@ -5129,17 +5109,11 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { +@@ -5254,17 +5234,11 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { ArrayBufferViewContents hbuf(args[offset]); @@ -255,7 +255,7 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee bool verify_result; Error err = verify->VerifyFinal(pkey, hbuf.data(), hbuf.length(), padding, -@@ -5172,17 +5146,11 @@ void VerifyOneShot(const FunctionCallbackInfo& args) { +@@ -5297,17 +5271,11 @@ void VerifyOneShot(const FunctionCallbackInfo& args) { return CheckThrow(env, SignBase::Error::kSignUnknownDigest); } @@ -277,7 +277,7 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee EVP_PKEY_CTX* pkctx = nullptr; EVPMDPointer mdctx(EVP_MD_CTX_new()); -@@ -6181,48 +6149,6 @@ class RSAKeyPairGenerationConfig : public KeyPairGenerationConfig { +@@ -6343,48 +6311,6 @@ class RSAKeyPairGenerationConfig : public KeyPairGenerationConfig { const unsigned int exponent_; }; @@ -326,7 +326,7 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee class DSAKeyPairGenerationConfig : public KeyPairGenerationConfig { public: DSAKeyPairGenerationConfig(unsigned int modulus_bits, int divisor_bits) -@@ -6456,44 +6382,6 @@ void GenerateKeyPairRSA(const FunctionCallbackInfo& args) { +@@ -6618,44 +6544,6 @@ void GenerateKeyPairRSA(const FunctionCallbackInfo& args) { GenerateKeyPair(args, 2, std::move(config)); } @@ -371,7 +371,7 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee void GenerateKeyPairDSA(const FunctionCallbackInfo& args) { CHECK(args[0]->IsUint32()); const uint32_t modulus_bits = args[0].As()->Value(); -@@ -6945,7 +6833,6 @@ void Initialize(Local target, +@@ -7107,7 +6995,6 @@ void Initialize(Local target, env->SetMethod(target, "pbkdf2", PBKDF2); env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA); @@ -380,10 +380,10 @@ index 1cc2372fe2e385e9a9db27e38f0da61d62de2e81..cd09cdb3f2244825f6631891b94e61ee env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC); env->SetMethod(target, "generateKeyPairNid", GenerateKeyPairNid); diff --git a/src/node_crypto.h b/src/node_crypto.h -index 07ca412e8f7fc6281553ca90a4c3c8c18726c526..04a06affce1de8c567034d084c43b1a016076353 100644 +index 206a19119a73216ffe16f935ee6a9423bff6865f..560694bfd2c1e2ce999b61682b0f3ea757f4f69e 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h -@@ -660,7 +660,7 @@ class Sign : public SignBase { +@@ -666,7 +666,7 @@ class Sign : public SignBase { SignResult SignFinal( const ManagedEVPPKey& pkey, int padding, @@ -392,7 +392,7 @@ index 07ca412e8f7fc6281553ca90a4c3c8c18726c526..04a06affce1de8c567034d084c43b1a0 protected: static void New(const v8::FunctionCallbackInfo& args); -@@ -681,7 +681,7 @@ class Verify : public SignBase { +@@ -687,7 +687,7 @@ class Verify : public SignBase { const char* sig, int siglen, int padding, diff --git a/patches/node/fixme_use_redefined_version_of_internalmodulestat.patch b/patches/node/fixme_use_redefined_version_of_internalmodulestat.patch index 60bb81f87958b..62e429f5b48b4 100644 --- a/patches/node/fixme_use_redefined_version_of_internalmodulestat.patch +++ b/patches/node/fixme_use_redefined_version_of_internalmodulestat.patch @@ -9,10 +9,10 @@ this has to be done after the upgrade to the Node.js v8.7.0. in the very beginn to a native Node.js implementation of the function. diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js -index 437546e38ad30c518dce45f2f9ab104bdffbbe38..adb6dffa8f89035078c6e5b8a975c4874a5b0eb8 100644 +index 481d59594dbca27c8529a8b424ab3cb190dd8def..03247551074e1ad3cdd412e6adc0d865746225d1 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js -@@ -117,6 +117,8 @@ function enrichCJSError(err) { +@@ -120,6 +120,8 @@ function enrichCJSError(err) { } function stat(filename) { diff --git a/patches/node/fsevents-regression-in-watching.patch b/patches/node/fsevents-regression-in-watching.patch index e808aba80e358..e32aa0bd3ff6a 100644 --- a/patches/node/fsevents-regression-in-watching.patch +++ b/patches/node/fsevents-regression-in-watching.patch @@ -1,7 +1,10 @@ -From ae12376dbb56fa080b699f00840c7b9c5230a85f Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 7 Sep 2019 20:45:39 -0400 -Subject: [PATCH] fsevents: regression in watching / +Subject: fsevents: regression in watching / +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit This case got lost by accident in https://github.com/libuv/libuv/pull/2082, @@ -13,7 +16,7 @@ Reviewed-By: Ben Noordhuis Reviewed-By: Saúl Ibarra Corretgé diff --git a/deps/uv/src/unix/fsevents.c b/deps/uv/src/unix/fsevents.c -index ddacda31..deeaa63d 100644 +index ddacda31fef87eee131fc2ee2ff46cc88be429d9..deeaa63d4730de9aa17ee87923acd96d6507a55d 100644 --- a/deps/uv/src/unix/fsevents.c +++ b/deps/uv/src/unix/fsevents.c @@ -263,10 +263,12 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, @@ -32,7 +35,7 @@ index ddacda31..deeaa63d 100644 if (memcmp(path, handle->realpath, handle->realpath_len) != 0) diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c -index 4b8bb1ef..7725c3af 100644 +index 4b8bb1ef03e54407cba8eef85179039632cc3f28..7725c3af94edd5d62bb960912262d38aefa6676e 100644 --- a/deps/uv/test/test-fs-event.c +++ b/deps/uv/test/test-fs-event.c @@ -47,6 +47,7 @@ static const char file_prefix[] = "fsevent-"; diff --git a/patches/node/fsevents-stop-using-fsevents-to-watch-files.patch b/patches/node/fsevents-stop-using-fsevents-to-watch-files.patch index b502c5540d58f..65ab81f31b145 100644 --- a/patches/node/fsevents-stop-using-fsevents-to-watch-files.patch +++ b/patches/node/fsevents-stop-using-fsevents-to-watch-files.patch @@ -1,7 +1,10 @@ -From 97b85e8b75b8f3df774b6e008dbaa143daa412b7 Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 7 Sep 2019 14:55:40 -0400 -Subject: [PATCH] fsevents: stop using fsevents to watch files +Subject: fsevents: stop using fsevents to watch files +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit Goes back to just using it to watch folders, but keeps the other logic changes around. @@ -17,7 +20,7 @@ Reviewed-By: Ben Noordhuis Reviewed-By: Saúl Ibarra Corretgé diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c -index c04e7a48..ad09f403 100644 +index c04e7a485cf992beec501144e04ff068c17b9494..ad09f4031318cafe08faed3f0a6373e2bb598672 100644 --- a/deps/uv/src/unix/kqueue.c +++ b/deps/uv/src/unix/kqueue.c @@ -454,10 +454,26 @@ int uv_fs_event_start(uv_fs_event_t* handle, @@ -102,7 +105,7 @@ index c04e7a48..ad09f403 100644 #endif diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c -index ea34bd63..4b8bb1ef 100644 +index ea34bd63a70625c3e2c60d5a1bbb087c5f0bbb2e..4b8bb1ef03e54407cba8eef85179039632cc3f28 100644 --- a/deps/uv/test/test-fs-event.c +++ b/deps/uv/test/test-fs-event.c @@ -656,6 +656,12 @@ TEST_IMPL(fs_event_watch_file_current_dir) { diff --git a/patches/node/make_module_globalpaths_a_reference.patch b/patches/node/make_module_globalpaths_a_reference.patch index cfa1b76fbc7f8..4ab77086aad7d 100644 --- a/patches/node/make_module_globalpaths_a_reference.patch +++ b/patches/node/make_module_globalpaths_a_reference.patch @@ -10,10 +10,10 @@ node's module code. (cherry picked from commit 76ba048c37588ee32636817fa7b8dffc64330cbf) diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js -index 95724d4c27f12e4f9c90f70b20d3d7fad3e9f035..2a1a412fe7c55b8046b19860005e89a52ee3b816 100644 +index 2d058b7e0dace3084d068f3e88dffbe93a67390d..a1c6db94e49fb2f13c68dc503481beb3decf5cf4 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js -@@ -1008,8 +1008,8 @@ Module._initPaths = function() { +@@ -1102,8 +1102,8 @@ Module._initPaths = function() { modulePaths = paths; diff --git a/patches/node/pass_all_globals_through_require.patch b/patches/node/pass_all_globals_through_require.patch index 4ea9e31851004..01ceb3f6fe9da 100644 --- a/patches/node/pass_all_globals_through_require.patch +++ b/patches/node/pass_all_globals_through_require.patch @@ -6,10 +6,10 @@ Subject: Pass all globals through "require" (cherry picked from commit 7d015419cb7a0ecfe6728431a4ed2056cd411d62) diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js -index 2a1a412fe7c55b8046b19860005e89a52ee3b816..70da676a40feeaa82d3a0ccd1c17882ab5d6edf3 100644 +index a1c6db94e49fb2f13c68dc503481beb3decf5cf4..481d59594dbca27c8529a8b424ab3cb190dd8def 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js -@@ -79,6 +79,13 @@ const { +@@ -83,6 +83,13 @@ const { CHAR_COLON } = require('internal/constants'); @@ -23,7 +23,7 @@ index 2a1a412fe7c55b8046b19860005e89a52ee3b816..70da676a40feeaa82d3a0ccd1c17882a const isWindows = process.platform === 'win32'; const relativeResolveCache = Object.create(null); -@@ -863,10 +870,12 @@ Module.prototype._compile = function(content, filename) { +@@ -951,10 +958,12 @@ Module.prototype._compile = function(content, filename) { if (requireDepth === 0) statCache = new Map(); if (inspectorWrapper) { result = inspectorWrapper(compiledWrapper, thisValue, exports, diff --git a/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch b/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch index 6a7a99b71c06a..85f9458ac315c 100644 --- a/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch +++ b/patches/node/refactor_allow_embedder_overriding_of_internal_fs_calls.patch @@ -5,10 +5,10 @@ Subject: refactor: allow embedder overriding of internal FS calls diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js -index 86b2164bb0d74a531acd0f01da5269642ea574cf..48a38a6b5054c6bf14df82a8cb3c41d4bfb7bf68 100644 +index 18acd9d2b64774efdb9261e69923a5ba320a7f0e..0f36697ce5ed0b5bb6df74ceca219574da7eea53 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js -@@ -65,6 +65,10 @@ if (ownsProcessState) { +@@ -63,6 +63,10 @@ if (ownsProcessState) { ); } @@ -20,10 +20,10 @@ index 86b2164bb0d74a531acd0f01da5269642ea574cf..48a38a6b5054c6bf14df82a8cb3c41d4 process.config = JSON.parse(internalBinding('native_module').config); diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js -index ffc7fb6fd5857b807198d4d26b7b899e63cde4a1..2a7ffbff213f23536b94664c3ecffa18eb812849 100644 +index 03247551074e1ad3cdd412e6adc0d865746225d1..18fdf148ccca5d92f10b78144eee39bdff7919d2 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js -@@ -37,10 +37,7 @@ const assert = require('internal/assert'); +@@ -42,10 +42,7 @@ const assert = require('internal/assert'); const fs = require('fs'); const internalFS = require('internal/fs/utils'); const path = require('path'); @@ -35,7 +35,7 @@ index ffc7fb6fd5857b807198d4d26b7b899e63cde4a1..2a7ffbff213f23536b94664c3ecffa18 const { safeGetenv } = internalBinding('credentials'); const { makeRequireFunction, -@@ -117,14 +117,12 @@ const relativeResolveCache = Object.create(null); +@@ -120,14 +117,12 @@ function enrichCJSError(err) { } function stat(filename) { @@ -51,7 +51,7 @@ index ffc7fb6fd5857b807198d4d26b7b899e63cde4a1..2a7ffbff213f23536b94664c3ecffa18 if (statCache !== null) statCache.set(filename, result); return result; } -@@ -233,7 +233,7 @@ function readPackage(requestPath) { +@@ -233,7 +228,7 @@ function readPackage(requestPath) { const existing = packageJsonCache.get(jsonPath); if (existing !== undefined) return existing; diff --git a/patches/node/refactor_transferrablemodule_is_deprecated_use_compiledwasmmodule.patch b/patches/node/refactor_transferrablemodule_is_deprecated_use_compiledwasmmodule.patch new file mode 100644 index 0000000000000..05e6918370720 --- /dev/null +++ b/patches/node/refactor_transferrablemodule_is_deprecated_use_compiledwasmmodule.patch @@ -0,0 +1,79 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Mon, 2 Dec 2019 17:25:38 -0800 +Subject: refactor: TransferrableModule is deprecated, use CompiledWasmModule + instead + +This will be upstreamed to nodejs/node + +diff --git a/src/node_messaging.cc b/src/node_messaging.cc +index 19065fdb7d1be5adb0f521dd27df08e5263fa40d..de92a9cc3d8cb57c2ce2a315f868b01ea9aba368 100644 +--- a/src/node_messaging.cc ++++ b/src/node_messaging.cc +@@ -58,7 +58,7 @@ class DeserializerDelegate : public ValueDeserializer::Delegate { + Environment* env, + const std::vector& message_ports, + const std::vector>& shared_array_buffers, +- const std::vector& wasm_modules) ++ const std::vector& wasm_modules) + : message_ports_(message_ports), + shared_array_buffers_(shared_array_buffers), + wasm_modules_(wasm_modules) {} +@@ -82,7 +82,7 @@ class DeserializerDelegate : public ValueDeserializer::Delegate { + MaybeLocal GetWasmModuleFromId( + Isolate* isolate, uint32_t transfer_id) override { + CHECK_LE(transfer_id, wasm_modules_.size()); +- return WasmModuleObject::FromTransferrableModule( ++ return WasmModuleObject::FromCompiledModule( + isolate, wasm_modules_[transfer_id]); + } + +@@ -91,7 +91,7 @@ class DeserializerDelegate : public ValueDeserializer::Delegate { + private: + const std::vector& message_ports_; + const std::vector>& shared_array_buffers_; +- const std::vector& wasm_modules_; ++ const std::vector& wasm_modules_; + }; + + } // anonymous namespace +@@ -181,7 +181,7 @@ void Message::AddMessagePort(std::unique_ptr&& data) { + message_ports_.emplace_back(std::move(data)); + } + +-uint32_t Message::AddWASMModule(WasmModuleObject::TransferrableModule&& mod) { ++uint32_t Message::AddWASMModule(v8::CompiledWasmModule&& mod) { + wasm_modules_.emplace_back(std::move(mod)); + return wasm_modules_.size() - 1; + } +@@ -264,7 +264,7 @@ class SerializerDelegate : public ValueSerializer::Delegate { + + Maybe GetWasmModuleTransferId( + Isolate* isolate, Local module) override { +- return Just(msg_->AddWASMModule(module->GetTransferrableModule())); ++ return Just(msg_->AddWASMModule(module->GetCompiledModule())); + } + + void Finish() { +diff --git a/src/node_messaging.h b/src/node_messaging.h +index 054521b0563c4256f65d6f01eeeb108e3a515d86..53c766da554452c635a3bdfb38b75c0492bbfb5e 100644 +--- a/src/node_messaging.h ++++ b/src/node_messaging.h +@@ -58,7 +58,7 @@ class Message : public MemoryRetainer { + void AddMessagePort(std::unique_ptr&& data); + // Internal method of Message that is called when a new WebAssembly.Module + // object is encountered in the incoming value's structure. +- uint32_t AddWASMModule(v8::WasmModuleObject::TransferrableModule&& mod); ++ uint32_t AddWASMModule(v8::CompiledWasmModule&& mod); + + // The MessagePorts that will be transferred, as recorded by Serialize(). + // Used for warning user about posting the target MessagePort to itself, +@@ -77,7 +77,7 @@ class Message : public MemoryRetainer { + std::vector> array_buffer_contents_; + std::vector shared_array_buffers_; + std::vector> message_ports_; +- std::vector wasm_modules_; ++ std::vector wasm_modules_; + + friend class MessagePort; + }; diff --git a/patches/node/revert_crypto_add_oaeplabel_option.patch b/patches/node/revert_crypto_add_oaeplabel_option.patch index 0ff0e73252539..cbe266930929b 100644 --- a/patches/node/revert_crypto_add_oaeplabel_option.patch +++ b/patches/node/revert_crypto_add_oaeplabel_option.patch @@ -4,14 +4,14 @@ Date: Tue, 15 Oct 2019 11:30:27 -0700 Subject: Revert "crypto: add oaepLabel option" This partially reverts https://github.com/nodejs/node/pull/29489. -The BoringSSL incompatibilities (OPENSSL_memdup) will be shimmed in and this should +The BoringSSL incompatibilities (OPENSSL_memdup) will be shimmed in and this should be removed when the associated update is rolled into Chromium. diff --git a/src/node_crypto.cc b/src/node_crypto.cc -index 63dd6a1863..1ca4c75d64 100644 +index cc03ab2e175adbd0a8558097c2df4098c22e045a..dcb6e2c5a2a634c816b744e8fc50f99d885674f3 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc -@@ -5188,17 +5188,6 @@ bool PublicKeyCipher::Cipher(Environment* env, +@@ -5232,17 +5232,6 @@ bool PublicKeyCipher::Cipher(Environment* env, return false; } diff --git a/patches/node/revert_tls_add_option_to_override_signature_algorithms.patch b/patches/node/revert_tls_add_option_to_override_signature_algorithms.patch index a0f41c01e5aab..0614f13827d58 100644 --- a/patches/node/revert_tls_add_option_to_override_signature_algorithms.patch +++ b/patches/node/revert_tls_add_option_to_override_signature_algorithms.patch @@ -8,7 +8,7 @@ Upstreamed at https://boringssl-review.googlesource.com/c/boringssl/+/38404 and can be removed when that is merged and rolled into Chromium. diff --git a/src/node_crypto.cc b/src/node_crypto.cc -index 870456216983c2685c53580e60c44aa4dd3f7267..63dd6a186366baad660ee8e38401ba842c6ddb17 100644 +index 9250246a453a89f29bb607ebcc6c9d07642c3561..cc03ab2e175adbd0a8558097c2df4098c22e045a 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -470,7 +470,6 @@ void SecureContext::Initialize(Environment* env, Local target) { @@ -141,7 +141,7 @@ index 870456216983c2685c53580e60c44aa4dd3f7267..63dd6a186366baad660ee8e38401ba84 void SSLWrap::GetProtocol(const FunctionCallbackInfo& args) { Base* w; diff --git a/src/node_crypto.h b/src/node_crypto.h -index fb21077132dec0900118d178605c9b93cc458105..31e7da7da9daf2ecf965dc38df24272f838ba1a0 100644 +index 560694bfd2c1e2ce999b61682b0f3ea757f4f69e..0a9c684fa3d3092294b40c4df82d32f98ac6dd79 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -129,7 +129,6 @@ class SecureContext : public BaseObject { diff --git a/patches/node/src_disable_node_use_v8_platform_in_node_options.patch b/patches/node/src_disable_node_use_v8_platform_in_node_options.patch index 758af2f813590..cd76a3b2ce368 100644 --- a/patches/node/src_disable_node_use_v8_platform_in_node_options.patch +++ b/patches/node/src_disable_node_use_v8_platform_in_node_options.patch @@ -6,7 +6,7 @@ Subject: src: Disable NODE_USE_V8_PLATFORM in node_options Electron does not set NODE_USE_V8_PLATFORM. If inspector is enabled when NODE_USE_V8_PLATFORM is false, then there's an error. We want to ignore that, use our own v8 platform but still use the node inspector diff --git a/src/node_options.cc b/src/node_options.cc -index b9d6108c5db800465e6c7156ad1c28002c33261b..01dddb979042c098326a8b1ff37397642733f49b 100644 +index 03919d450b3373983d647165ee93c006eb5f2902..b9db121250c77e1bc8c35af336361ee444c271e4 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -33,7 +33,7 @@ DebugOptions& DebugOptions::operator=(const DebugOptions&) = default; diff --git a/patches/perfetto/.patches b/patches/perfetto/.patches deleted file mode 100644 index c2c8ef7f493f4..0000000000000 --- a/patches/perfetto/.patches +++ /dev/null @@ -1,3 +0,0 @@ -revert_fix_chrome_roll_update_protoc_host_toolchain_rules.patch -revert_make_trace_processor_a_host-only_target.patch -metatrace_remove_memset_and_trivial_ctor_assumption.patch diff --git a/patches/perfetto/metatrace_remove_memset_and_trivial_ctor_assumption.patch b/patches/perfetto/metatrace_remove_memset_and_trivial_ctor_assumption.patch deleted file mode 100644 index 86041ae606174..0000000000000 --- a/patches/perfetto/metatrace_remove_memset_and_trivial_ctor_assumption.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Primiano Tucci -Date: Thu, 3 Oct 2019 16:24:52 +0100 -Subject: metatrace: remove memset and trivial-ctor assumption - -Turns out that on MSVC std::atomic is not trivially constructible -(although I think is still a plain old int, it just fails the check). -Fall back on resetting each element individually. -Thankfully the compiler can see through and eventually figures out -it can do a memset: https://godbolt.org/z/wMre8O - -Bug: chromium:1010616 -Change-Id: I971ff888306d6bdbaf6e6b886f9ca506ddc1b30a - -diff --git a/include/perfetto/ext/base/metatrace.h b/include/perfetto/ext/base/metatrace.h -index 3858f68ec5eaf130aafa7d33f52a00e370395204..2c587c3fc63093f71a05e4c757def5c6384bf703 100644 ---- a/include/perfetto/ext/base/metatrace.h -+++ b/include/perfetto/ext/base/metatrace.h -@@ -116,22 +116,33 @@ struct Record { - timestamp_ns_high = static_cast(diff >> 32); - } - -+ // We can't just memset() this class because on MSVC std::atomic<> is not -+ // trivially constructible anymore. Also std::atomic<> has a deleted copy -+ // constructor so we cant just do "*this = Record()" either. -+ // See http://bit.ly/339Jlzd . -+ void clear() { -+ this->~Record(); -+ new (this) Record(); -+ } -+ - // This field holds the type (counter vs event) in the MSB and event ID (as - // defined in metatrace_events.h) in the lowest 15 bits. It is also used also - // as a linearization point: this is always written after all the other - // fields with a release-store. This is so the reader can determine whether it - // can safely process the other event fields after a load-acquire. -- std::atomic type_and_id; -+ std::atomic type_and_id{}; - - // Timestamp is stored as a 48-bits value diffed against g_enabled_timestamp. - // This gives us 78 hours from Enabled(). -- uint16_t timestamp_ns_high; -- uint32_t timestamp_ns_low; -+ uint16_t timestamp_ns_high = 0; -+ uint32_t timestamp_ns_low = 0; - -- uint32_t thread_id; -+ uint32_t thread_id = 0; - - union { -- uint32_t duration_ns; // If type == event. -+ // Only one of the two elements can be zero initialized, clang complains -+ // about "initializing multiple members of union" otherwise. -+ uint32_t duration_ns = 0; // If type == event. - int32_t counter_value; // If type == counter. - }; - }; -diff --git a/src/base/metatrace.cc b/src/base/metatrace.cc -index 9ef2c68777c5d497d92b12d52df4df2454feda02..67d167c8d07bc8701d261c56d11ba17afcb6ec8a 100644 ---- a/src/base/metatrace.cc -+++ b/src/base/metatrace.cc -@@ -84,15 +84,9 @@ void Disable() { - - // static - void RingBuffer::Reset() { -- static_assert(PERFETTO_IS_TRIVIALLY_CONSTRUCTIBLE(Record) && -- std::is_trivially_destructible::value, -- "Record must be trivial"); -- // Cast pointers to void* to suppress "-Wclass-memaccess" from gcc, which -- // triggers as we're doing a raw memory set for a class (Record) that doesn't -- // have a copy assignment operator (due to the atomic |type_and_id|). -- memset(static_cast(records_.data()), 0, sizeof(records_)); -- memset(static_cast(&bankruptcy_record_), 0, -- sizeof(bankruptcy_record_)); -+ bankruptcy_record_.clear(); -+ for (Record& record : records_) -+ record.clear(); - wr_index_ = 0; - rd_index_ = 0; - has_overruns_ = false; diff --git a/patches/quiche/.patches b/patches/quiche/.patches deleted file mode 100644 index ff63d53aaae8c..0000000000000 --- a/patches/quiche/.patches +++ /dev/null @@ -1 +0,0 @@ -include_ostream_in_quic_ip_address_h.patch diff --git a/patches/quiche/include_ostream_in_quic_ip_address_h.patch b/patches/quiche/include_ostream_in_quic_ip_address_h.patch deleted file mode 100644 index 530a5e19ab3ad..0000000000000 --- a/patches/quiche/include_ostream_in_quic_ip_address_h.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: vasilvv -Date: Thu, 3 Oct 2019 09:37:10 -0700 -Subject: [build] Include ostream in quic_ip_address.h - -Explicitly #include in quic/platform/api/quic_ip_address.h, since std::ostream is referenced in it. In the C++ STL shipped with Visual Studio 2019, none of the headers included in quic_ip_address.h ends up including the required templates, which caused a compile error. - -Patch by Jaime Bernardo : https://quiche-review.googlesource.com/c/quiche/+/10280 - -gfe-relnote: n/a (no functional change) -PiperOrigin-RevId: 272678937 -Change-Id: I304ed7ad7f114924268ef832551fb2ddcba73402 - -diff --git a/quic/platform/api/quic_ip_address.h b/quic/platform/api/quic_ip_address.h -index e1a1076cbcbd0bfe72e69b4c0fefe2b21ec4426c..3585970d9d50f2b026616e758576dd466044c39b 100644 ---- a/quic/platform/api/quic_ip_address.h -+++ b/quic/platform/api/quic_ip_address.h -@@ -15,6 +15,7 @@ - #include - #endif - -+#include - #include - - #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" diff --git a/patches/usrsctp/.patches b/patches/usrsctp/.patches new file mode 100644 index 0000000000000..3b257adbc6487 --- /dev/null +++ b/patches/usrsctp/.patches @@ -0,0 +1 @@ +cherry-pick-e89fe66d0473.patch diff --git a/patches/usrsctp/cherry-pick-e89fe66d0473.patch b/patches/usrsctp/cherry-pick-e89fe66d0473.patch new file mode 100644 index 0000000000000..717a44a031aef --- /dev/null +++ b/patches/usrsctp/cherry-pick-e89fe66d0473.patch @@ -0,0 +1,57 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Michael Tuexen +Date: Fri, 10 Apr 2020 00:43:45 +0000 +Subject: Improve input validation when processing AUTH chunks + +Cherry picked from: +https://chromium.googlesource.com/external/github.com/sctplab/usrsctp/+/b218e8957d7af0b07ee3ca74caded05ada8d1df9 +https://chromium.googlesource.com/external/github.com/sctplab/usrsctp/+/e89fe66d04735dcfc6bfda1648fbe68008da6277 + +Bug: 1073602 + +diff --git a/usrsctplib/netinet/sctp_input.c b/usrsctplib/netinet/sctp_input.c +index c62bb8506dc0fb86112f35e0b66d08c198e8f9bd..4e53dfb27ec597a8f6ac99c329bc80b9d7306836 100755 +--- a/usrsctplib/netinet/sctp_input.c ++++ b/usrsctplib/netinet/sctp_input.c +@@ -2178,7 +2178,7 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, + int init_offset, initack_offset, initack_limit; + int retval; + int error = 0; +- uint8_t auth_chunk_buf[SCTP_PARAM_BUFFER_SIZE]; ++ uint8_t auth_chunk_buf[SCTP_CHUNK_BUFFER_SIZE]; + #if defined(__APPLE__) || defined(SCTP_SO_LOCK_TESTING) + struct socket *so; + +@@ -2373,8 +2373,11 @@ sctp_process_cookie_new(struct mbuf *m, int iphlen, int offset, + if (auth_skipped) { + struct sctp_auth_chunk *auth; + +- auth = (struct sctp_auth_chunk *) +- sctp_m_getptr(m, auth_offset, auth_len, auth_chunk_buf); ++ if (auth_len <= SCTP_CHUNK_BUFFER_SIZE) { ++ auth = (struct sctp_auth_chunk *)sctp_m_getptr(m, auth_offset, auth_len, auth_chunk_buf); ++ } else { ++ auth = NULL; ++ } + if ((auth == NULL) || sctp_handle_auth(stcb, auth, m, auth_offset)) { + /* auth HMAC failed, dump the assoc and packet */ + SCTPDBG(SCTP_DEBUG_AUTH1, +@@ -4846,11 +4849,13 @@ sctp_process_control(struct mbuf *m, int iphlen, int *offset, int length, + if (auth_skipped && (stcb != NULL)) { + struct sctp_auth_chunk *auth; + +- auth = (struct sctp_auth_chunk *) +- sctp_m_getptr(m, auth_offset, +- auth_len, chunk_buf); +- got_auth = 1; +- auth_skipped = 0; ++ if (auth_len <= SCTP_CHUNK_BUFFER_SIZE) { ++ auth = (struct sctp_auth_chunk *)sctp_m_getptr(m, auth_offset, auth_len, chunk_buf); ++ got_auth = 1; ++ auth_skipped = 0; ++ } else { ++ auth = NULL; ++ } + if ((auth == NULL) || sctp_handle_auth(stcb, auth, m, + auth_offset)) { + /* auth HMAC failed so dump it */ diff --git a/patches/v8/.patches b/patches/v8/.patches index 2e3b1a2799568..7bdbcfeb87985 100644 --- a/patches/v8/.patches +++ b/patches/v8/.patches @@ -7,4 +7,7 @@ export_symbols_needed_for_windows_build.patch workaround_an_undefined_symbol_error.patch do_not_export_private_v8_symbols_on_windows.patch revert_cleanup_switch_offset_of_to_offsetof_where_possible.patch -ptr-compr_temporarily_disable_31_bit_smis_on_64-bit_architectures.patch +objects_fix_memory_leak_in_prototypeusers_add.patch +fix_build_deprecated_attirbute_for_older_msvc_versions.patch +use_context_of_then_function_for_promiseresolvethenablejob.patch +merged_regexp_reserve_space_for_all_registers_in_interpreter.patch diff --git a/patches/v8/add_realloc.patch b/patches/v8/add_realloc.patch index 45d904a2cc945..d71e585638e53 100644 --- a/patches/v8/add_realloc.patch +++ b/patches/v8/add_realloc.patch @@ -12,10 +12,10 @@ when we override ReallocateBufferMemory, so we therefore need to implement Realloc on the v8 side. diff --git a/include/v8.h b/include/v8.h -index 24052f25bf7499743d1bf59846325274c3fd81c0..c5f0436eb771b6ca86fdf8e0dc195221c6b481f4 100644 +index caae20abef88b7330823ce74218bd300175d1c9a..9d73ba87bd938a3d04d2327de93d48c85ff70d1e 100644 --- a/include/v8.h +++ b/include/v8.h -@@ -4748,6 +4748,13 @@ class V8_EXPORT ArrayBuffer : public Object { +@@ -4919,6 +4919,13 @@ class V8_EXPORT ArrayBuffer : public Object { */ virtual void* AllocateUninitialized(size_t length) = 0; @@ -30,10 +30,10 @@ index 24052f25bf7499743d1bf59846325274c3fd81c0..c5f0436eb771b6ca86fdf8e0dc195221 * Free the memory block of size |length|, pointed to by |data|. * That memory is guaranteed to be previously allocated by |Allocate|. diff --git a/src/api/api.cc b/src/api/api.cc -index b03cc11532e0ec1d0e047bd794d15416cb343670..e9e46fd117dbf67a0fb7bdf560e152daa8624ff8 100644 +index f64538ec1c47f62caf1ff6383b6b3812bb827c67..d21545a7f1a5ca5f593809d7a6475da20651c116 100644 --- a/src/api/api.cc +++ b/src/api/api.cc -@@ -518,6 +518,10 @@ void V8::SetSnapshotDataBlob(StartupData* snapshot_blob) { +@@ -516,6 +516,10 @@ void V8::SetSnapshotDataBlob(StartupData* snapshot_blob) { i::V8::SetSnapshotBlob(snapshot_blob); } diff --git a/patches/v8/build_gn.patch b/patches/v8/build_gn.patch index 3f28cc361533b..a4708a33b4c32 100644 --- a/patches/v8/build_gn.patch +++ b/patches/v8/build_gn.patch @@ -5,10 +5,10 @@ Subject: build_gn.patch diff --git a/BUILD.gn b/BUILD.gn -index 7e683f86e529316f923b8ff2b6fe5ed94f22420a..b0e242d6c369185172184c1f4a88247aa1e0896c 100644 +index 90b7e33972af8befd24b44e86a09f7e442bca881..11cf04af2193c486eb49cc24dff54a4085ad7c16 100644 --- a/BUILD.gn +++ b/BUILD.gn -@@ -284,7 +284,7 @@ config("internal_config") { +@@ -290,7 +290,7 @@ config("internal_config") { ":v8_header_features", ] @@ -17,7 +17,7 @@ index 7e683f86e529316f923b8ff2b6fe5ed94f22420a..b0e242d6c369185172184c1f4a88247a defines += [ "BUILDING_V8_SHARED" ] } } -@@ -3834,7 +3834,7 @@ if (current_toolchain == v8_generator_toolchain) { +@@ -3810,7 +3810,7 @@ if (current_toolchain == v8_generator_toolchain) { "src/interpreter/bytecodes.h", ] @@ -26,7 +26,7 @@ index 7e683f86e529316f923b8ff2b6fe5ed94f22420a..b0e242d6c369185172184c1f4a88247a deps = [ ":v8_libbase", -@@ -3865,6 +3865,8 @@ if (v8_use_snapshot && current_toolchain == v8_snapshot_toolchain) { +@@ -3843,6 +3843,8 @@ if (current_toolchain == v8_snapshot_toolchain) { configs = [ ":internal_config" ] diff --git a/patches/v8/dcheck.patch b/patches/v8/dcheck.patch index 9502b62a0e9bd..29412e01813d0 100644 --- a/patches/v8/dcheck.patch +++ b/patches/v8/dcheck.patch @@ -5,10 +5,10 @@ Subject: dcheck.patch diff --git a/src/api/api.cc b/src/api/api.cc -index 11ca1064085568986b8d21250dd8617f0d03f652..39dc796b72553259b167721a4fd51f3f51ef1358 100644 +index 4e80aae91a871581261b04ec2a18781ef7836cb8..6228b23b890af55de1c5053d5b3b7b0d5142fa9a 100644 --- a/src/api/api.cc +++ b/src/api/api.cc -@@ -8501,7 +8501,7 @@ void Isolate::SetPromiseRejectCallback(PromiseRejectCallback callback) { +@@ -8757,7 +8757,7 @@ void Isolate::SetPromiseRejectCallback(PromiseRejectCallback callback) { } void Isolate::RunMicrotasks() { @@ -18,10 +18,10 @@ index 11ca1064085568986b8d21250dd8617f0d03f652..39dc796b72553259b167721a4fd51f3f isolate->default_microtask_queue()->RunMicrotasks(isolate); } diff --git a/src/heap/heap.cc b/src/heap/heap.cc -index 92265dd7665887cea46be407180e42de4985f8fd..4268f289c22953c386e28ed3968bedf80e595914 100644 +index b7868e5e62f6b13f5e75f9a463b9b71a63612c5c..47ebb3c64f2823bfd541ec7a5f438e953f5d5423 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc -@@ -5312,9 +5312,9 @@ void Heap::TearDown() { +@@ -5356,9 +5356,9 @@ void Heap::TearDown() { void Heap::AddGCPrologueCallback(v8::Isolate::GCCallbackWithData callback, GCType gc_type, void* data) { DCHECK_NOT_NULL(callback); diff --git a/patches/v8/deps_provide_more_v8_backwards_compatibility.patch b/patches/v8/deps_provide_more_v8_backwards_compatibility.patch index 098d78aa4522c..c7c762aa13db9 100644 --- a/patches/v8/deps_provide_more_v8_backwards_compatibility.patch +++ b/patches/v8/deps_provide_more_v8_backwards_compatibility.patch @@ -22,128 +22,129 @@ Reviewed-By: Yang Guo Reviewed-By: Michaël Zasso diff --git a/include/v8.h b/include/v8.h -index c5f0436eb771b6ca86fdf8e0dc195221c6b481f4..4c93d92fe36c6712a239c47e42bcb2cb6992cb57 100644 +index 9d73ba87bd938a3d04d2327de93d48c85ff70d1e..72bcfea94c7d327b2e2f5e2115cac24105a820c7 100644 --- a/include/v8.h +++ b/include/v8.h -@@ -1195,6 +1195,10 @@ class V8_EXPORT PrimitiveArray { +@@ -1330,6 +1330,10 @@ class V8_EXPORT PrimitiveArray { public: static Local New(Isolate* isolate, int length); int Length() const; -+ V8_DEPRECATED("Use Isolate* version", -+ void Set(int index, Local item)); -+ V8_DEPRECATED("Use Isolate* version", -+ Local Get(int index)); ++ V8_DEPRECATED("Use Isolate* version") ++ void Set(int index, Local item); ++ V8_DEPRECATED("Use Isolate* version") ++ Local Get(int index); void Set(Isolate* isolate, int index, Local item); Local Get(Isolate* isolate, int index); }; -@@ -1935,6 +1939,8 @@ class V8_EXPORT StackTrace { +@@ -2085,6 +2089,8 @@ class V8_EXPORT StackTrace { /** * Returns a StackFrame at a particular index. */ -+ V8_DEPRECATED("Use Isolate version", -+ Local GetFrame(uint32_t index) const); ++ V8_DEPRECATED("Use Isolate version") ++ Local GetFrame(uint32_t index) const; Local GetFrame(Isolate* isolate, uint32_t index) const; /** -@@ -2629,6 +2635,13 @@ class V8_EXPORT Value : public Data { +@@ -2781,6 +2787,15 @@ class V8_EXPORT Value : public Data { Local ToBoolean(Isolate* isolate) const; -+ inline V8_DEPRECATED("Use maybe version", -+ Local ToBoolean() const); -+ inline V8_DEPRECATED("Use maybe version", Local ToString() const); -+ inline V8_DEPRECATED("Use maybe version", Local ToObject() const); -+ inline V8_DEPRECATED("Use maybe version", -+ Local ToInteger() const); ++ V8_DEPRECATED("Use maybe version") ++ inline Local ToBoolean() const; ++ V8_DEPRECATED("Use maybe version") ++ inline Local ToString() const; ++ V8_DEPRECATED("Use maybe version") ++ inline Local ToObject() const; ++ V8_DEPRECATED("Use maybe version") ++ inline Local ToInteger() const; + /** * Attempts to convert a string to an array index. * Returns an empty handle if the conversion fails. -@@ -2645,7 +2658,14 @@ class V8_EXPORT Value : public Data { +@@ -2797,7 +2812,14 @@ class V8_EXPORT Value : public Data { Local context) const; V8_WARN_UNUSED_RESULT Maybe Int32Value(Local context) const; -+ V8_DEPRECATED("Use maybe version", bool BooleanValue() const); -+ V8_DEPRECATED("Use maybe version", double NumberValue() const); -+ V8_DEPRECATED("Use maybe version", int64_t IntegerValue() const); -+ V8_DEPRECATED("Use maybe version", uint32_t Uint32Value() const); -+ V8_DEPRECATED("Use maybe version", int32_t Int32Value() const); ++ V8_DEPRECATED("Use maybe version") bool BooleanValue() const; ++ V8_DEPRECATED("Use maybe version") double NumberValue() const; ++ V8_DEPRECATED("Use maybe version") int64_t IntegerValue() const; ++ V8_DEPRECATED("Use maybe version") uint32_t Uint32Value() const; ++ V8_DEPRECATED("Use maybe version") int32_t Int32Value() const; + /** JS == */ -+ V8_DEPRECATED("Use maybe version", bool Equals(Local that) const); ++ V8_DEPRECATED("Use maybe version") bool Equals(Local that) const; V8_WARN_UNUSED_RESULT Maybe Equals(Local context, Local that) const; bool StrictEquals(Local that) const; -@@ -2752,6 +2772,8 @@ class V8_EXPORT String : public Name { +@@ -2903,6 +2925,8 @@ class V8_EXPORT String : public Name { * Returns the number of bytes in the UTF-8 encoded * representation of this string. */ -+ V8_DEPRECATED("Use Isolate version instead", int Utf8Length() const); ++ V8_DEPRECATED("Use Isolate version instead") int Utf8Length() const; + int Utf8Length(Isolate* isolate) const; /** -@@ -2808,12 +2830,23 @@ class V8_EXPORT String : public Name { +@@ -2959,12 +2983,22 @@ class V8_EXPORT String : public Name { // 16-bit character codes. int Write(Isolate* isolate, uint16_t* buffer, int start = 0, int length = -1, int options = NO_OPTIONS) const; -+ V8_DEPRECATED("Use Isolate* version", -+ int Write(uint16_t* buffer, int start = 0, int length = -1, -+ int options = NO_OPTIONS) const); ++ V8_DEPRECATED("Use Isolate* version") ++ int Write(uint16_t* buffer, int start = 0, int length = -1, ++ int options = NO_OPTIONS) const; // One byte characters. int WriteOneByte(Isolate* isolate, uint8_t* buffer, int start = 0, int length = -1, int options = NO_OPTIONS) const; -+ V8_DEPRECATED("Use Isolate* version", -+ int WriteOneByte(uint8_t* buffer, int start = 0, -+ int length = -1, int options = NO_OPTIONS) -+ const); ++ V8_DEPRECATED("Use Isolate* version") ++ int WriteOneByte(uint8_t* buffer, int start = 0, ++ int length = -1, int options = NO_OPTIONS) const; // UTF-8 encoded characters. int WriteUtf8(Isolate* isolate, char* buffer, int length = -1, int* nchars_ref = nullptr, int options = NO_OPTIONS) const; -+ V8_DEPRECATED("Use Isolate* version", -+ int WriteUtf8(char* buffer, int length = -1, -+ int* nchars_ref = nullptr, -+ int options = NO_OPTIONS) const); ++ V8_DEPRECATED("Use Isolate* version") ++ int WriteUtf8(char* buffer, int length = -1, ++ int* nchars_ref = nullptr, ++ int options = NO_OPTIONS) const; /** * A zero length string. -@@ -2981,6 +3014,9 @@ class V8_EXPORT String : public Name { +@@ -3132,6 +3166,8 @@ class V8_EXPORT String : public Name { */ static Local Concat(Isolate* isolate, Local left, Local right); -+ static V8_DEPRECATED("Use Isolate* version", -+ Local Concat(Local left, -+ Local right)); ++ V8_DEPRECATED("Use Isolate* version") ++ static Local Concat(Local left, Local right); /** * Creates a new external string using the data defined in the given -@@ -3045,6 +3081,8 @@ class V8_EXPORT String : public Name { +@@ -3196,6 +3232,8 @@ class V8_EXPORT String : public Name { */ class V8_EXPORT Utf8Value { public: -+ V8_DEPRECATED("Use Isolate version", -+ explicit Utf8Value(Local obj)); ++ V8_DEPRECATED("Use Isolate version") ++ explicit Utf8Value(Local obj); Utf8Value(Isolate* isolate, Local obj); ~Utf8Value(); char* operator*() { return str_; } -@@ -3068,6 +3106,7 @@ class V8_EXPORT String : public Name { +@@ -3219,6 +3257,8 @@ class V8_EXPORT String : public Name { */ class V8_EXPORT Value { public: -+ V8_DEPRECATED("Use Isolate version", explicit Value(Local obj)); ++ V8_DEPRECATED("Use Isolate version") ++ explicit Value(Local obj); Value(Isolate* isolate, Local obj); ~Value(); uint16_t* operator*() { return str_; } -@@ -5407,6 +5446,8 @@ class V8_EXPORT BooleanObject : public Object { +@@ -5706,6 +5746,8 @@ class V8_EXPORT BooleanObject : public Object { class V8_EXPORT StringObject : public Object { public: static Local New(Isolate* isolate, Local value); -+ V8_DEPRECATED("Use Isolate* version", -+ static Local New(Local value)); ++ V8_DEPRECATED("Use Isolate* version") ++ static Local New(Local value); Local ValueOf() const; -@@ -10741,6 +10782,29 @@ template Value* Value::Cast(T* value) { +@@ -11234,6 +11276,29 @@ template Value* Value::Cast(T* value) { } @@ -174,10 +175,10 @@ index c5f0436eb771b6ca86fdf8e0dc195221c6b481f4..4c93d92fe36c6712a239c47e42bcb2cb #ifdef V8_ENABLE_CHECKS CheckCast(value); diff --git a/src/api/api.cc b/src/api/api.cc -index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f0d03f652 100644 +index d21545a7f1a5ca5f593809d7a6475da20651c116..4e80aae91a871581261b04ec2a18781ef7836cb8 100644 --- a/src/api/api.cc +++ b/src/api/api.cc -@@ -2206,6 +2206,10 @@ int PrimitiveArray::Length() const { +@@ -2200,6 +2200,10 @@ int PrimitiveArray::Length() const { return array->length(); } @@ -188,7 +189,7 @@ index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f void PrimitiveArray::Set(Isolate* v8_isolate, int index, Local item) { i::Isolate* isolate = reinterpret_cast(v8_isolate); -@@ -2219,6 +2223,10 @@ void PrimitiveArray::Set(Isolate* v8_isolate, int index, +@@ -2213,6 +2217,10 @@ void PrimitiveArray::Set(Isolate* v8_isolate, int index, array->set(index, *i_item); } @@ -199,7 +200,7 @@ index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f Local PrimitiveArray::Get(Isolate* v8_isolate, int index) { i::Isolate* isolate = reinterpret_cast(v8_isolate); i::Handle array = Utils::OpenHandle(this); -@@ -2960,6 +2968,10 @@ void Message::PrintCurrentStackTrace(Isolate* isolate, FILE* out) { +@@ -2996,6 +3004,10 @@ void Message::PrintCurrentStackTrace(Isolate* isolate, FILE* out) { // --- S t a c k T r a c e --- @@ -210,7 +211,7 @@ index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f Local StackTrace::GetFrame(Isolate* v8_isolate, uint32_t index) const { i::Isolate* isolate = reinterpret_cast(v8_isolate); -@@ -3547,6 +3559,34 @@ MaybeLocal Value::ToBigInt(Local context) const { +@@ -3583,6 +3595,34 @@ MaybeLocal Value::ToBigInt(Local context) const { RETURN_ESCAPED(result); } @@ -245,7 +246,7 @@ index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f bool Value::BooleanValue(Isolate* v8_isolate) const { return Utils::OpenHandle(this)->BooleanValue( reinterpret_cast(v8_isolate)); -@@ -3894,6 +3934,11 @@ MaybeLocal Value::ToArrayIndex(Local context) const { +@@ -3972,6 +4012,11 @@ MaybeLocal Value::ToArrayIndex(Local context) const { return Local(); } @@ -257,7 +258,7 @@ index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f Maybe Value::Equals(Local context, Local that) const { i::Isolate* isolate = Utils::OpenHandle(*context)->GetIsolate(); auto self = Utils::OpenHandle(this); -@@ -5111,6 +5156,10 @@ bool String::ContainsOnlyOneByte() const { +@@ -5189,6 +5234,10 @@ bool String::ContainsOnlyOneByte() const { return helper.Check(*str); } @@ -268,7 +269,7 @@ index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f int String::Utf8Length(Isolate* isolate) const { i::Handle str = Utils::OpenHandle(this); str = i::String::Flatten(reinterpret_cast(isolate), str); -@@ -5263,6 +5312,14 @@ static int WriteUtf8Impl(i::Vector string, char* write_start, +@@ -5341,6 +5390,14 @@ static int WriteUtf8Impl(i::Vector string, char* write_start, } } // anonymous namespace @@ -283,7 +284,7 @@ index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f int String::WriteUtf8(Isolate* v8_isolate, char* buffer, int capacity, int* nchars_ref, int options) const { i::Handle str = Utils::OpenHandle(this); -@@ -5301,6 +5358,17 @@ static inline int WriteHelper(i::Isolate* isolate, const String* string, +@@ -5379,6 +5436,17 @@ static inline int WriteHelper(i::Isolate* isolate, const String* string, return end - start; } @@ -301,7 +302,7 @@ index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f int String::WriteOneByte(Isolate* isolate, uint8_t* buffer, int start, int length, int options) const { return WriteHelper(reinterpret_cast(isolate), this, buffer, -@@ -6218,6 +6286,11 @@ MaybeLocal String::NewFromTwoByte(Isolate* isolate, +@@ -6323,6 +6391,11 @@ MaybeLocal String::NewFromTwoByte(Isolate* isolate, return result; } @@ -313,7 +314,7 @@ index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f Local v8::String::Concat(Isolate* v8_isolate, Local left, Local right) { i::Isolate* isolate = reinterpret_cast(v8_isolate); -@@ -6488,6 +6561,10 @@ bool v8::BooleanObject::ValueOf() const { +@@ -6593,6 +6666,10 @@ bool v8::BooleanObject::ValueOf() const { return js_primitive_wrapper->value().IsTrue(isolate); } @@ -324,7 +325,7 @@ index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f Local v8::StringObject::New(Isolate* v8_isolate, Local value) { i::Handle string = Utils::OpenHandle(*value); -@@ -8874,6 +8951,9 @@ bool MicrotasksScope::IsRunningMicrotasks(Isolate* v8_isolate) { +@@ -9137,6 +9214,9 @@ bool MicrotasksScope::IsRunningMicrotasks(Isolate* v8_isolate) { return microtask_queue->IsRunningMicrotasks(); } @@ -334,7 +335,7 @@ index e9e46fd117dbf67a0fb7bdf560e152daa8624ff8..11ca1064085568986b8d21250dd8617f String::Utf8Value::Utf8Value(v8::Isolate* isolate, v8::Local obj) : str_(nullptr), length_(0) { if (obj.IsEmpty()) return; -@@ -8891,6 +8971,9 @@ String::Utf8Value::Utf8Value(v8::Isolate* isolate, v8::Local obj) +@@ -9154,6 +9234,9 @@ String::Utf8Value::Utf8Value(v8::Isolate* isolate, v8::Local obj) String::Utf8Value::~Utf8Value() { i::DeleteArray(str_); } diff --git a/patches/v8/do_not_export_private_v8_symbols_on_windows.patch b/patches/v8/do_not_export_private_v8_symbols_on_windows.patch index 91d3c1564a4dd..a515a0da35cc1 100644 --- a/patches/v8/do_not_export_private_v8_symbols_on_windows.patch +++ b/patches/v8/do_not_export_private_v8_symbols_on_windows.patch @@ -12,10 +12,10 @@ This patch can be safely removed if, when it is removed, `node.lib` does not contain any standard C++ library exports (e.g. `std::ostringstream`). diff --git a/BUILD.gn b/BUILD.gn -index 62d1548a10e367795ec3d649fe1cf112b278ec19..8d49e29cb4ab4d7d3373cef20e9033b0ac4ca07a 100644 +index b1b2d17d19a706ba1a035484625de0000214c7f9..bb51f495489c8b52416744cb86111866a4bb7e5d 100644 --- a/BUILD.gn +++ b/BUILD.gn -@@ -284,6 +284,10 @@ config("internal_config") { +@@ -290,6 +290,10 @@ config("internal_config") { ":v8_header_features", ] @@ -27,10 +27,10 @@ index 62d1548a10e367795ec3d649fe1cf112b278ec19..8d49e29cb4ab4d7d3373cef20e9033b0 defines += [ "BUILDING_V8_SHARED" ] } diff --git a/src/base/macros.h b/src/base/macros.h -index 329c5a90636aa5ef587c743ed20b56c2c2195037..1d43c165341fcf089bf8d5566394d1b80d3c8b22 100644 +index 5f52a9893e6a564640bde19d6cf376b74ab9a429..b7f3c4e837947311cef6373bd6a7c1b589da9dc9 100644 --- a/src/base/macros.h +++ b/src/base/macros.h -@@ -395,13 +395,17 @@ bool is_inbounds(float_t v) { +@@ -399,13 +399,17 @@ bool is_inbounds(float_t v) { #ifdef V8_OS_WIN // Setup for Windows shared library export. diff --git a/patches/v8/export_symbols_needed_for_windows_build.patch b/patches/v8/export_symbols_needed_for_windows_build.patch index 4a01d116152b7..a135dba26f7a7 100644 --- a/patches/v8/export_symbols_needed_for_windows_build.patch +++ b/patches/v8/export_symbols_needed_for_windows_build.patch @@ -6,10 +6,10 @@ Subject: Export symbols needed for Windows build These symbols are required to build v8 with BUILD_V8_SHARED on Windows. diff --git a/src/objects/objects.h b/src/objects/objects.h -index b4e78a19377df73051c2e6e650d5b654ca435c6b..93de4136f2a0498038d17866f99f72ea8e217b2f 100644 +index 4f06c40e11a0fc85adafcddb38e21594ba479910..6b3f5188605b247b1405a62bd9f4e5e410ab3c2b 100644 --- a/src/objects/objects.h +++ b/src/objects/objects.h -@@ -796,7 +796,7 @@ enum class KeyCollectionMode { +@@ -803,7 +803,7 @@ enum class KeyCollectionMode { // Utility superclass for stack-allocated objects that must be updated // on gc. It provides two ways for the gc to update instances, either // iterating or updating after gc. @@ -19,7 +19,7 @@ index b4e78a19377df73051c2e6e650d5b654ca435c6b..93de4136f2a0498038d17866f99f72ea explicit inline Relocatable(Isolate* isolate); inline virtual ~Relocatable(); diff --git a/src/objects/ordered-hash-table.h b/src/objects/ordered-hash-table.h -index 21decaeba72246ef95be0f031c605c7f840715b1..d661a11cf48f51eb6080cc086a7a26b4bd497cae 100644 +index 590846f1302775c0ee0f85767d23701bf99c9133..611874eaf7a5132478006a87ca1a5939fe754785 100644 --- a/src/objects/ordered-hash-table.h +++ b/src/objects/ordered-hash-table.h @@ -60,7 +60,7 @@ namespace internal { diff --git a/patches/v8/expose_mksnapshot.patch b/patches/v8/expose_mksnapshot.patch index 5c832f8d4a391..176803647386e 100644 --- a/patches/v8/expose_mksnapshot.patch +++ b/patches/v8/expose_mksnapshot.patch @@ -6,14 +6,14 @@ Subject: expose_mksnapshot.patch Needed in order to target mksnapshot for mksnapshot zip. diff --git a/BUILD.gn b/BUILD.gn -index b0e242d6c369185172184c1f4a88247aa1e0896c..62d1548a10e367795ec3d649fe1cf112b278ec19 100644 +index 11cf04af2193c486eb49cc24dff54a4085ad7c16..b1b2d17d19a706ba1a035484625de0000214c7f9 100644 --- a/BUILD.gn +++ b/BUILD.gn -@@ -3845,7 +3845,6 @@ if (current_toolchain == v8_generator_toolchain) { +@@ -3821,7 +3821,6 @@ if (current_toolchain == v8_generator_toolchain) { - if (v8_use_snapshot && current_toolchain == v8_snapshot_toolchain) { + if (current_toolchain == v8_snapshot_toolchain) { v8_executable("mksnapshot") { - visibility = [ ":*" ] # Only targets in this file can depend on this. sources = [ - "src/snapshot/embedded/embedded-file-writer.cc", + "src/snapshot/embedded/embedded-empty.cc", diff --git a/patches/v8/fix_build_deprecated_attirbute_for_older_msvc_versions.patch b/patches/v8/fix_build_deprecated_attirbute_for_older_msvc_versions.patch new file mode 100644 index 0000000000000..406f88ffe43d7 --- /dev/null +++ b/patches/v8/fix_build_deprecated_attirbute_for_older_msvc_versions.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Deepak Mohan +Date: Tue, 28 Jan 2020 15:48:03 -0800 +Subject: fix: usage of c++ [[deprecated]] attirbute for older msvc versions + +VS 2015 update 3 has a bug where [[deprecated]] attribute cannot +be applied to constructor declarations, this is fixed in 2017 and +higher versions, but native module compiling with this version +will have an issue. + +diff --git a/include/v8config.h b/include/v8config.h +index 2307dbe534da471da75fa020252f217b1e441004..3978c7d52db672aaa2c413f9030ad3edc1b8e1bf 100644 +--- a/include/v8config.h ++++ b/include/v8config.h +@@ -389,10 +389,13 @@ + # define V8_NOINLINE /* NOT SUPPORTED */ + #endif + +- + // A macro (V8_DEPRECATED) to mark classes or functions as deprecated. + #if defined(V8_DEPRECATION_WARNINGS) +-# define V8_DEPRECATED(message) [[deprecated(message)]] ++# if defined(_MSC_VER) && _MSC_VER <= 1900 ++# define V8_DEPRECATED(message) __declspec(deprecated(message)) ++# else ++# define V8_DEPRECATED(message) [[deprecated(message)]] ++# endif + #else + # define V8_DEPRECATED(message) + #endif +@@ -400,7 +403,11 @@ + + // A macro (V8_DEPRECATE_SOON) to make it easier to see what will be deprecated. + #if defined(V8_IMMINENT_DEPRECATION_WARNINGS) +-# define V8_DEPRECATE_SOON(message) [[deprecated(message)]] ++# if defined(_MSC_VER) && _MSC_VER <= 1900 ++# define V8_DEPRECATE_SOON(message) __declspec(deprecated(message)) ++# else ++# define V8_DEPRECATE_SOON(message) [[deprecated(message)]] ++# endif + #else + # define V8_DEPRECATE_SOON(message) + #endif diff --git a/patches/v8/merged_regexp_reserve_space_for_all_registers_in_interpreter.patch b/patches/v8/merged_regexp_reserve_space_for_all_registers_in_interpreter.patch new file mode 100644 index 0000000000000..5563e28ebfb0f --- /dev/null +++ b/patches/v8/merged_regexp_reserve_space_for_all_registers_in_interpreter.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jakob Gruber +Date: Mon, 6 Apr 2020 15:48:53 +0200 +Subject: Merged: [regexp] Reserve space for all registers in interpreter + +This is a minimal version of https://crrev.com/c/2135642 intended for +backmerges. + +Ensure that the interpreter has space for all required registers. + +(cherry picked from commit 30658b6b1b672e535e6046fa84674882e29b2279) + +Tbr: leszeks@chromium.org +No-Try: true +No-Presubmit: true +No-Treechecks: true +Bug: chromium:1067270 +Change-Id: Iefd016b4845fb8698d1e0ef5f6a03df0e66aa576 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2137403 +Commit-Queue: Jakob Gruber +Reviewed-by: Leszek Swirski +Cr-Original-Commit-Position: refs/heads/master@{#67013} +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2144052 +Reviewed-by: Jakob Gruber +Cr-Commit-Position: refs/branch-heads/8.1@{#61} +Cr-Branched-From: a4dcd39d521d14c4b1cac020812e44ee04a7f244-refs/heads/8.1.307@{#1} +Cr-Branched-From: f22c213304ec3542df87019aed0909b7dafeaa93-refs/heads/master@{#66031} + +diff --git a/src/regexp/regexp-interpreter.cc b/src/regexp/regexp-interpreter.cc +index adea0cf7c0951bd707cfd05dc3b69a34b55efa77..c0d4435d380f880f2d6890d3ac783944518560e2 100644 +--- a/src/regexp/regexp-interpreter.cc ++++ b/src/regexp/regexp-interpreter.cc +@@ -1045,8 +1045,29 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromJs( + return IrregexpInterpreter::RETRY; + } + +- return Match(isolate, regexp_obj, subject_string, registers, registers_length, +- start_position, call_origin); ++ // In generated code, registers are allocated on the stack. The given ++ // `registers` argument is only guaranteed to hold enough space for permanent ++ // registers (i.e. for captures), and not for temporary registers used only ++ // during matcher execution. We match that behavior in the interpreter by ++ // using a SmallVector as internal register storage. ++ static constexpr int kBaseRegisterArraySize = 64; // Arbitrary. ++ const int internal_register_count = ++ Smi::ToInt(regexp_obj.DataAt(JSRegExp::kIrregexpMaxRegisterCountIndex)); ++ base::SmallVector internal_registers( ++ internal_register_count); ++ ++ Result result = ++ Match(isolate, regexp_obj, subject_string, internal_registers.data(), ++ internal_register_count, start_position, call_origin); ++ ++ // Copy capture registers to the output array. ++ if (result == IrregexpInterpreter::SUCCESS) { ++ CHECK_GE(internal_registers.size(), registers_length); ++ MemCopy(registers, internal_registers.data(), ++ registers_length * sizeof(registers[0])); ++ } ++ ++ return result; + } + + IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromRuntime( +diff --git a/test/mjsunit/regress/regress-1067270.js b/test/mjsunit/regress/regress-1067270.js +new file mode 100644 +index 0000000000000000000000000000000000000000..1c6eddf505aa55e622df9d7116ea7fbb2f516713 +--- /dev/null ++++ b/test/mjsunit/regress/regress-1067270.js +@@ -0,0 +1,11 @@ ++// Copyright 2020 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 ++ ++const needle = Array(1802).join(" +") + Array(16884).join("A"); ++const string = "A"; ++ ++assertEquals(string.search(needle), -1); ++assertEquals(string.search(needle), -1); diff --git a/patches/v8/objects_fix_memory_leak_in_prototypeusers_add.patch b/patches/v8/objects_fix_memory_leak_in_prototypeusers_add.patch new file mode 100644 index 0000000000000..1533e2496ff35 --- /dev/null +++ b/patches/v8/objects_fix_memory_leak_in_prototypeusers_add.patch @@ -0,0 +1,71 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Dominik=20Inf=C3=BChr?= +Date: Fri, 13 Dec 2019 14:13:21 +0100 +Subject: Fix memory leak in PrototypeUsers::Add +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PrototypeUsers::Add now iterates the WeakArrayList to find empty slots +before growing the array. Not reusing empty slots caused a memory leak. + +It might also be desirable to shrink the WeakArrayList in the future. +Right now it is only compacted when invoking CreateBlob. + +Also removed unused PrototypeUsers::IsEmptySlot declaration. + +Bug: v8:10031 +Change-Id: I570ec78fca37e8f0c794f1f40846a4daab47c225 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1967317 +Reviewed-by: Ulan Degenbaev +Reviewed-by: Igor Sheludko +Commit-Queue: Dominik Inführ +Cr-Commit-Position: refs/heads/master@{#65456} + +diff --git a/src/objects/objects.cc b/src/objects/objects.cc +index ffe9555238aa8ae9111c6d7f43178f3e298edbb0..df192142c16de26678f832b1f7107c383f28ff2a 100644 +--- a/src/objects/objects.cc ++++ b/src/objects/objects.cc +@@ -4044,6 +4044,13 @@ Handle PrototypeUsers::Add(Isolate* isolate, + + // If there are empty slots, use one of them. + int empty_slot = Smi::ToInt(empty_slot_index(*array)); ++ ++ if (empty_slot == kNoEmptySlotsMarker) { ++ // GCs might have cleared some references, rescan the array for empty slots. ++ PrototypeUsers::ScanForEmptySlots(*array); ++ empty_slot = Smi::ToInt(empty_slot_index(*array)); ++ } ++ + if (empty_slot != kNoEmptySlotsMarker) { + DCHECK_GE(empty_slot, kFirstIndex); + CHECK_LT(empty_slot, array->length()); +@@ -4066,6 +4073,15 @@ Handle PrototypeUsers::Add(Isolate* isolate, + return array; + } + ++// static ++void PrototypeUsers::ScanForEmptySlots(WeakArrayList array) { ++ for (int i = kFirstIndex; i < array.length(); i++) { ++ if (array.Get(i)->IsCleared()) { ++ PrototypeUsers::MarkSlotEmpty(array, i); ++ } ++ } ++} ++ + WeakArrayList PrototypeUsers::Compact(Handle array, Heap* heap, + CompactionCallback callback, + AllocationType allocation) { +diff --git a/src/objects/prototype-info.h b/src/objects/prototype-info.h +index 94d86d2e1931c397f683c0824dd05dab6a9963c3..6f777eda8936c81a139a80d8be71258f1181ce8d 100644 +--- a/src/objects/prototype-info.h ++++ b/src/objects/prototype-info.h +@@ -99,7 +99,7 @@ class V8_EXPORT_PRIVATE PrototypeUsers : public WeakArrayList { + static inline Smi empty_slot_index(WeakArrayList array); + static inline void set_empty_slot_index(WeakArrayList array, int index); + +- static void IsSlotEmpty(WeakArrayList array, int index); ++ static void ScanForEmptySlots(WeakArrayList array); + + DISALLOW_IMPLICIT_CONSTRUCTORS(PrototypeUsers); + }; diff --git a/patches/v8/ptr-compr_temporarily_disable_31_bit_smis_on_64-bit_architectures.patch b/patches/v8/ptr-compr_temporarily_disable_31_bit_smis_on_64-bit_architectures.patch deleted file mode 100644 index 5602dd0c85711..0000000000000 --- a/patches/v8/ptr-compr_temporarily_disable_31_bit_smis_on_64-bit_architectures.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Igor Sheludko -Date: Tue, 15 Oct 2019 10:46:52 +0200 -Subject: [ptr-compr] Temporarily disable 31 bit Smis on 64-bit architectures - -The reason is to unblock M79 blocked by chromium:1009439 while full -solution is not ready yet. - -This CL will be reverted after the M79 branch point. - -Bug: v8:9767, chromium:1009439 -Change-Id: I5302d86fe953ecd94d9a4bba0d29c807b7b9d703 -Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1862554 -Reviewed-by: Toon Verwaest -Commit-Queue: Igor Sheludko -Cr-Commit-Position: refs/heads/master@{#64286} - -diff --git a/BUILD.gn b/BUILD.gn -index 6207d71c542c46a4716a51baeb8518eca0f8e1c0..db7179968237406318c64d68eae08027860e98f9 100644 ---- a/BUILD.gn -+++ b/BUILD.gn -@@ -108,7 +108,7 @@ declare_args() { - - # Enable pointer compression (sets -dV8_COMPRESS_POINTERS). - v8_enable_pointer_compression = false -- v8_enable_31bit_smis_on_64bit_arch = true -+ v8_enable_31bit_smis_on_64bit_arch = false - - # Sets -dOBJECT_PRINT. - v8_enable_object_print = "" diff --git a/patches/v8/use_context_of_then_function_for_promiseresolvethenablejob.patch b/patches/v8/use_context_of_then_function_for_promiseresolvethenablejob.patch new file mode 100644 index 0000000000000..ee38f41831965 --- /dev/null +++ b/patches/v8/use_context_of_then_function_for_promiseresolvethenablejob.patch @@ -0,0 +1,99 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +Date: Mon, 20 Apr 2020 12:11:40 -0700 +Subject: Use context of then function for PromiseResolveThenableJob + +When a microtask is executed, we need to use an appropriate, +non-detached Context for its execution. Currently with +PromiseResolveThenableJobs [1], the Context used is always drawn from +the realm of the Promise constructor being used. This may cause +non-intuitive behavior, such as in the following case: + + const DeadPromise = iframe.contentWindow.Promise; + const p = DeadPromise.resolve({ + then() { + return { success: true }; + } + }); + p.then(result => { console.log(result); }); + + // Some time later, but synchronously... + iframe.src = "http://example.com"; // navigate away. + // DeadPromise's Context is detached state now. + // p never gets resolved, and its reaction handler never gets called. + +To fix this behavior, when PromiseResolveThenableJob is being queued up, +the `then` method of the thenable should be used to determine the +context of the resultant microtask. Doing so aligns with Firefox, and +also with the latest HTML spec [2][3]. + +diff --git a/src/builtins/builtins-promise-gen.cc b/src/builtins/builtins-promise-gen.cc +index fc0a3515883514f5e8b704cd10b0c4820c5107dc..adf9743f0e2ecfbe834cb053c5d3611e85b3d294 100644 +--- a/src/builtins/builtins-promise-gen.cc ++++ b/src/builtins/builtins-promise-gen.cc +@@ -1333,10 +1333,17 @@ TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) { + { + // 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob, + // «promise, resolution, thenAction»). ++ // According to HTML, we use the context of the then function ++ // (|thenAction|) as the context of the microtask. See step 3 of HTML's ++ // EnqueueJob: ++ // https://html.spec.whatwg.org/C/#enqueuejob(queuename,-job,-arguments) ++ VARIABLE(var_then_context, MachineRepresentation::kTagged, native_context); ++ ExtractHandlerContext(var_then.value(), &var_then_context); ++ const TNode native_then_context = LoadNativeContext(var_then_context.value()); + const TNode task = + AllocatePromiseResolveThenableJobTask(promise, var_then.value(), +- CAST(resolution), native_context); +- TailCallBuiltin(Builtins::kEnqueueMicrotask, native_context, task); ++ CAST(resolution), native_then_context); ++ TailCallBuiltin(Builtins::kEnqueueMicrotask, native_then_context, task); + } + + BIND(&if_fulfill); +diff --git a/src/objects/objects.cc b/src/objects/objects.cc +index df192142c16de26678f832b1f7107c383f28ff2a..947141a5e6be4793c8915663e7f4cd7b7ae11486 100644 +--- a/src/objects/objects.cc ++++ b/src/objects/objects.cc +@@ -5903,10 +5903,20 @@ MaybeHandle JSPromise::Resolve(Handle promise, + + // 12. Perform EnqueueJob("PromiseJobs", PromiseResolveThenableJob, + // «promise, resolution, thenAction»). ++ ++ // According to HTML, we use the context of the then function (|thenAction|) ++ // as the context of the microtask. See step 3 of HTML's EnqueueJob: ++ // https://html.spec.whatwg.org/C/#enqueuejob(queuename,-job,-arguments) ++ Handle then_context; ++ if (!JSReceiver::GetContextForMicrotask(Handle::cast(then_action)) ++ .ToHandle(&then_context)) { ++ then_context = isolate->native_context(); ++ } ++ + Handle task = + isolate->factory()->NewPromiseResolveThenableJobTask( + promise, Handle::cast(then_action), +- Handle::cast(resolution), isolate->native_context()); ++ Handle::cast(resolution), then_context); + if (isolate->debug()->is_active() && resolution->IsJSPromise()) { + // Mark the dependency of the new {promise} on the {resolution}. + Object::SetProperty(isolate, resolution, +@@ -5914,8 +5924,7 @@ MaybeHandle JSPromise::Resolve(Handle promise, + promise) + .Check(); + } +- MicrotaskQueue* microtask_queue = +- isolate->native_context()->microtask_queue(); ++ MicrotaskQueue* microtask_queue = then_context->microtask_queue(); + if (microtask_queue) microtask_queue->EnqueueMicrotask(*task); + + // 13. Return undefined. +@@ -5951,6 +5960,9 @@ Handle JSPromise::TriggerPromiseReactions(Isolate* isolate, + Handle reaction = Handle::cast(task); + reactions = handle(reaction->next(), isolate); + ++ // According to HTML, we use the context of the appropriate handler as the ++ // context of the microtask. See step 3 of HTML's EnqueueJob: ++ // https://html.spec.whatwg.org/C/#enqueuejob(queuename,-job,-arguments) + Handle handler_context; + + Handle primary_handler; diff --git a/patches/v8/workaround_an_undefined_symbol_error.patch b/patches/v8/workaround_an_undefined_symbol_error.patch index c082c66707b8c..9ffb959bba3e9 100644 --- a/patches/v8/workaround_an_undefined_symbol_error.patch +++ b/patches/v8/workaround_an_undefined_symbol_error.patch @@ -12,10 +12,10 @@ By moving some functions out of the the arm64-assembler header file, this error no longer seems to happen. diff --git a/src/codegen/arm64/assembler-arm64.cc b/src/codegen/arm64/assembler-arm64.cc -index c798d3a8a03ed9ed49c4562ef3968fae38c55fbe..7369b80c0762bc86d7e763674479bbd426c0a1fa 100644 +index dc2efddc2379354e45c0d5a1afab1e379f8ea80b..294b7802dedacf16c10b6216ea7943824313ce60 100644 --- a/src/codegen/arm64/assembler-arm64.cc +++ b/src/codegen/arm64/assembler-arm64.cc -@@ -3589,6 +3589,22 @@ void Assembler::MoveWide(const Register& rd, uint64_t imm, int shift, +@@ -3588,6 +3588,22 @@ void Assembler::MoveWide(const Register& rd, uint64_t imm, int shift, ImmMoveWide(static_cast(imm)) | ShiftMoveWide(shift)); } @@ -39,10 +39,10 @@ index c798d3a8a03ed9ed49c4562ef3968fae38c55fbe..7369b80c0762bc86d7e763674479bbd4 const Operand& operand, FlagsUpdate S, AddSubOp op) { DCHECK_EQ(rd.SizeInBits(), rn.SizeInBits()); diff --git a/src/codegen/arm64/assembler-arm64.h b/src/codegen/arm64/assembler-arm64.h -index 10542a278db27660ec2dc4650f74653f46139c65..44b679cdf6e08297d77cdbe1224dd861a24e2f17 100644 +index 04fbaf3fb0105ea082a09a09ac02f06f5d0c53ef..5bea3dcd328f4b94cfa9038d1216a1ebe6a17d73 100644 --- a/src/codegen/arm64/assembler-arm64.h +++ b/src/codegen/arm64/assembler-arm64.h -@@ -2099,11 +2099,7 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase { +@@ -2103,11 +2103,7 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase { return rm.code() << Rm_offset; } @@ -55,7 +55,7 @@ index 10542a278db27660ec2dc4650f74653f46139c65..44b679cdf6e08297d77cdbe1224dd861 static Instr Ra(CPURegister ra) { DCHECK_NE(ra.code(), kSPRegInternalCode); -@@ -2127,15 +2123,8 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase { +@@ -2131,15 +2127,8 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase { // These encoding functions allow the stack pointer to be encoded, and // disallow the zero register. diff --git a/script/add-debug-link.py b/script/add-debug-link.py new file mode 100755 index 0000000000000..72b1d9a3b6357 --- /dev/null +++ b/script/add-debug-link.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +from __future__ import print_function +import argparse +import os +import sys + +from lib.config import LINUX_BINARIES, PLATFORM +from lib.util import execute, get_objcopy_path, get_out_dir + +def add_debug_link_into_binaries(directory, target_cpu, debug_dir): + for binary in LINUX_BINARIES: + binary_path = os.path.join(directory, binary) + if os.path.isfile(binary_path): + add_debug_link_into_binary(binary_path, target_cpu, debug_dir) + +def add_debug_link_into_binary(binary_path, target_cpu, debug_dir): + try: + objcopy = get_objcopy_path(target_cpu) + except: + if PLATFORM == 'linux' and (target_cpu == 'x86' or target_cpu == 'arm' or + target_cpu == 'arm64'): + # Skip because no objcopy binary on the given target. + return + raise + debug_name = get_debug_name(binary_path) + # Make sure the path to the binary is not relative because of cwd param. + real_binary_path = os.path.realpath(binary_path) + cmd = [objcopy, '--add-gnu-debuglink=' + debug_name, real_binary_path] + execute(cmd, cwd=debug_dir) + +def get_debug_name(binary_path): + return os.path.basename(binary_path) + '.debug' + +def main(): + args = parse_args() + if args.file: + add_debug_link_into_binary(args.file, args.target_cpu, args.debug_dir) + else: + add_debug_link_into_binaries(args.directory, args.target_cpu, + args.debug_dir) + +def parse_args(): + parser = argparse.ArgumentParser(description='Add debug link to binaries') + parser.add_argument('-d', '--directory', + help='Path to the dir that contains files to add links', + default=get_out_dir(), + required=False) + parser.add_argument('-f', '--file', + help='Path to a specific file to add debug link', + required=False) + parser.add_argument('-s', '--debug-dir', + help='Path to the dir that contain the debugs', + default=None, + required=True) + parser.add_argument('-v', '--verbose', + action='store_true', + help='Prints the output of the subprocesses') + parser.add_argument('--target-cpu', + default='', + required=False, + help='Target cpu of binaries to add debug link') + + return parser.parse_args() + +if __name__ == '__main__': + sys.exit(main()) diff --git a/script/check-trailing-whitespace.py b/script/check-trailing-whitespace.py index 731b07f8ed0a5..9de5c6aa3b406 100755 --- a/script/check-trailing-whitespace.py +++ b/script/check-trailing-whitespace.py @@ -49,7 +49,8 @@ def hasTrailingWhiteSpace(filepath, fix): for num, line in enumerate(lines): fixed_lines.append(line.rstrip() + '\n') if not fix and line != line.rstrip(): - print("Trailing whitespace on line {} in file: {}".format(num + 1, filepath)) + print("Trailing whitespace on line {} in file: {}".format( + num + 1, filepath)) return True if fix: with open(filepath, 'w') as f: diff --git a/script/codesign/.gitignore b/script/codesign/.gitignore new file mode 100644 index 0000000000000..2717ebb4fbd89 --- /dev/null +++ b/script/codesign/.gitignore @@ -0,0 +1 @@ +.working \ No newline at end of file diff --git a/script/codesign/codesign.cnf b/script/codesign/codesign.cnf new file mode 100644 index 0000000000000..aad66e16c7b0f --- /dev/null +++ b/script/codesign/codesign.cnf @@ -0,0 +1,18 @@ +[req] +default_bits = 4096 +encrypt_key = no +default_md = 512 +distinguished_name = req_distinguished_name +prompt = no + +[req_distinguished_name] +C = CA +ST = BC +L = Vancouver +O = ElectronJS +OU = BuildAutomation +CN = codesign.electronjs.org + +[extended] +keyUsage = critical,digitalSignature +extendedKeyUsage = critical,codeSigning diff --git a/script/codesign/gen-trust.js b/script/codesign/gen-trust.js new file mode 100644 index 0000000000000..9dca60aab0b89 --- /dev/null +++ b/script/codesign/gen-trust.js @@ -0,0 +1,38 @@ +const cp = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const certificatePath = process.argv[2]; +const outPath = process.argv[3]; +const templatePath = path.resolve(__dirname, 'trust.xml'); + +const template = fs.readFileSync(templatePath, 'utf8'); + +const fingerprintResult = cp.spawnSync('openssl', ['x509', '-noout', '-fingerprint', '-sha1', '-in', certificatePath]); +if (fingerprintResult.status !== 0) { + console.error(fingerprintResult.stderr.toString()); + process.exit(1); +} + +const fingerprint = fingerprintResult.stdout.toString().replace(/^SHA1 Fingerprint=/, '').replace(/:/g, '').trim(); + +const serialResult = cp.spawnSync('openssl', ['x509', '-serial', '-noout', '-in', certificatePath]); +if (serialResult.status !== 0) { + console.error(serialResult.stderr.toString()); + process.exit(1); +} + +let serialHex = serialResult.stdout.toString().replace(/^serial=/, '').trim(); +// Pad the serial number out to 18 hex chars +while (serialHex.length < 18) { + serialHex = `0${serialHex}`; +} +const serialB64 = Buffer.from(serialHex, 'hex').toString('base64'); + +const trust = template + .replace(/{{FINGERPRINT}}/g, fingerprint) + .replace(/{{SERIAL_BASE64}}/g, serialB64); + +fs.writeFileSync(outPath, trust); + +console.log('Generated Trust Settings'); diff --git a/script/codesign/generate-identity.sh b/script/codesign/generate-identity.sh new file mode 100755 index 0000000000000..27a1c8f244fb4 --- /dev/null +++ b/script/codesign/generate-identity.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +set -eo pipefail + +dir="$(dirname $0)"/.working + +cleanup() { + rm -rf "$dir" +} + +# trap cleanup EXIT + +# Clean Up +cleanup + +# Create Working Dir +mkdir -p "$dir" + +# Generate Certs +openssl req -new -newkey rsa:2048 -x509 -days 7300 -nodes -config "$(dirname $0)"/codesign.cnf -extensions extended -batch -out "$dir"/certificate.cer -keyout "$dir"/certificate.key +sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "$dir"/certificate.cer +sudo security import "$dir"/certificate.key -A -k /Library/Keychains/System.keychain + +# restart(reload) taskgated daemon +sudo pkill -f /usr/libexec/taskgated + +# need once +sudo security authorizationdb write system.privilege.taskport allow +# need once +DevToolsSecurity -enable + +# openssl req -newkey rsa:2048 -nodes -keyout "$dir"/private.pem -x509 -days 1 -out "$dir"/certificate.pem -extensions extended -config "$(dirname $0)"/codesign.cnf +# openssl x509 -inform PEM -in "$dir"/certificate.pem -outform DER -out "$dir"/certificate.cer +# openssl x509 -pubkey -noout -in "$dir"/certificate.pem > "$dir"/public.key +# rm -f "$dir"/certificate.pem + +# Import Certs +# security import "$dir"/certificate.cer -k $KEY_CHAIN +# security import "$dir"/private.pem -k $KEY_CHAIN +# security import "$dir"/public.key -k $KEY_CHAIN + +# Generate Trust Settings +node "$(dirname $0)"/gen-trust.js "$dir"/certificate.cer "$dir"/trust.xml + +# Import Trust Settings +sudo security trust-settings-import -d "$dir/trust.xml" diff --git a/script/codesign/import-testing-cert-ci.sh b/script/codesign/import-testing-cert-ci.sh deleted file mode 100755 index 03732f89d6e9c..0000000000000 --- a/script/codesign/import-testing-cert-ci.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -KEY_CHAIN=mac-build.keychain -KEYCHAIN_PASSWORD=unsafe_keychain_pass -security create-keychain -p $KEYCHAIN_PASSWORD $KEY_CHAIN -# Make the keychain the default so identities are found -security default-keychain -s $KEY_CHAIN -# Unlock the keychain -security unlock-keychain -p $KEYCHAIN_PASSWORD $KEY_CHAIN - -# Add certificates to keychain and allow codesign to access them -security import "$(dirname $0)"/signing.cer -k $KEY_CHAIN -A /usr/bin/codesign -security import "$(dirname $0)"/signing.pem -k $KEY_CHAIN -A /usr/bin/codesign -security import "$(dirname $0)"/signing.p12 -k $KEY_CHAIN -P $SPEC_KEY_PASSWORD -A /usr/bin/codesign - -echo "Add keychain to keychain-list" -security list-keychains -s $KEY_CHAIN - -echo "Setting key partition list" -security set-key-partition-list -S apple-tool:,apple: -s -k $KEYCHAIN_PASSWORD $KEY_CHAIN - -echo "Trusting self-signed certificate" -sudo security trust-settings-import -d "$(dirname $0)"/trust-settings.plist diff --git a/script/codesign/signing.cer b/script/codesign/signing.cer deleted file mode 100644 index aa91d20272b58..0000000000000 Binary files a/script/codesign/signing.cer and /dev/null differ diff --git a/script/codesign/signing.p12 b/script/codesign/signing.p12 deleted file mode 100644 index b8791f3bf0234..0000000000000 Binary files a/script/codesign/signing.p12 and /dev/null differ diff --git a/script/codesign/signing.pem b/script/codesign/signing.pem deleted file mode 100644 index 0f5dc502edaf8..0000000000000 --- a/script/codesign/signing.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN RSA PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw91mumcVpai94X7PASje -R9+meqEHsavRsKQmtVV5JkJk9ZZbWTdpXgOjy1hhGQURrbp3li9lmi3MFHVqZjuQ -H8omufj0iFiUD0bBY9EZeQjmcXd/ZgP8SoFfMS3BSAeRzXI5UQ5zFq86CWyzBh4k -lgRN+iuhmxxZ/8PUcuEQ49fzNWVtRskkX+ZDwj8mn9YYRQMm3nl+bB+lYbpgVnkX -WztXvSdRxCMjvjzLtoSJQhG36DEz6Sv7XeEAfYi70diQIwr/yCtgCpYUTadjOdzO -h0W/rpC2DTVE/yC3xZxg2uVjEa9siC8+DX9F6luAytkx2TgUGF6KdVblPVVCYkxW -QQIDAQAB ------END RSA PUBLIC KEY----- diff --git a/script/codesign/trust-settings.plist b/script/codesign/trust-settings.plist deleted file mode 100644 index 3019399b20760..0000000000000 --- a/script/codesign/trust-settings.plist +++ /dev/null @@ -1,138 +0,0 @@ - - - - - trustList - - 80079C1EC6AED92C03B4C67E9A94B0B05E854AC8 - - issuerName - - MIGIMSYwJAYDVQQDDB1FbGVjdHJvblNlbGZTaWduZWRTcGVjU2ln - bmluZzEUMBIGA1UECgwLRWxlY3Ryb24gSlMxEDAOBgNVBAsMB1Rl - c3RpbmcxCzAJBgNVBAYTAlVTMSkwJwYJKoZIhvcNAQkBFhpjb2Rl - c2lnbmluZ0BlbGVjdHJvbmpzLm9yZw== - - modDate - 2019-03-19T02:33:38Z - serialNumber - - AQ== - - trustSettings - - - kSecTrustSettingsAllowedError - -2147409654 - kSecTrustSettingsPolicy - - KoZIhvdjZAED - - kSecTrustSettingsPolicyName - sslServer - kSecTrustSettingsResult - 1 - - - kSecTrustSettingsAllowedError - -2147408896 - kSecTrustSettingsPolicy - - KoZIhvdjZAED - - kSecTrustSettingsPolicyName - sslServer - kSecTrustSettingsResult - 1 - - - kSecTrustSettingsAllowedError - -2147409654 - kSecTrustSettingsPolicy - - KoZIhvdjZAEI - - kSecTrustSettingsPolicyName - SMIME - kSecTrustSettingsResult - 1 - - - kSecTrustSettingsAllowedError - -2147408872 - kSecTrustSettingsPolicy - - KoZIhvdjZAEI - - kSecTrustSettingsPolicyName - SMIME - kSecTrustSettingsResult - 1 - - - kSecTrustSettingsAllowedError - -2147409654 - kSecTrustSettingsPolicy - - KoZIhvdjZAEJ - - kSecTrustSettingsPolicyName - eapServer - kSecTrustSettingsResult - 1 - - - kSecTrustSettingsAllowedError - -2147409654 - kSecTrustSettingsPolicy - - KoZIhvdjZAEL - - kSecTrustSettingsPolicyName - ipsecServer - kSecTrustSettingsResult - 1 - - - kSecTrustSettingsAllowedError - -2147409654 - kSecTrustSettingsPolicy - - KoZIhvdjZAEQ - - kSecTrustSettingsPolicyName - CodeSigning - kSecTrustSettingsResult - 1 - - - kSecTrustSettingsAllowedError - -2147409654 - kSecTrustSettingsPolicy - - KoZIhvdjZAEU - - kSecTrustSettingsPolicyName - AppleTimeStamping - kSecTrustSettingsResult - 1 - - - kSecTrustSettingsAllowedError - -2147409654 - kSecTrustSettingsPolicy - - KoZIhvdjZAEC - - kSecTrustSettingsPolicyName - basicX509 - kSecTrustSettingsResult - 1 - - - - - trustVersion - 1 - - diff --git a/script/codesign/trust.xml b/script/codesign/trust.xml new file mode 100644 index 0000000000000..1767b1ef1c50c --- /dev/null +++ b/script/codesign/trust.xml @@ -0,0 +1,138 @@ + + + + + trustList + + {{FINGERPRINT}} + + issuerName + + MH8xCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJCQzESMBAGA1UEBwwJ + VmFuY291dmVyMRMwEQYDVQQKDApFbGVjdHJvbkpTMRgwFgYDVQQL + DA9CdWlsZEF1dG9tYXRpb24xIDAeBgNVBAMMF2NvZGVzaWduLmVs + ZWN0cm9uanMub3Jn + + modDate + 2019-01-01T00:00:00Z + serialNumber + + {{SERIAL_BASE64}} + + trustSettings + + + kSecTrustSettingsAllowedError + -2147409654 + kSecTrustSettingsPolicy + + KoZIhvdjZAED + + kSecTrustSettingsPolicyName + sslServer + kSecTrustSettingsResult + 1 + + + kSecTrustSettingsAllowedError + -2147408896 + kSecTrustSettingsPolicy + + KoZIhvdjZAED + + kSecTrustSettingsPolicyName + sslServer + kSecTrustSettingsResult + 1 + + + kSecTrustSettingsAllowedError + -2147409654 + kSecTrustSettingsPolicy + + KoZIhvdjZAEI + + kSecTrustSettingsPolicyName + SMIME + kSecTrustSettingsResult + 1 + + + kSecTrustSettingsAllowedError + -2147408872 + kSecTrustSettingsPolicy + + KoZIhvdjZAEI + + kSecTrustSettingsPolicyName + SMIME + kSecTrustSettingsResult + 1 + + + kSecTrustSettingsAllowedError + -2147409654 + kSecTrustSettingsPolicy + + KoZIhvdjZAEJ + + kSecTrustSettingsPolicyName + eapServer + kSecTrustSettingsResult + 1 + + + kSecTrustSettingsAllowedError + -2147409654 + kSecTrustSettingsPolicy + + KoZIhvdjZAEL + + kSecTrustSettingsPolicyName + ipsecServer + kSecTrustSettingsResult + 1 + + + kSecTrustSettingsAllowedError + -2147409654 + kSecTrustSettingsPolicy + + KoZIhvdjZAEQ + + kSecTrustSettingsPolicyName + CodeSigning + kSecTrustSettingsResult + 1 + + + kSecTrustSettingsAllowedError + -2147409654 + kSecTrustSettingsPolicy + + KoZIhvdjZAEU + + kSecTrustSettingsPolicyName + AppleTimeStamping + kSecTrustSettingsResult + 1 + + + kSecTrustSettingsAllowedError + -2147409654 + kSecTrustSettingsPolicy + + KoZIhvdjZAEC + + kSecTrustSettingsPolicyName + basicX509 + kSecTrustSettingsResult + 1 + + + + + trustVersion + 1 + + \ No newline at end of file diff --git a/script/copy-debug-symbols.py b/script/copy-debug-symbols.py new file mode 100755 index 0000000000000..51d3f7498d919 --- /dev/null +++ b/script/copy-debug-symbols.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +from __future__ import print_function +import argparse +import os +import sys + +from lib.config import LINUX_BINARIES, PLATFORM +from lib.util import execute, get_objcopy_path, get_out_dir, safe_mkdir + +# It has to be done before stripping the binaries. +def copy_debug_from_binaries(directory, out_dir, target_cpu, compress): + for binary in LINUX_BINARIES: + binary_path = os.path.join(directory, binary) + if os.path.isfile(binary_path): + copy_debug_from_binary(binary_path, out_dir, target_cpu, compress) + +def copy_debug_from_binary(binary_path, out_dir, target_cpu, compress): + try: + objcopy = get_objcopy_path(target_cpu) + except: + if PLATFORM == 'linux' and (target_cpu == 'x86' or target_cpu == 'arm' or + target_cpu == 'arm64'): + # Skip because no objcopy binary on the given target. + return + raise + debug_name = get_debug_name(binary_path) + cmd = [objcopy, '--only-keep-debug'] + if compress: + cmd.extend(['--compress-debug-sections']) + cmd.extend([binary_path, os.path.join(out_dir, debug_name)]) + execute(cmd) + return debug_name + +def get_debug_name(binary_path): + return os.path.basename(binary_path) + '.debug' + +def main(): + args = parse_args() + safe_mkdir(args.out_dir) + if args.file: + copy_debug_from_binary(args.file, args.out_dir, args.target_cpu, + args.compress) + else: + copy_debug_from_binaries(args.directory, args.out_dir, args.target_cpu, + args.compress) + +def parse_args(): + parser = argparse.ArgumentParser(description='Copy debug from binaries') + parser.add_argument('-d', '--directory', + help='Path to the dir that contains files to copy', + default=get_out_dir(), + required=False) + parser.add_argument('-f', '--file', + help='Path to a specific file to copy debug symbols', + required=False) + parser.add_argument('-o', '--out-dir', + help='Path to the dir that will contain the debugs', + default=None, + required=True) + parser.add_argument('-v', '--verbose', + action='store_true', + help='Prints the output of the subprocesses') + parser.add_argument('--target-cpu', + default='', + required=False, + help='Target cpu of binaries to copy debug symbols') + parser.add_argument('--compress', + action='store_true', + required=False, + help='Compress the debug symbols') + + return parser.parse_args() + +if __name__ == '__main__': + sys.exit(main()) diff --git a/script/doc-only-change.js b/script/doc-only-change.js new file mode 100644 index 0000000000000..467ee5399b875 --- /dev/null +++ b/script/doc-only-change.js @@ -0,0 +1,58 @@ +const args = require('minimist')(process.argv.slice(2)); +const octokit = require('@octokit/rest')(); +const path = require('path'); + +const SOURCE_ROOT = path.normalize(path.dirname(__dirname)); + +async function checkIfDocOnlyChange () { + if (args.prNumber || args.prBranch || args.prURL) { + try { + let pullRequestNumber = args.prNumber; + if (!pullRequestNumber || isNaN(pullRequestNumber)) { + if (args.prURL) { + // CircleCI doesn't provide the PR number for branch builds, but it does provide the PR URL + const pullRequestParts = args.prURL.split('/'); + pullRequestNumber = pullRequestParts[pullRequestParts.length - 1]; + } else if (args.prBranch) { + // AppVeyor doesn't provide a PR number for branch builds - figure it out from the branch + const prsForBranch = await octokit.pulls.list({ + owner: 'electron', + repo: 'electron', + state: 'open', + head: `electron:${args.prBranch}` + }); + if (prsForBranch.data.length === 1) { + pullRequestNumber = prsForBranch.data[0].number; + } else { + // If there are 0 PRs or more than one PR on a branch, just assume that this is more than a doc change + process.exit(1); + } + } + } + const filesChanged = await octokit.pulls.listFiles({ + owner: 'electron', repo: 'electron', pull_number: pullRequestNumber + }); + + const nonDocChange = filesChanged.data.find((fileInfo) => { + const fileDirs = fileInfo.filename.split('/'); + if (fileDirs[0] !== 'docs') { + return true; + } + }); + if (nonDocChange || filesChanged.data.length === 0) { + process.exit(1); + } else { + process.exit(0); + } + } catch (ex) { + console.error('Error getting list of files changed: ', ex); + process.exit(-1); + } + } else { + console.error(`Check if only the docs were changed for a commit. + Usage: doc-only-change.js --prNumber=PR_NUMBER || --prBranch=PR_BRANCH || --prURL=PR_URL`); + process.exit(-1); + } +} + +checkIfDocOnlyChange(); diff --git a/script/download-circleci-artifacts.js b/script/download-circleci-artifacts.js index 8fb63030cbb61..36cea8af7db12 100644 --- a/script/download-circleci-artifacts.js +++ b/script/download-circleci-artifacts.js @@ -1,38 +1,38 @@ -const args = require('minimist')(process.argv.slice(2)) -const nugget = require('nugget') -const request = require('request') +const args = require('minimist')(process.argv.slice(2)); +const nugget = require('nugget'); +const request = require('request'); async function makeRequest (requestOptions, parseResponse) { return new Promise((resolve, reject) => { request(requestOptions, (err, res, body) => { if (!err && res.statusCode >= 200 && res.statusCode < 300) { if (parseResponse) { - const build = JSON.parse(body) - resolve(build) + const build = JSON.parse(body); + resolve(build); } else { - resolve(body) + resolve(body); } } else { if (args.verbose) { - console.error('Error occurred while requesting:', requestOptions.url) + console.error('Error occurred while requesting:', requestOptions.url); if (parseResponse) { try { - console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions) + console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions); } catch (err) { - console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions) + console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions); } } else { - console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions) + console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions); } } - reject(err) + reject(err); } - }) - }) + }); + }); } async function downloadArtifact (name, buildNum, dest) { - const circleArtifactUrl = `https://circleci.com/api/v1.1/project/github/electron/electron/${args.buildNum}/artifacts?circle-token=${process.env.CIRCLE_TOKEN}` + const circleArtifactUrl = `https://circleci.com/api/v1.1/project/github/electron/electron/${args.buildNum}/artifacts?circle-token=${process.env.CIRCLE_TOKEN}`; const artifacts = await makeRequest({ method: 'GET', url: circleArtifactUrl, @@ -42,47 +42,47 @@ async function downloadArtifact (name, buildNum, dest) { } }, true).catch(err => { if (args.verbose) { - console.log('Error calling CircleCI:', err) + console.log('Error calling CircleCI:', err); } else { - console.error('Error calling CircleCI to get artifact details') + console.error('Error calling CircleCI to get artifact details'); } - }) + }); const artifactToDownload = artifacts.find(artifact => { - return (artifact.path === name) - }) + return (artifact.path === name); + }); if (!artifactToDownload) { - console.log(`Could not find artifact called ${name} to download for build #${buildNum}.`) - process.exit(1) + console.log(`Could not find artifact called ${name} to download for build #${buildNum}.`); + process.exit(1); } else { - console.log(`Downloading ${artifactToDownload.url}.`) - let downloadError = false + console.log(`Downloading ${artifactToDownload.url}.`); + let downloadError = false; await downloadWithRetry(artifactToDownload.url, dest).catch(err => { if (args.verbose) { - console.log(`${artifactToDownload.url} could not be successfully downloaded. Error was:`, err) + console.log(`${artifactToDownload.url} could not be successfully downloaded. Error was:`, err); } else { - console.log(`${artifactToDownload.url} could not be successfully downloaded.`) + console.log(`${artifactToDownload.url} could not be successfully downloaded.`); } - downloadError = true - }) + downloadError = true; + }); if (!downloadError) { - console.log(`Successfully downloaded ${name}.`) + console.log(`Successfully downloaded ${name}.`); } } } async function downloadWithRetry (url, directory) { - let lastError - const downloadURL = `${url}?circle-token=${process.env.CIRCLE_TOKEN}` + let lastError; + const downloadURL = `${url}?circle-token=${process.env.CIRCLE_TOKEN}`; for (let i = 0; i < 5; i++) { - console.log(`Attempting to download ${url} - attempt #${(i + 1)}`) + console.log(`Attempting to download ${url} - attempt #${(i + 1)}`); try { - return await downloadFile(downloadURL, directory) + return await downloadFile(downloadURL, directory); } catch (err) { - lastError = err - await new Promise((resolve, reject) => setTimeout(resolve, 30000)) + lastError = err; + await new Promise((resolve, reject) => setTimeout(resolve, 30000)); } } - throw lastError + throw lastError; } function downloadFile (url, directory) { @@ -90,21 +90,21 @@ function downloadFile (url, directory) { const nuggetOpts = { dir: directory, quiet: args.verbose - } + }; nugget(url, nuggetOpts, (err) => { if (err) { - reject(err) + reject(err); } else { - resolve() + resolve(); } - }) - }) + }); + }); } if (!args.name || !args.buildNum || !args.dest) { console.log(`Download CircleCI artifacts. - Usage: download-circleci-artifacts.js [--buildNum=CIRCLE_BUILD_NUMBER] [--name=artifactName] [--dest] [--verbose]`) - process.exit(0) + Usage: download-circleci-artifacts.js [--buildNum=CIRCLE_BUILD_NUMBER] [--name=artifactName] [--dest] [--verbose]`); + process.exit(0); } else { - downloadArtifact(args.name, args.buildNum, args.dest) + downloadArtifact(args.name, args.buildNum, args.dest); } diff --git a/script/export_all_patches.py b/script/export_all_patches.py new file mode 100644 index 0000000000000..562e2d1de4582 --- /dev/null +++ b/script/export_all_patches.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +import argparse +import json +import os +import sys + +from lib import git + + +def export_patches(dirs): + for patch_dir, repo in dirs.iteritems(): + git.export_patches(repo=repo, out_dir=patch_dir) + + +def parse_args(): + parser = argparse.ArgumentParser(description='Export Electron patches') + parser.add_argument('config', nargs='+', + type=argparse.FileType('r'), + help='patches\' config(s) in the JSON format') + return parser.parse_args() + + +def main(): + configs = parse_args().config + for config_json in configs: + export_patches(json.load(config_json)) + + +if __name__ == '__main__': + main() diff --git a/script/external-binaries.json b/script/external-binaries.json index f1bdc4d071f2d..1ff47895074c5 100644 --- a/script/external-binaries.json +++ b/script/external-binaries.json @@ -1,6 +1,6 @@ { "baseUrl": "https://github.com/electron/electron-frameworks/releases/download", - "version": "v1.4.0", + "version": "v1.4.1", "binaries": [ { "url": "Mantle.zip", diff --git a/script/gen-filenames.js b/script/gen-filenames.js index 1fec22facd97b..f306ba83f0017 100644 --- a/script/gen-filenames.js +++ b/script/gen-filenames.js @@ -1,17 +1,17 @@ -const cp = require('child_process') -const fs = require('fs-extra') -const os = require('os') -const path = require('path') +const cp = require('child_process'); +const fs = require('fs-extra'); +const os = require('os'); +const path = require('path'); -const rootPath = path.resolve(__dirname, '..') -const gniPath = path.resolve(__dirname, '../filenames.auto.gni') +const rootPath = path.resolve(__dirname, '..'); +const gniPath = path.resolve(__dirname, '../filenames.auto.gni'); const allDocs = fs.readdirSync(path.resolve(__dirname, '../docs/api')) .map(doc => `docs/api/${doc}`) .concat( fs.readdirSync(path.resolve(__dirname, '../docs/api/structures')) .map(doc => `docs/api/structures/${doc}`) - ) + ); const main = async () => { const webpackTargets = [ @@ -39,30 +39,30 @@ const main = async () => { name: 'worker_bundle_deps', config: 'webpack.config.worker.js' } - ] + ]; await Promise.all(webpackTargets.map(async webpackTarget => { - const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-filenames-')) + const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-filenames-')); const child = cp.spawn('node', [ 'build/webpack/get-outputs.js', `./${webpackTarget.config}`, path.resolve(tmpDir, `${webpackTarget.name}.measure.js`) ], { cwd: path.resolve(__dirname, '..') - }) - let output = '' + }); + let output = ''; child.stdout.on('data', chunk => { - output += chunk.toString() - }) - child.stderr.on('data', chunk => console.error(chunk.toString())) + output += chunk.toString(); + }); + child.stderr.on('data', chunk => console.error(chunk.toString())); await new Promise((resolve, reject) => child.on('exit', (code) => { if (code !== 0) { - console.error(output) - return reject(new Error(`Failed to list webpack dependencies for entry: ${webpackTarget.name}`)) + console.error(output); + return reject(new Error(`Failed to list webpack dependencies for entry: ${webpackTarget.name}`)); } - resolve() - })) + resolve(); + })); webpackTarget.dependencies = JSON.parse(output) // Remove whitespace @@ -76,9 +76,9 @@ const main = async () => { // All webpack builds depend on the tsconfig and package json files .concat(['tsconfig.json', 'tsconfig.electron.json', 'package.json']) // Make the generated list easier to read - .sort() - await fs.remove(tmpDir) - })) + .sort(); + await fs.remove(tmpDir); + })); fs.writeFileSync( gniPath, @@ -92,12 +92,12 @@ ${webpackTargets.map(target => ` ${target.name} = [ ${target.dependencies.map(dep => ` "${dep}",`).join('\n')} ]`).join('\n\n')} } -`) -} +`); +}; if (process.mainModule === module) { main().catch((err) => { - console.error(err) - process.exit(1) - }) + console.error(err); + process.exit(1); + }); } diff --git a/script/gen-hunspell-filenames.js b/script/gen-hunspell-filenames.js new file mode 100644 index 0000000000000..4e9b9d2a01052 --- /dev/null +++ b/script/gen-hunspell-filenames.js @@ -0,0 +1,35 @@ +const fs = require('fs'); +const path = require('path'); + +const check = process.argv.includes('--check'); + +const dictsPath = path.resolve(__dirname, '..', '..', 'third_party', 'hunspell_dictionaries'); +const gclientPath = 'third_party/hunspell_dictionaries'; + +const allFiles = fs.readdirSync(dictsPath); + +const dictionaries = allFiles + .filter(file => path.extname(file) === '.bdic'); + +const licenses = allFiles + .filter(file => file.startsWith('LICENSE') || file.startsWith('COPYING')); + +const content = `hunspell_dictionaries = [ + ${dictionaries.map(f => `"//${path.posix.join(gclientPath, f)}"`).join(',\n ')}, +] + +hunspell_licenses = [ + ${licenses.map(f => `"//${path.posix.join(gclientPath, f)}"`).join(',\n ')}, +] +`; + +const filenamesPath = path.resolve(__dirname, '..', 'filenames.hunspell.gni'); + +if (check) { + const currentContent = fs.readFileSync(filenamesPath, 'utf8'); + if (currentContent !== content) { + throw new Error('hunspell filenames need to be regenerated, latest generation does not match current file. Please run node gen-hunspell-filenames.js'); + } +} else { + fs.writeFileSync(filenamesPath, content); +} diff --git a/script/generate-deps-hash.js b/script/generate-deps-hash.js index b6cc316f60c96..28b1aa522452d 100644 --- a/script/generate-deps-hash.js +++ b/script/generate-deps-hash.js @@ -1,38 +1,39 @@ -const crypto = require('crypto') -const fs = require('fs') -const path = require('path') +const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); // Fallback to blow away old cache keys -const HASH_VERSION = 1 +const HASH_VERSION = 1; // Base files to hash const filesToHash = [ path.resolve(__dirname, '../DEPS'), path.resolve(__dirname, '../yarn.lock') -] +]; const addAllFiles = (dir) => { for (const child of fs.readdirSync(dir).sort()) { - const childPath = path.resolve(dir, child) + const childPath = path.resolve(dir, child); if (fs.statSync(childPath).isDirectory()) { - addAllFiles(childPath) + addAllFiles(childPath); } else { - filesToHash.push(childPath) + filesToHash.push(childPath); } } -} +}; // Add all patch files to the hash -addAllFiles(path.resolve(__dirname, '../patches')) +addAllFiles(path.resolve(__dirname, '../patches')); // Create Hash -const hasher = crypto.createHash('SHA256') +const hasher = crypto.createHash('SHA256'); +hasher.update(`HASH_VERSION:${HASH_VERSION}`); for (const file of filesToHash) { - hasher.update(fs.readFileSync(file)) + hasher.update(fs.readFileSync(file)); } // Add the GCLIENT_EXTRA_ARGS variable to the hash -hasher.update(process.env.GCLIENT_EXTRA_ARGS || 'no_extra_args') +hasher.update(process.env.GCLIENT_EXTRA_ARGS || 'no_extra_args'); // Write the hash to disk -fs.writeFileSync(path.resolve(__dirname, '../.depshash'), hasher.digest('hex')) +fs.writeFileSync(path.resolve(__dirname, '../.depshash'), hasher.digest('hex')); diff --git a/script/generate-version-json.js b/script/generate-version-json.js index 442fbc53dce08..5c41d0543394d 100644 --- a/script/generate-version-json.js +++ b/script/generate-version-json.js @@ -1,23 +1,23 @@ -const fs = require('fs') -const path = require('path') -const semver = require('semver') +const fs = require('fs'); +const path = require('path'); +const semver = require('semver'); -const outputPath = process.argv[2] +const outputPath = process.argv[2]; -const currentVersion = fs.readFileSync(path.resolve(__dirname, '../ELECTRON_VERSION'), 'utf8').trim() +const currentVersion = fs.readFileSync(path.resolve(__dirname, '../ELECTRON_VERSION'), 'utf8').trim(); -const parsed = semver.parse(currentVersion) +const parsed = semver.parse(currentVersion); -let prerelease = '' +let prerelease = ''; if (parsed.prerelease && parsed.prerelease.length > 0) { - prerelease = parsed.prerelease.join('.') + prerelease = parsed.prerelease.join('.'); } const { major, minor, patch -} = parsed +} = parsed; fs.writeFileSync(outputPath, JSON.stringify({ major, @@ -25,4 +25,4 @@ fs.writeFileSync(outputPath, JSON.stringify({ patch, prerelease, has_prerelease: prerelease === '' ? 0 : 1 -}, null, 2)) +}, null, 2)); diff --git a/script/git-export-patches b/script/git-export-patches index de68c485aaa1b..70930aab63707 100755 --- a/script/git-export-patches +++ b/script/git-export-patches @@ -6,115 +6,7 @@ import re import subprocess import sys - -def guess_base_commit(repo): - """Guess which commit the patches might be based on""" - try: - args = [ - 'git', - '-C', - repo, - 'rev-parse', - '--verify', - 'refs/patches/upstream-head', - ] - upstream_head = subprocess.check_output(args).strip() - args = [ - 'git', - '-C', - repo, - 'rev-list', - '--count', - upstream_head + '..', - ] - num_commits = subprocess.check_output(args).strip() - return [upstream_head, num_commits] - except subprocess.CalledProcessError: - args = [ - 'git', - '-C', - repo, - 'describe', - '--tags', - ] - return subprocess.check_output(args).rsplit('-', 2)[0:2] - - -def format_patch(repo, since): - args = [ - 'git', - '-C', - repo, - '-c', - 'core.attributesfile=' + os.path.join(os.path.dirname(os.path.realpath(__file__)), '.electron.attributes'), - # Ensure it is not possible to match anything - # Disabled for now as we have consistent chunk headers - # '-c', - # 'diff.electron.xfuncname=$^', - 'format-patch', - '--keep-subject', - '--no-stat', - '--stdout', - - # Per RFC 3676 the signature is separated from the body by a line with - # '-- ' on it. If the signature option is omitted the signature defaults - # to the Git version number. - '--no-signature', - - # The name of the parent commit object isn't useful information in this - # context, so zero it out to avoid needless patch-file churn. - '--zero-commit', - - # Some versions of git print out different numbers of characters in the - # 'index' line of patches, so pass --full-index to get consistent - # behaviour. - '--full-index', - since - ] - return subprocess.check_output(args) - - -def split_patches(patch_data): - """Split a concatenated series of patches into N separate patches""" - patches = [] - patch_start = re.compile('^From [0-9a-f]+ ') - for line in patch_data.splitlines(): - if patch_start.match(line): - patches.append([]) - patches[-1].append(line) - return patches - - -def munge_subject_to_filename(subject): - """Derive a suitable filename from a commit's subject""" - if subject.endswith('.patch'): - subject = subject[:-6] - return re.sub(r'[^A-Za-z0-9-]+', '_', subject).strip('_').lower() + '.patch' - - -def get_file_name(patch): - """Return the name of the file to which the patch should be written""" - for line in patch: - if line.startswith('Patch-Filename: '): - return line[len('Patch-Filename: '):] - # If no patch-filename header, munge the subject. - for line in patch: - if line.startswith('Subject: '): - return munge_subject_to_filename(line[len('Subject: '):]) - - -def remove_patch_filename(patch): - """Strip out the Patch-Filename trailer from a patch's message body""" - force_keep_next_line = False - for i, l in enumerate(patch): - is_patchfilename = l.startswith('Patch-Filename: ') - next_is_patchfilename = i < len(patch) - 1 and patch[i+1].startswith('Patch-Filename: ') - if not force_keep_next_line and (is_patchfilename or (next_is_patchfilename and len(l.rstrip()) == 0)): - pass # drop this line - else: - yield l - force_keep_next_line = l.startswith('Subject: ') - +from lib import git def main(argv): parser = argparse.ArgumentParser() @@ -127,32 +19,7 @@ def main(argv): "most recent tag or remote branch.") args = parser.parse_args(argv) - repo = '.' - patch_range = args.patch_range - if patch_range is None: - patch_range, num_patches = guess_base_commit(repo) - sys.stderr.write("Exporting {} patches since {}\n".format(num_patches, patch_range)) - patch_data = format_patch(repo, patch_range) - patches = split_patches(patch_data) - - out_dir = args.output - try: - os.mkdir(out_dir) - except OSError: - pass - - # remove old patches, so that deleted commits are correctly reflected in the - # patch files (as a removed file) - for p in os.listdir(out_dir): - if p.endswith('.patch'): - os.remove(os.path.join(out_dir, p)) - - with open(os.path.join(out_dir, '.patches'), 'w') as pl: - for patch in patches: - filename = get_file_name(patch) - with open(os.path.join(out_dir, filename), 'w') as f: - f.write('\n'.join(remove_patch_filename(patch)).rstrip('\n') + '\n') - pl.write(filename + '\n') + git.export_patches('.', args.output, patch_range=args.patch_range) if __name__ == '__main__': diff --git a/script/gn-asar.js b/script/gn-asar.js index aeb69e7fddd7e..80e2cdb34f4c8 100644 --- a/script/gn-asar.js +++ b/script/gn-asar.js @@ -1,63 +1,63 @@ -const asar = require('asar') -const assert = require('assert') -const fs = require('fs-extra') -const os = require('os') -const path = require('path') +const asar = require('asar'); +const assert = require('assert'); +const fs = require('fs-extra'); +const os = require('os'); +const path = require('path'); const getArgGroup = (name) => { - const group = [] - let inGroup = false + const group = []; + let inGroup = false; for (const arg of process.argv) { // At the next flag we stop being in the current group - if (arg.startsWith('--')) inGroup = false + if (arg.startsWith('--')) inGroup = false; // Push all args in the group - if (inGroup) group.push(arg) + if (inGroup) group.push(arg); // If we find the start flag, start pushing - if (arg === `--${name}`) inGroup = true + if (arg === `--${name}`) inGroup = true; } - return group -} + return group; +}; -const base = getArgGroup('base') -const files = getArgGroup('files') -const out = getArgGroup('out') +const base = getArgGroup('base'); +const files = getArgGroup('files'); +const out = getArgGroup('out'); -assert(base.length === 1, 'should have a single base dir') -assert(files.length >= 1, 'should have at least one input file') -assert(out.length === 1, 'should have a single out path') +assert(base.length === 1, 'should have a single base dir'); +assert(files.length >= 1, 'should have at least one input file'); +assert(out.length === 1, 'should have a single out path'); // Ensure all files are inside the base dir for (const file of files) { if (!file.startsWith(base[0])) { - console.error(`Expected all files to be inside the base dir but "${file}" was not in "${base[0]}"`) - process.exit(1) + console.error(`Expected all files to be inside the base dir but "${file}" was not in "${base[0]}"`); + process.exit(1); } } -const tmpPath = fs.mkdtempSync(path.resolve(os.tmpdir(), 'electron-gn-asar-')) +const tmpPath = fs.mkdtempSync(path.resolve(os.tmpdir(), 'electron-gn-asar-')); try { // Copy all files to a tmp dir to avoid including scrap files in the ASAR for (const file of files) { - const newLocation = path.resolve(tmpPath, path.relative(base[0], file)) - fs.mkdirsSync(path.dirname(newLocation)) - fs.writeFileSync(newLocation, fs.readFileSync(file)) + const newLocation = path.resolve(tmpPath, path.relative(base[0], file)); + fs.mkdirsSync(path.dirname(newLocation)); + fs.writeFileSync(newLocation, fs.readFileSync(file)); } } catch (err) { - console.error('Unexpected error while generating ASAR', err) + console.error('Unexpected error while generating ASAR', err); fs.remove(tmpPath) .then(() => process.exit(1)) - .catch(() => process.exit(1)) - return + .catch(() => process.exit(1)); + return; } // Create the ASAR archive asar.createPackageWithOptions(tmpPath, out[0], {}) .catch(err => { const exit = () => { - console.error('Unexpected error while generating ASAR', err) - process.exit(1) - } - fs.remove(tmpPath).then(exit).catch(exit) - }).then(() => fs.remove(tmpPath)) + console.error('Unexpected error while generating ASAR', err); + process.exit(1); + }; + fs.remove(tmpPath).then(exit).catch(exit); + }).then(() => fs.remove(tmpPath)); diff --git a/script/gn-check.js b/script/gn-check.js index 4652c7bebd7e7..632d545c66000 100644 --- a/script/gn-check.js +++ b/script/gn-check.js @@ -1,34 +1,34 @@ -const cp = require('child_process') -const path = require('path') +const cp = require('child_process'); +const path = require('path'); -const { getOutDir } = require('./lib/utils') +const { getOutDir } = require('./lib/utils'); -const SOURCE_ROOT = path.normalize(path.dirname(__dirname)) -const DEPOT_TOOLS = path.resolve(SOURCE_ROOT, '..', 'third_party', 'depot_tools') -const OUT_DIR = getOutDir() +const SOURCE_ROOT = path.normalize(path.dirname(__dirname)); +const DEPOT_TOOLS = path.resolve(SOURCE_ROOT, '..', 'third_party', 'depot_tools'); +const OUT_DIR = getOutDir(); if (!OUT_DIR) { - throw new Error(`No viable out dir: one of Debug, Testing, or Release must exist.`) + throw new Error(`No viable out dir: one of Debug, Testing, or Release must exist.`); } const env = Object.assign({ CHROMIUM_BUILDTOOLS_PATH: path.resolve(SOURCE_ROOT, '..', 'buildtools'), DEPOT_TOOLS_WIN_TOOLCHAIN: '0' -}, process.env) +}, process.env); // Users may not have depot_tools in PATH. -env.PATH = `${env.PATH}${path.delimiter}${DEPOT_TOOLS}` +env.PATH = `${env.PATH}${path.delimiter}${DEPOT_TOOLS}`; const gnCheckDirs = [ '//electron:electron_lib', '//electron:electron_app', '//electron:manifests', '//electron/shell/common/api:mojo' -] +]; for (const dir of gnCheckDirs) { - const args = ['check', `../out/${OUT_DIR}`, dir] - const result = cp.spawnSync('gn', args, { env, stdio: 'inherit' }) - if (result.status !== 0) process.exit(result.status) + const args = ['check', `../out/${OUT_DIR}`, dir]; + const result = cp.spawnSync('gn', args, { env, stdio: 'inherit' }); + if (result.status !== 0) process.exit(result.status); } -process.exit(0) +process.exit(0); diff --git a/script/lib/config.py b/script/lib/config.py index ffae54190a42d..9985183c98bed 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -21,10 +21,23 @@ PLATFORM = { 'cygwin': 'win32', 'darwin': 'darwin', + 'linux': 'linux', 'linux2': 'linux', 'win32': 'win32', }[sys.platform] +LINUX_BINARIES = [ + 'electron', + 'chrome-sandbox', + 'crashpad_handler', + 'libffmpeg.so', + 'libGLESv2.so', + 'libEGL.so', + 'swiftshader/libGLESv2.so', + 'swiftshader/libEGL.so', + 'libvk_swiftshader.so' +] + verbose_mode = False diff --git a/script/lib/git.py b/script/lib/git.py index d391f64d09736..933146cd770e4 100644 --- a/script/lib/git.py +++ b/script/lib/git.py @@ -7,7 +7,9 @@ """ import os +import re import subprocess +import sys def is_repo_root(path): @@ -42,7 +44,7 @@ def get_repo_root(path): def am(repo, patch_data, threeway=False, directory=None, exclude=None, committer_name=None, committer_email=None): - args = [] + args = ['--ignore-space-change'] if threeway: args += ['--3way'] if directory is not None: @@ -126,7 +128,7 @@ def reset(repo): def commit(repo, author, message): - """ Commit whatever in the index is now.""" + """Commit whatever in the index is now.""" # Let's setup committer info so git won't complain about it being missing. # TODO: Is there a better way to set committer's name and email? @@ -142,3 +144,143 @@ def commit(repo, author, message): return_code = subprocess.call(args, env=env) committed_successfully = (return_code == 0) return committed_successfully + +def get_upstream_head(repo): + args = [ + 'git', + '-C', + repo, + 'rev-parse', + '--verify', + 'refs/patches/upstream-head', + ] + return subprocess.check_output(args).strip() + +def get_commit_count(repo, commit_range): + args = [ + 'git', + '-C', + repo, + 'rev-list', + '--count', + commit_range + ] + return int(subprocess.check_output(args).strip()) + +def guess_base_commit(repo): + """Guess which commit the patches might be based on""" + try: + upstream_head = get_upstream_head(repo) + num_commits = get_commit_count(repo, upstream_head + '..') + return [upstream_head, num_commits] + except subprocess.CalledProcessError: + args = [ + 'git', + '-C', + repo, + 'describe', + '--tags', + ] + return subprocess.check_output(args).rsplit('-', 2)[0:2] + + +def format_patch(repo, since): + args = [ + 'git', + '-C', + repo, + '-c', + 'core.attributesfile=' + os.path.join(os.path.dirname(os.path.realpath(__file__)), '.electron.attributes'), + # Ensure it is not possible to match anything + # Disabled for now as we have consistent chunk headers + # '-c', + # 'diff.electron.xfuncname=$^', + 'format-patch', + '--keep-subject', + '--no-stat', + '--stdout', + + # Per RFC 3676 the signature is separated from the body by a line with + # '-- ' on it. If the signature option is omitted the signature defaults + # to the Git version number. + '--no-signature', + + # The name of the parent commit object isn't useful information in this + # context, so zero it out to avoid needless patch-file churn. + '--zero-commit', + + # Some versions of git print out different numbers of characters in the + # 'index' line of patches, so pass --full-index to get consistent + # behaviour. + '--full-index', + since + ] + return subprocess.check_output(args) + + +def split_patches(patch_data): + """Split a concatenated series of patches into N separate patches""" + patches = [] + patch_start = re.compile('^From [0-9a-f]+ ') + for line in patch_data.splitlines(): + if patch_start.match(line): + patches.append([]) + patches[-1].append(line) + return patches + + +def munge_subject_to_filename(subject): + """Derive a suitable filename from a commit's subject""" + if subject.endswith('.patch'): + subject = subject[:-6] + return re.sub(r'[^A-Za-z0-9-]+', '_', subject).strip('_').lower() + '.patch' + + +def get_file_name(patch): + """Return the name of the file to which the patch should be written""" + for line in patch: + if line.startswith('Patch-Filename: '): + return line[len('Patch-Filename: '):] + # If no patch-filename header, munge the subject. + for line in patch: + if line.startswith('Subject: '): + return munge_subject_to_filename(line[len('Subject: '):]) + + +def remove_patch_filename(patch): + """Strip out the Patch-Filename trailer from a patch's message body""" + force_keep_next_line = False + for i, l in enumerate(patch): + is_patchfilename = l.startswith('Patch-Filename: ') + next_is_patchfilename = i < len(patch) - 1 and patch[i+1].startswith('Patch-Filename: ') + if not force_keep_next_line and (is_patchfilename or (next_is_patchfilename and len(l.rstrip()) == 0)): + pass # drop this line + else: + yield l + force_keep_next_line = l.startswith('Subject: ') + + +def export_patches(repo, out_dir, patch_range=None): + if patch_range is None: + patch_range, num_patches = guess_base_commit(repo) + sys.stderr.write("Exporting {} patches since {}\n".format(num_patches, patch_range)) + patch_data = format_patch(repo, patch_range) + patches = split_patches(patch_data) + + try: + os.mkdir(out_dir) + except OSError: + pass + + # remove old patches, so that deleted commits are correctly reflected in the + # patch files (as a removed file) + for p in os.listdir(out_dir): + if p.endswith('.patch'): + os.remove(os.path.join(out_dir, p)) + + with open(os.path.join(out_dir, '.patches'), 'w') as pl: + for patch in patches: + filename = get_file_name(patch) + with open(os.path.join(out_dir, filename), 'w') as f: + f.write('\n'.join(remove_patch_filename(patch)).rstrip('\n') + '\n') + pl.write(filename + '\n') diff --git a/script/lib/patches.py b/script/lib/patches.py index 68035f169bc1a..2365303de0cbd 100644 --- a/script/lib/patches.py +++ b/script/lib/patches.py @@ -8,10 +8,13 @@ def read_patch(patch_dir, patch_filename): """Read a patch from |patch_dir/filename| and amend the commit message with metadata about the patch file it came from.""" ret = [] - with codecs.open(os.path.join(patch_dir, patch_filename), encoding='utf-8') as f: + added_filename_line = False + patch_path = os.path.join(patch_dir, patch_filename) + with codecs.open(patch_path, encoding='utf-8') as f: for l in f.readlines(): - if l.startswith('diff -'): + if not added_filename_line and (l.startswith('diff -') or l.startswith('---')): ret.append('Patch-Filename: {}\n'.format(patch_filename)) + added_filename_line = True ret.append(l) return ''.join(ret) diff --git a/script/lib/util.py b/script/lib/util.py index 5ef42f98f94c0..4af2271b7542a 100644 --- a/script/lib/util.py +++ b/script/lib/util.py @@ -16,7 +16,11 @@ import sys import tarfile import tempfile -import urllib2 +# Python 3 / 2 compat import +try: + from urllib.request import urlopen +except ImportError: + from urllib2 import urlopen import zipfile from lib.config import is_verbose_mode, PLATFORM @@ -69,8 +73,12 @@ def download(text, url, path): ssl._create_default_https_context = ssl._create_unverified_context print("Downloading %s to %s" % (url, path)) - web_file = urllib2.urlopen(url) - file_size = int(web_file.info().getheaders("Content-Length")[0]) + web_file = urlopen(url) + info = web_file.info() + if hasattr(info, 'getheader'): + file_size = int(info.getheaders("Content-Length")[0]) + else: + file_size = int(info.get("Content-Length")[0]) downloaded_size = 0 block_size = 4096 @@ -112,10 +120,11 @@ def extract_zip(zip_path, destination): def make_zip(zip_file_path, files, dirs): safe_unlink(zip_file_path) if sys.platform == 'darwin': - files += dirs - execute(['zip', '-r', '-y', zip_file_path] + files) + allfiles = files + dirs + execute(['zip', '-r', '-y', zip_file_path] + allfiles) else: - zip_file = zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_DEFLATED) + zip_file = zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_DEFLATED, + allowZip64=True) for filename in files: zip_file.write(filename, filename) for dirname in dirs: @@ -258,3 +267,14 @@ def get_buildtools_executable(name): if sys.platform == 'win32': path += '.exe' return path + +def get_objcopy_path(target_cpu): + if PLATFORM != 'linux': + raise Exception( + "get_objcopy_path: unexpected platform '{0}'".format(PLATFORM)) + + if target_cpu != 'x64': + raise Exception( + "get_objcopy_path: unexpected target cpu '{0}'".format(target_cpu)) + return os.path.join(SRC_DIR, 'third_party', 'binutils', 'Linux_x64', + 'Release', 'bin', 'objcopy') diff --git a/script/lib/utils.js b/script/lib/utils.js index 56a4c8b268864..f1dce243d1095 100644 --- a/script/lib/utils.js +++ b/script/lib/utils.js @@ -1,76 +1,76 @@ -const { GitProcess } = require('dugite') -const fs = require('fs') -const path = require('path') +const { GitProcess } = require('dugite'); +const fs = require('fs'); +const path = require('path'); -const ELECTRON_DIR = path.resolve(__dirname, '..', '..') -const SRC_DIR = path.resolve(ELECTRON_DIR, '..') +const ELECTRON_DIR = path.resolve(__dirname, '..', '..'); +const SRC_DIR = path.resolve(ELECTRON_DIR, '..'); -require('colors') -const pass = '✓'.green -const fail = '✗'.red +require('colors'); +const pass = '✓'.green; +const fail = '✗'.red; function getElectronExec () { - const OUT_DIR = getOutDir() + const OUT_DIR = getOutDir(); switch (process.platform) { case 'darwin': - return `out/${OUT_DIR}/Electron.app/Contents/MacOS/Electron` + return `out/${OUT_DIR}/Electron.app/Contents/MacOS/Electron`; case 'win32': - return `out/${OUT_DIR}/electron.exe` + return `out/${OUT_DIR}/electron.exe`; case 'linux': - return `out/${OUT_DIR}/electron` + return `out/${OUT_DIR}/electron`; default: - throw new Error('Unknown platform') + throw new Error('Unknown platform'); } } function getOutDir (shouldLog) { if (process.env.ELECTRON_OUT_DIR) { - return process.env.ELECTRON_OUT_DIR + return process.env.ELECTRON_OUT_DIR; } else { - for (const buildType of ['Debug', 'Testing', 'Release', 'Default']) { - const outPath = path.resolve(SRC_DIR, 'out', buildType) + for (const buildType of ['Testing', 'Release', 'Default']) { + const outPath = path.resolve(SRC_DIR, 'out', buildType); if (fs.existsSync(outPath)) { - if (shouldLog) console.log(`OUT_DIR is: ${buildType}`) - return buildType + if (shouldLog) console.log(`OUT_DIR is: ${buildType}`); + return buildType; } } } } function getAbsoluteElectronExec () { - return path.resolve(SRC_DIR, getElectronExec()) + return path.resolve(SRC_DIR, getElectronExec()); } async function handleGitCall (args, gitDir) { - const details = await GitProcess.exec(args, gitDir) + const details = await GitProcess.exec(args, gitDir); if (details.exitCode === 0) { - return details.stdout.replace(/^\*|\s+|\s+$/, '') + return details.stdout.replace(/^\*|\s+|\s+$/, ''); } else { - const error = GitProcess.parseError(details.stderr) - console.log(`${fail} couldn't parse git process call: `, error) - process.exit(1) + const error = GitProcess.parseError(details.stderr); + console.log(`${fail} couldn't parse git process call: `, error); + process.exit(1); } } async function getCurrentBranch (gitDir) { - let branch = await handleGitCall(['rev-parse', '--abbrev-ref', 'HEAD'], gitDir) - if (branch !== 'master' && !branch.match(/[0-9]+-[0-9]+-x/)) { - const lastCommit = await handleGitCall(['rev-parse', 'HEAD'], gitDir) + let branch = await handleGitCall(['rev-parse', '--abbrev-ref', 'HEAD'], gitDir); + if (branch !== 'master' && !branch.match(/[0-9]+-[0-9]+-x$/) && !branch.match(/[0-9]+-x-y$/)) { + const lastCommit = await handleGitCall(['rev-parse', 'HEAD'], gitDir); const branches = (await handleGitCall([ 'branch', '--contains', lastCommit, '--remote' - ], gitDir)).split('\n') + ], gitDir)).split('\n'); - branch = branches.filter(b => b.trim() === 'master' || b.match(/[0-9]+-[0-9]+-x/))[0] + branch = branches.filter(b => b.trim() === 'master' || b.trim().match(/[0-9]+-[0-9]+-x$/) || b.trim().match(/[0-9]+-x-y$/))[0]; if (!branch) { - console.log(`${fail} no release branch exists for this ref`) - process.exit(1) + console.log(`${fail} no release branch exists for this ref`); + process.exit(1); } - if (branch.startsWith('origin/')) branch = branch.substr('origin/'.length) + if (branch.startsWith('origin/')) branch = branch.substr('origin/'.length); } - return branch.trim() + return branch.trim(); } module.exports = { @@ -80,4 +80,4 @@ module.exports = { getAbsoluteElectronExec, ELECTRON_DIR, SRC_DIR -} +}; diff --git a/script/lint.js b/script/lint.js index e34a8e821bb67..b69ba10ec79cc 100755 --- a/script/lint.js +++ b/script/lint.js @@ -1,14 +1,14 @@ #!/usr/bin/env node -const { GitProcess } = require('dugite') -const childProcess = require('child_process') -const fs = require('fs') -const klaw = require('klaw') -const minimist = require('minimist') -const path = require('path') +const { GitProcess } = require('dugite'); +const childProcess = require('child_process'); +const fs = require('fs'); +const klaw = require('klaw'); +const minimist = require('minimist'); +const path = require('path'); -const SOURCE_ROOT = path.normalize(path.dirname(__dirname)) -const DEPOT_TOOLS = path.resolve(SOURCE_ROOT, '..', 'third_party', 'depot_tools') +const SOURCE_ROOT = path.normalize(path.dirname(__dirname)); +const DEPOT_TOOLS = path.resolve(SOURCE_ROOT, '..', 'third_party', 'depot_tools'); const BLACKLIST = new Set([ ['shell', 'browser', 'mac', 'atom_application.h'], @@ -30,40 +30,44 @@ const BLACKLIST = new Set([ ['spec', 'ts-smoke', 'electron', 'main.ts'], ['spec', 'ts-smoke', 'electron', 'renderer.ts'], ['spec', 'ts-smoke', 'runner.js'] -].map(tokens => path.join(SOURCE_ROOT, ...tokens))) +].map(tokens => path.join(SOURCE_ROOT, ...tokens))); function spawnAndCheckExitCode (cmd, args, opts) { - opts = Object.assign({ stdio: 'inherit' }, opts) - const status = childProcess.spawnSync(cmd, args, opts).status - if (status) process.exit(status) + opts = Object.assign({ stdio: 'inherit' }, opts); + const status = childProcess.spawnSync(cmd, args, opts).status; + if (status) process.exit(status); } function cpplint (args) { - const result = childProcess.spawnSync('cpplint.py', args, { encoding: 'utf8' }) + const result = childProcess.spawnSync('cpplint.py', args, { encoding: 'utf8' }); // cpplint.py writes EVERYTHING to stderr, including status messages if (result.stderr) { for (const line of result.stderr.split(/[\r\n]+/)) { if (line.length && !line.startsWith('Done processing ') && line !== 'Total errors found: 0') { - console.warn(line) + console.warn(line); } } } if (result.status) { - process.exit(result.status) + process.exit(result.status); } } +function isObjCHeader (filename) { + return /\/(mac|cocoa)\//.test(filename); +} + const LINTERS = [ { key: 'c++', roots: ['shell', 'native_mate'], - test: filename => filename.endsWith('.cc') || filename.endsWith('.h'), + test: filename => filename.endsWith('.cc') || (filename.endsWith('.h') && !isObjCHeader(filename)), run: (opts, filenames) => { if (opts.fix) { - spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]) + spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]); } else { - spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]) + spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]); } - cpplint(filenames) + cpplint(filenames); } }, { key: 'objc', @@ -71,27 +75,27 @@ const LINTERS = [ { test: filename => filename.endsWith('.mm'), run: (opts, filenames) => { if (opts.fix) { - spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]) + spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]); } else { - spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]) + spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]); } const filter = [ '-readability/casting', '-whitespace/braces', '-whitespace/indent', '-whitespace/parens' - ] - cpplint(['--extensions=mm', `--filter=${filter.join(',')}`, ...filenames]) + ]; + cpplint(['--extensions=mm', `--filter=${filter.join(',')}`, ...filenames]); } }, { key: 'python', roots: ['script'], test: filename => filename.endsWith('.py'), run: (opts, filenames) => { - const rcfile = path.join(DEPOT_TOOLS, 'pylintrc') - const args = ['--rcfile=' + rcfile, ...filenames] - const env = Object.assign({ PYTHONPATH: path.join(SOURCE_ROOT, 'script') }, process.env) - spawnAndCheckExitCode('pylint.py', args, { env }) + const rcfile = path.join(DEPOT_TOOLS, 'pylintrc'); + const args = ['--rcfile=' + rcfile, ...filenames]; + const env = Object.assign({ PYTHONPATH: path.join(SOURCE_ROOT, 'script') }, process.env); + spawnAndCheckExitCode('pylint.py', args, { env }); } }, { key: 'javascript', @@ -99,10 +103,10 @@ const LINTERS = [ { ignoreRoots: ['spec/node_modules'], test: filename => filename.endsWith('.js') || filename.endsWith('.ts'), run: (opts, filenames) => { - const cmd = path.join(SOURCE_ROOT, 'node_modules', '.bin', 'eslint') - const args = [ '--cache', '--ext', '.js,.ts', ...filenames ] - if (opts.fix) args.unshift('--fix') - spawnAndCheckExitCode(cmd, args, { cwd: SOURCE_ROOT }) + const cmd = path.join(SOURCE_ROOT, 'node_modules', '.bin', 'eslint'); + const args = [ '--cache', '--ext', '.js,.ts', ...filenames ]; + if (opts.fix) args.unshift('--fix'); + spawnAndCheckExitCode(cmd, args, { cwd: SOURCE_ROOT }); } }, { key: 'gn', @@ -113,24 +117,24 @@ const LINTERS = [ { const env = Object.assign({ CHROMIUM_BUILDTOOLS_PATH: path.resolve(SOURCE_ROOT, '..', 'buildtools'), DEPOT_TOOLS_WIN_TOOLCHAIN: '0' - }, process.env) + }, process.env); // Users may not have depot_tools in PATH. - env.PATH = `${env.PATH}${path.delimiter}${DEPOT_TOOLS}` - const args = ['format', filename] - if (!opts.fix) args.push('--dry-run') - const result = childProcess.spawnSync('gn', args, { env, stdio: 'inherit', shell: true }) + env.PATH = `${env.PATH}${path.delimiter}${DEPOT_TOOLS}`; + const args = ['format', filename]; + if (!opts.fix) args.push('--dry-run'); + const result = childProcess.spawnSync('gn', args, { env, stdio: 'inherit', shell: true }); if (result.status === 0) { - return true + return true; } else if (result.status === 2) { - console.log(`GN format errors in "${filename}". Run 'gn format "${filename}"' or rerun with --fix to fix them.`) - return false + console.log(`GN format errors in "${filename}". Run 'gn format "${filename}"' or rerun with --fix to fix them.`); + return false; } else { - console.log(`Error running 'gn format --dry-run "${filename}"': exit code ${result.status}`) - return false + console.log(`Error running 'gn format --dry-run "${filename}"': exit code ${result.status}`); + return false; } - }).every(x => x) + }).every(x => x); if (!allOk) { - process.exit(1) + process.exit(1); } } }, { @@ -138,155 +142,155 @@ const LINTERS = [ { roots: ['patches'], test: () => true, run: () => { - const patchesDir = path.resolve(__dirname, '../patches') + const patchesDir = path.resolve(__dirname, '../patches'); for (const patchTarget of fs.readdirSync(patchesDir)) { - const targetDir = path.resolve(patchesDir, patchTarget) + const targetDir = path.resolve(patchesDir, patchTarget); // If the config does not exist that is OK, we just skip this dir - const targetConfig = path.resolve(targetDir, 'config.json') - if (!fs.existsSync(targetConfig)) continue + const targetConfig = path.resolve(targetDir, 'config.json'); + if (!fs.existsSync(targetConfig)) continue; - const config = JSON.parse(fs.readFileSync(targetConfig, 'utf8')) + const config = JSON.parse(fs.readFileSync(targetConfig, 'utf8')); for (const key of Object.keys(config)) { // The directory the config points to should exist - const targetPatchesDir = path.resolve(__dirname, '../../..', key) - if (!fs.existsSync(targetPatchesDir)) throw new Error(`target patch directory: "${targetPatchesDir}" does not exist`) + const targetPatchesDir = path.resolve(__dirname, '../../..', key); + if (!fs.existsSync(targetPatchesDir)) throw new Error(`target patch directory: "${targetPatchesDir}" does not exist`); // We need a .patches file - const dotPatchesPath = path.resolve(targetPatchesDir, '.patches') - if (!fs.existsSync(dotPatchesPath)) throw new Error(`.patches file: "${dotPatchesPath}" does not exist`) + const dotPatchesPath = path.resolve(targetPatchesDir, '.patches'); + if (!fs.existsSync(dotPatchesPath)) throw new Error(`.patches file: "${dotPatchesPath}" does not exist`); // Read the patch list - const patchFileList = fs.readFileSync(dotPatchesPath, 'utf8').trim().split('\n') - const patchFileSet = new Set(patchFileList) + const patchFileList = fs.readFileSync(dotPatchesPath, 'utf8').trim().split('\n'); + const patchFileSet = new Set(patchFileList); patchFileList.reduce((seen, file) => { if (seen.has(file)) { - throw new Error(`'${file}' is listed in ${dotPatchesPath} more than once`) + throw new Error(`'${file}' is listed in ${dotPatchesPath} more than once`); } - return seen.add(file) - }, new Set()) - if (patchFileList.length !== patchFileSet.size) throw new Error('each patch file should only be in the .patches file once') + return seen.add(file); + }, new Set()); + if (patchFileList.length !== patchFileSet.size) throw new Error('each patch file should only be in the .patches file once'); for (const file of fs.readdirSync(targetPatchesDir)) { // Ignore the .patches file and READMEs - if (file === '.patches' || file === 'README.md') continue + if (file === '.patches' || file === 'README.md') continue; if (!patchFileSet.has(file)) { - throw new Error(`Expected the .patches file at "${dotPatchesPath}" to contain a patch file ("${file}") present in the directory but it did not`) + throw new Error(`Expected the .patches file at "${dotPatchesPath}" to contain a patch file ("${file}") present in the directory but it did not`); } - patchFileSet.delete(file) + patchFileSet.delete(file); } // If anything is left in this set, it means it did not exist on disk if (patchFileSet.size > 0) { - throw new Error(`Expected all the patch files listed in the .patches file at "${dotPatchesPath}" to exist but some did not:\n${JSON.stringify([...patchFileSet.values()], null, 2)}`) + throw new Error(`Expected all the patch files listed in the .patches file at "${dotPatchesPath}" to exist but some did not:\n${JSON.stringify([...patchFileSet.values()], null, 2)}`); } } } } -}] +}]; function parseCommandLine () { - let help + let help; const opts = minimist(process.argv.slice(2), { boolean: [ 'c++', 'objc', 'javascript', 'python', 'gn', 'patches', 'help', 'changed', 'fix', 'verbose', 'only' ], alias: { 'c++': ['cc', 'cpp', 'cxx'], javascript: ['js', 'es'], python: 'py', changed: 'c', help: 'h', verbose: 'v' }, - unknown: arg => { help = true } - }) + unknown: arg => { help = true; } + }); if (help || opts.help) { - console.log('Usage: script/lint.js [--cc] [--js] [--py] [-c|--changed] [-h|--help] [-v|--verbose] [--fix] [--only -- file1 file2]') - process.exit(0) + console.log('Usage: script/lint.js [--cc] [--js] [--py] [-c|--changed] [-h|--help] [-v|--verbose] [--fix] [--only -- file1 file2]'); + process.exit(0); } - return opts + return opts; } async function findChangedFiles (top) { - const result = await GitProcess.exec(['diff', '--name-only', '--cached'], top) + const result = await GitProcess.exec(['diff', '--name-only', '--cached'], top); if (result.exitCode !== 0) { - console.log('Failed to find changed files', GitProcess.parseError(result.stderr)) - process.exit(1) + console.log('Failed to find changed files', GitProcess.parseError(result.stderr)); + process.exit(1); } - const relativePaths = result.stdout.split(/\r\n|\r|\n/g) - const absolutePaths = relativePaths.map(x => path.join(top, x)) - return new Set(absolutePaths) + const relativePaths = result.stdout.split(/\r\n|\r|\n/g); + const absolutePaths = relativePaths.map(x => path.join(top, x)); + return new Set(absolutePaths); } async function findMatchingFiles (top, test) { return new Promise((resolve, reject) => { - const matches = [] + const matches = []; klaw(top, { filter: f => path.basename(f) !== '.bin' }) .on('end', () => resolve(matches)) .on('data', item => { if (test(item.path)) { - matches.push(item.path) + matches.push(item.path); } - }) - }) + }); + }); } async function findFiles (args, linter) { - let filenames = [] - let whitelist = null + let filenames = []; + let whitelist = null; // build the whitelist if (args.changed) { - whitelist = await findChangedFiles(SOURCE_ROOT) + whitelist = await findChangedFiles(SOURCE_ROOT); if (!whitelist.size) { - return [] + return []; } } else if (args.only) { - whitelist = new Set(args._) + whitelist = new Set(args._); } // accumulate the raw list of files for (const root of linter.roots) { - const files = await findMatchingFiles(path.join(SOURCE_ROOT, root), linter.test) - filenames.push(...files) + const files = await findMatchingFiles(path.join(SOURCE_ROOT, root), linter.test); + filenames.push(...files); } for (const ignoreRoot of (linter.ignoreRoots) || []) { - const ignorePath = path.join(SOURCE_ROOT, ignoreRoot) - if (!fs.existsSync(ignorePath)) continue + const ignorePath = path.join(SOURCE_ROOT, ignoreRoot); + if (!fs.existsSync(ignorePath)) continue; - const ignoreFiles = new Set(await findMatchingFiles(ignorePath, linter.test)) - filenames = filenames.filter(fileName => !ignoreFiles.has(fileName)) + const ignoreFiles = new Set(await findMatchingFiles(ignorePath, linter.test)); + filenames = filenames.filter(fileName => !ignoreFiles.has(fileName)); } // remove blacklisted files - filenames = filenames.filter(x => !BLACKLIST.has(x)) + filenames = filenames.filter(x => !BLACKLIST.has(x)); // if a whitelist exists, remove anything not in it if (whitelist) { - filenames = filenames.filter(x => whitelist.has(x)) + filenames = filenames.filter(x => whitelist.has(x)); } // it's important that filenames be relative otherwise clang-format will // produce patches with absolute paths in them, which `git apply` will refuse // to apply. - return filenames.map(x => path.relative(SOURCE_ROOT, x)) + return filenames.map(x => path.relative(SOURCE_ROOT, x)); } async function main () { - const opts = parseCommandLine() + const opts = parseCommandLine(); // no mode specified? run 'em all - if (!opts['c++'] && !opts.javascript && !opts.python && !opts.gn && !opts.patches) { - opts['c++'] = opts.javascript = opts.python = opts.gn = opts.patches = true + if (!opts['c++'] && !opts.javascript && !opts.objc && !opts.python && !opts.gn && !opts.patches) { + opts['c++'] = opts.javascript = opts.objc = opts.python = opts.gn = opts.patches = true; } - const linters = LINTERS.filter(x => opts[x.key]) + const linters = LINTERS.filter(x => opts[x.key]); for (const linter of linters) { - const filenames = await findFiles(opts, linter) + const filenames = await findFiles(opts, linter); if (filenames.length) { - if (opts.verbose) { console.log(`linting ${filenames.length} ${linter.key} ${filenames.length === 1 ? 'file' : 'files'}`) } - linter.run(opts, filenames) + if (opts.verbose) { console.log(`linting ${filenames.length} ${linter.key} ${filenames.length === 1 ? 'file' : 'files'}`); } + linter.run(opts, filenames); } } } if (process.mainModule === module) { main().catch((error) => { - console.error(error) - process.exit(1) - }) + console.error(error); + process.exit(1); + }); } diff --git a/script/nan-spec-runner.js b/script/nan-spec-runner.js index 5230e4e00f375..6672a83ac4a05 100644 --- a/script/nan-spec-runner.js +++ b/script/nan-spec-runner.js @@ -1,49 +1,49 @@ -const cp = require('child_process') -const fs = require('fs') -const path = require('path') +const cp = require('child_process'); +const fs = require('fs'); +const path = require('path'); -const BASE = path.resolve(__dirname, '../..') -const NAN_DIR = path.resolve(BASE, 'third_party', 'nan') -const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx' +const BASE = path.resolve(__dirname, '../..'); +const NAN_DIR = path.resolve(BASE, 'third_party', 'nan'); +const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx'; -const utils = require('./lib/utils') -const { YARN_VERSION } = require('./yarn') +const utils = require('./lib/utils'); +const { YARN_VERSION } = require('./yarn'); if (!process.mainModule) { - throw new Error('Must call the nan spec runner directly') + throw new Error('Must call the nan spec runner directly'); } async function main () { - const nodeDir = path.resolve(BASE, `out/${utils.getOutDir(true)}/gen/node_headers`) + const nodeDir = path.resolve(BASE, `out/${utils.getOutDir(true)}/gen/node_headers`); const env = Object.assign({}, process.env, { npm_config_nodedir: nodeDir, - npm_config_msvs_version: '2017', + npm_config_msvs_version: '2019', npm_config_arch: process.env.NPM_CONFIG_ARCH - }) + }); const { status: buildStatus } = cp.spawnSync(NPX_CMD, ['node-gyp', 'rebuild', '--directory', 'test'], { env, cwd: NAN_DIR, stdio: 'inherit' - }) + }); if (buildStatus !== 0) { - console.error('Failed to build nan test modules') - return process.exit(buildStatus) + console.error('Failed to build nan test modules'); + return process.exit(buildStatus); } const { status: installStatus } = cp.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install'], { env, cwd: NAN_DIR, stdio: 'inherit' - }) + }); if (installStatus !== 0) { - console.error('Failed to install nan node_modules') - return process.exit(installStatus) + console.error('Failed to install nan node_modules'); + return process.exit(installStatus); } - const DISABLED_TESTS = ['nannew-test.js'] + const DISABLED_TESTS = ['nannew-test.js']; const testsToRun = fs.readdirSync(path.resolve(NAN_DIR, 'test', 'js')) .filter(test => !DISABLED_TESTS.includes(test)) - .map(test => `test/js/${test}`) + .map(test => `test/js/${test}`); const testChild = cp.spawn(utils.getAbsoluteElectronExec(), ['node_modules/.bin/tap', ...testsToRun], { env: { @@ -52,13 +52,13 @@ async function main () { }, cwd: NAN_DIR, stdio: 'inherit' - }) + }); testChild.on('exit', (testCode) => { - process.exit(testCode) - }) + process.exit(testCode); + }); } main().catch((err) => { - console.error('An unhandled error occurred in the nan spec runner', err) - process.exit(1) -}) + console.error('An unhandled error occurred in the nan spec runner', err); + process.exit(1); +}); diff --git a/script/node-disabled-tests.json b/script/node-disabled-tests.json index 66c5a21b6d37e..e587ba4b01f49 100644 --- a/script/node-disabled-tests.json +++ b/script/node-disabled-tests.json @@ -166,7 +166,6 @@ "parallel/test-v8-flags", "parallel/test-v8-coverage", "parallel/test-vm-basic", - "parallel/test-vm-codegen", "parallel/test-vm-parse-abort-on-uncaught-exception", "parallel/test-vm-syntax-error-message", "parallel/test-warn-sigprof", diff --git a/script/node-spec-runner.js b/script/node-spec-runner.js index 91315ad7d41ee..4abb168ab39e3 100644 --- a/script/node-spec-runner.js +++ b/script/node-spec-runner.js @@ -1,47 +1,86 @@ -const cp = require('child_process') -const fs = require('fs') -const path = require('path') +const cp = require('child_process'); +const fs = require('fs'); +const path = require('path'); -const BASE = path.resolve(__dirname, '../..') -const NODE_DIR = path.resolve(BASE, 'third_party', 'electron_node') -const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx' -const JUNIT_DIR = process.argv[2] ? path.resolve(process.argv[2]) : null -const TAP_FILE_NAME = 'test.tap' +const args = require('minimist')(process.argv.slice(2), { + boolean: ['default'], + string: ['jUnitDir'] +}); -const utils = require('./lib/utils') -const { YARN_VERSION } = require('./yarn') +const BASE = path.resolve(__dirname, '../..'); +const DISABLED_TESTS = require('./node-disabled-tests.json'); +const NODE_DIR = path.resolve(BASE, 'third_party', 'electron_node'); +const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx'; +const JUNIT_DIR = args.jUnitDir ? path.resolve(args.jUnitDir) : null; +const TAP_FILE_NAME = 'test.tap'; + +const utils = require('./lib/utils'); +const { YARN_VERSION } = require('./yarn'); if (!process.mainModule) { - throw new Error('Must call the node spec runner directly') + throw new Error('Must call the node spec runner directly'); } +const defaultOptions = [ + 'tools/test.py', + '-p', + 'tap', + '--logfile', + TAP_FILE_NAME, + '--mode=debug', + 'default', + `--skip-tests=${DISABLED_TESTS.join(',')}`, + '--shell', + utils.getAbsoluteElectronExec(), + '-J' +]; + +const getCustomOptions = () => { + let customOptions = ['tools/test.py']; + + // Add all custom arguments. + const extra = process.argv.slice(2); + if (extra) { + customOptions = customOptions.concat(extra); + } + + // We need this unilaterally or Node.js will try + // to run from out/Release/node. + customOptions = customOptions.concat([ + '--shell', + utils.getAbsoluteElectronExec() + ]); + + return customOptions; +}; + async function main () { - const DISABLED_TESTS = require('./node-disabled-tests.json') + const options = args.default ? defaultOptions : getCustomOptions(); - const testChild = cp.spawn('python', ['tools/test.py', '--verbose', '-p', 'tap', '--logfile', TAP_FILE_NAME, '--mode=debug', 'default', `--skip-tests=${DISABLED_TESTS.join(',')}`, '--shell', utils.getAbsoluteElectronExec(), '-J'], { + const testChild = cp.spawn('python', options, { env: { ...process.env, ELECTRON_RUN_AS_NODE: 'true' }, cwd: NODE_DIR, stdio: 'inherit' - }) + }); testChild.on('exit', (testCode) => { if (JUNIT_DIR) { - fs.mkdirSync(JUNIT_DIR) - const converterStream = require('tap-xunit')() + fs.mkdirSync(JUNIT_DIR); + const converterStream = require('tap-xunit')(); fs.createReadStream( path.resolve(NODE_DIR, TAP_FILE_NAME) ).pipe(converterStream).pipe( fs.createWriteStream(path.resolve(JUNIT_DIR, 'nodejs.xml')) ).on('close', () => { - process.exit(testCode) - }) + process.exit(testCode); + }); } - }) + }); } main().catch((err) => { - console.error('An unhandled error occurred in the node spec runner', err) - process.exit(1) -}) + console.error('An unhandled error occurred in the node spec runner', err); + process.exit(1); +}); diff --git a/script/release/ci-release-build.js b/script/release/ci-release-build.js index 84402886fe31b..70831255ceb30 100644 --- a/script/release/ci-release-build.js +++ b/script/release/ci-release-build.js @@ -1,16 +1,18 @@ -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); -const assert = require('assert') -const request = require('request') -const buildAppVeyorURL = 'https://ci.appveyor.com/api/builds' -const circleCIPipelineURL = 'https://circleci.com/api/v2/project/gh/electron/electron/pipeline' -const vstsURL = 'https://github.visualstudio.com/electron/_apis/build' +const assert = require('assert'); +const request = require('request'); + +const BUILD_APPVEYOR_URL = 'https://ci.appveyor.com/api/builds'; +const CIRCLECI_PIPELINE_URL = 'https://circleci.com/api/v2/project/gh/electron/electron/pipeline'; +const VSTS_URL = 'https://github.visualstudio.com/electron/_apis/build'; +const CIRCLECI_WAIT_TIME = process.env.CIRCLECI_WAIT_TIME || 30000; const appVeyorJobs = { 'electron-x64': 'electron-x64-release', 'electron-ia32': 'electron-ia32-release', 'electron-woa': 'electron-woa-release' -} +}; const circleCIJobs = [ 'linux-arm-publish', @@ -19,45 +21,45 @@ const circleCIJobs = [ 'linux-x64-publish', 'mas-publish', 'osx-publish' -] +]; const vstsArmJobs = [ 'electron-arm-testing', 'electron-arm64-testing', 'electron-woa-testing' -] +]; -let jobRequestedCount = 0 +let jobRequestedCount = 0; async function makeRequest (requestOptions, parseResponse) { return new Promise((resolve, reject) => { request(requestOptions, (err, res, body) => { if (!err && res.statusCode >= 200 && res.statusCode < 300) { if (parseResponse) { - const build = JSON.parse(body) - resolve(build) + const build = JSON.parse(body); + resolve(build); } else { - resolve(body) + resolve(body); } } else { - console.error('Error occurred while requesting:', requestOptions.url) + console.error('Error occurred while requesting:', requestOptions.url); if (parseResponse) { try { - console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body)) + console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body)); } catch (err) { - console.log('Error: ', `(status ${res.statusCode})`, res.body) + console.log('Error: ', `(status ${res.statusCode})`, res.body); } } else { - console.log('Error: ', `(status ${res.statusCode})`, err || res.body) + console.log('Error: ', `(status ${res.statusCode})`, err || res.body); } - reject(err) + reject(err); } - }) - }) + }); + }); } async function circleCIcall (targetBranch, job, options) { - console.log(`Triggering CircleCI to run build job: ${job} on branch: ${targetBranch} with release flag.`) + console.log(`Triggering CircleCI to run build job: ${job} on branch: ${targetBranch} with release flag.`); const buildRequest = { 'branch': targetBranch, 'parameters': { @@ -65,70 +67,77 @@ async function circleCIcall (targetBranch, job, options) { 'run-build-linux': false, 'run-build-mac': false } - } + }; if (options.ghRelease) { - buildRequest.parameters['upload-to-s3'] = '0' + buildRequest.parameters['upload-to-s3'] = '0'; } else { - buildRequest.parameters['upload-to-s3'] = '1' + buildRequest.parameters['upload-to-s3'] = '1'; } - buildRequest.parameters[`run-${job}`] = true - jobRequestedCount++ + buildRequest.parameters[`run-${job}`] = true; + jobRequestedCount++; // The logic below expects that the CircleCI workflows for releases each // contain only one job in order to maintain compatibility with sudowoodo. // If the workflows are changed in the CircleCI config.yml, this logic will // also need to be changed as well as possibly changing sudowoodo. try { - const circleResponse = await circleCIRequest(circleCIPipelineURL, 'POST', buildRequest) - console.log(`CircleCI release build pipeline ${circleResponse.id} for ${job} triggered.`) - const pipelineInfoUrl = `https://circleci.com/api/v2/pipeline/${circleResponse.id}` - const workflowId = await getCircleCIWorkflowId(circleResponse.id) + const circleResponse = await circleCIRequest(CIRCLECI_PIPELINE_URL, 'POST', buildRequest); + console.log(`CircleCI release build pipeline ${circleResponse.id} for ${job} triggered.`); + const pipelineInfoUrl = `https://circleci.com/api/v2/pipeline/${circleResponse.id}`; + const workflowId = await getCircleCIWorkflowId(circleResponse.id); if (workflowId === -1) { - return + return; } - console.log(`CircleCI release build workflow running at https://circleci.com/workflow-run/${workflowId} for ${job}.`) - const jobNumber = await getCircleCIJobNumber(workflowId) + console.log(`CircleCI release build workflow running at https://circleci.com/workflow-run/${workflowId} for ${job}.`); + const jobNumber = await getCircleCIJobNumber(workflowId); if (jobNumber === -1) { - return + return; } - const jobUrl = `https://circleci.com/gh/electron/electron/${jobNumber}` - console.log(`CircleCI release build request for ${job} successful. Check ${jobUrl} for status.`) + const jobUrl = `https://circleci.com/gh/electron/electron/${jobNumber}`; + console.log(`CircleCI release build request for ${job} successful. Check ${jobUrl} for status.`); } catch (err) { - console.log('Error calling CircleCI: ', err) + console.log('Error calling CircleCI: ', err); } } async function getCircleCIWorkflowId (pipelineId) { - const pipelineInfoUrl = `https://circleci.com/api/v2/pipeline/${pipelineId}` - for (let i = 0; i < 5; i++) { - const pipelineInfo = await circleCIRequest(pipelineInfoUrl, 'GET') + const pipelineInfoUrl = `https://circleci.com/api/v2/pipeline/${pipelineId}`; + let workflowId = 0; + while (workflowId === 0) { + const pipelineInfo = await circleCIRequest(pipelineInfoUrl, 'GET'); switch (pipelineInfo.state) { case 'created': { - if (pipelineInfo.workflows.length === 1) { - return pipelineInfo.workflows[0].id + const workflows = await circleCIRequest(`${pipelineInfoUrl}/workflow`, 'GET'); + if (workflows.items.length === 1) { + workflowId = workflows.items[0].id; + break; } - console.log('Unxpected number of workflows, response was:', pipelineInfo) - return -1 + console.log('Unxpected number of workflows, response was:', pipelineInfo); + workflowId = -1; + break; } case 'error': { - console.log('Error retrieving workflows, response was:', pipelineInfo) - return -1 + console.log('Error retrieving workflows, response was:', pipelineInfo); + workflowId = -1; + break; } } - await new Promise(resolve => setTimeout(resolve, 5000)) + await new Promise(resolve => setTimeout(resolve, CIRCLECI_WAIT_TIME)); } - return -1 + return workflowId; } async function getCircleCIJobNumber (workflowId) { - const jobInfoUrl = `https://circleci.com/api/v2/workflow/${workflowId}/jobs` - for (let i = 0; i < 5; i++) { - const jobInfo = await circleCIRequest(jobInfoUrl, 'GET') + const jobInfoUrl = `https://circleci.com/api/v2/workflow/${workflowId}/job`; + let jobNumber = 0; + while (jobNumber === 0) { + const jobInfo = await circleCIRequest(jobInfoUrl, 'GET'); if (!jobInfo.items) { - continue + continue; } if (jobInfo.items.length !== 1) { - console.log('Unxpected number of jobs, response was:', jobInfo) - return -1 + console.log('Unxpected number of jobs, response was:', jobInfo); + jobNumber = -1; + break; } switch (jobInfo.items[0].status) { @@ -136,18 +145,24 @@ async function getCircleCIJobNumber (workflowId) { case 'queued': case 'running': { if (jobInfo.items[0].job_number && !isNaN(jobInfo.items[0].job_number)) { - return jobInfo.items[0].job_number + jobNumber = jobInfo.items[0].job_number; } - break + break; } - case 'error': { - console.log('Error retrieving jobs, response was:', jobInfo) - return -1 + case 'canceled': + case 'error': + case 'infrastructure_fail': + case 'timedout': + case 'not_run': + case 'failed': { + console.log(`Error job returned a status of ${jobInfo.items[0].status}, response was:`, jobInfo); + jobNumber = -1; + break; } } - await new Promise(resolve => setTimeout(resolve, 5000)) + await new Promise(resolve => setTimeout(resolve, CIRCLECI_WAIT_TIME)); } - return -1 + return jobNumber; } async function circleCIRequest (url, method, requestBody) { @@ -164,32 +179,32 @@ async function circleCIRequest (url, method, requestBody) { }, body: requestBody ? JSON.stringify(requestBody) : null }, true).catch(err => { - console.log('Error calling CircleCI:', err) - }) + console.log('Error calling CircleCI:', err); + }); } function buildAppVeyor (targetBranch, options) { - const validJobs = Object.keys(appVeyorJobs) + const validJobs = Object.keys(appVeyorJobs); if (options.job) { - assert(validJobs.includes(options.job), `Unknown AppVeyor CI job name: ${options.job}. Valid values are: ${validJobs}.`) - callAppVeyor(targetBranch, options.job, options) + assert(validJobs.includes(options.job), `Unknown AppVeyor CI job name: ${options.job}. Valid values are: ${validJobs}.`); + callAppVeyor(targetBranch, options.job, options); } else { - validJobs.forEach((job) => callAppVeyor(targetBranch, job, options)) + validJobs.forEach((job) => callAppVeyor(targetBranch, job, options)); } } async function callAppVeyor (targetBranch, job, options) { - console.log(`Triggering AppVeyor to run build job: ${job} on branch: ${targetBranch} with release flag.`) + console.log(`Triggering AppVeyor to run build job: ${job} on branch: ${targetBranch} with release flag.`); const environmentVariables = { ELECTRON_RELEASE: 1 - } + }; if (!options.ghRelease) { - environmentVariables.UPLOAD_TO_S3 = 1 + environmentVariables.UPLOAD_TO_S3 = 1; } const requestOpts = { - url: buildAppVeyorURL, + url: BUILD_APPVEYOR_URL, auth: { bearer: process.env.APPVEYOR_CLOUD_TOKEN }, @@ -203,48 +218,48 @@ async function callAppVeyor (targetBranch, job, options) { environmentVariables }), method: 'POST' - } - jobRequestedCount++ + }; + 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}`) + 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}`); } function buildCircleCI (targetBranch, options) { if (options.job) { - assert(circleCIJobs.includes(options.job), `Unknown CircleCI job name: ${options.job}. Valid values are: ${circleCIJobs}.`) - circleCIcall(targetBranch, options.job, options) + assert(circleCIJobs.includes(options.job), `Unknown CircleCI job name: ${options.job}. Valid values are: ${circleCIJobs}.`); + circleCIcall(targetBranch, options.job, options); } else { - circleCIJobs.forEach((job) => circleCIcall(targetBranch, job, options)) + circleCIJobs.forEach((job) => circleCIcall(targetBranch, job, options)); } } async function buildVSTS (targetBranch, options) { if (options.armTest) { - assert(vstsArmJobs.includes(options.job), `Unknown VSTS CI arm test job name: ${options.job}. Valid values are: ${vstsArmJobs}.`) + assert(vstsArmJobs.includes(options.job), `Unknown VSTS CI arm test job name: ${options.job}. Valid values are: ${vstsArmJobs}.`); } - console.log(`Triggering VSTS to run build on branch: ${targetBranch} with release flag.`) + console.log(`Triggering VSTS to run build on branch: ${targetBranch} with release flag.`); const environmentVariables = { ELECTRON_RELEASE: 1 - } + }; if (options.armTest) { if (options.circleBuildNum) { - environmentVariables.CIRCLE_BUILD_NUM = options.circleBuildNum + environmentVariables.CIRCLE_BUILD_NUM = options.circleBuildNum; } else if (options.appveyorJobId) { - environmentVariables.APPVEYOR_JOB_ID = options.appveyorJobId + environmentVariables.APPVEYOR_JOB_ID = options.appveyorJobId; } } else { if (!options.ghRelease) { - environmentVariables.UPLOAD_TO_S3 = 1 + environmentVariables.UPLOAD_TO_S3 = 1; } } const requestOpts = { - url: `${vstsURL}/definitions?api-version=4.1`, + url: `${VSTS_URL}/definitions?api-version=4.1`, auth: { user: '', password: process.env.VSTS_TOKEN @@ -252,12 +267,12 @@ async function buildVSTS (targetBranch, options) { headers: { 'Content-Type': 'application/json' } - } + }; const vstsResponse = await makeRequest(requestOpts, true).catch(err => { - console.log('Error calling VSTS to get build definitions:', err) - }) - const buildsToRun = vstsResponse.value.filter(build => build.name === options.job) - buildsToRun.forEach((build) => callVSTSBuild(build, targetBranch, environmentVariables)) + console.log('Error calling VSTS to get build definitions:', err); + }); + const buildsToRun = vstsResponse.value.filter(build => build.name === options.job); + buildsToRun.forEach((build) => callVSTSBuild(build, targetBranch, environmentVariables)); } async function callVSTSBuild (build, targetBranch, environmentVariables) { @@ -265,12 +280,12 @@ async function callVSTSBuild (build, targetBranch, environmentVariables) { definition: build, sourceBranch: targetBranch, priority: 'high' - } + }; if (Object.keys(environmentVariables).length !== 0) { - buildBody.parameters = JSON.stringify(environmentVariables) + buildBody.parameters = JSON.stringify(environmentVariables); } const requestOpts = { - url: `${vstsURL}/builds?api-version=4.1`, + url: `${VSTS_URL}/builds?api-version=4.1`, auth: { user: '', password: process.env.VSTS_TOKEN @@ -280,55 +295,54 @@ async function callVSTSBuild (build, targetBranch, environmentVariables) { }, body: JSON.stringify(buildBody), method: 'POST' - } - jobRequestedCount++ + }; + jobRequestedCount++; 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.`) + 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.`); } function runRelease (targetBranch, options) { if (options.ci) { switch (options.ci) { case 'CircleCI': { - buildCircleCI(targetBranch, options) - break + buildCircleCI(targetBranch, options); + break; } case 'AppVeyor': { - buildAppVeyor(targetBranch, options) - break + buildAppVeyor(targetBranch, options); + break; } case 'VSTS': { - buildVSTS(targetBranch, options) - break + buildVSTS(targetBranch, options); + break; } default: { - console.log(`Error! Unknown CI: ${options.ci}.`) - process.exit(1) + console.log(`Error! Unknown CI: ${options.ci}.`); + process.exit(1); } } } else { - buildCircleCI(targetBranch, options) - buildAppVeyor(targetBranch, options) - buildVSTS(targetBranch, options) + buildCircleCI(targetBranch, options); + buildAppVeyor(targetBranch, options); } - console.log(`${jobRequestedCount} jobs were requested.`) + console.log(`${jobRequestedCount} jobs were requested.`); } -module.exports = runRelease +module.exports = runRelease; if (require.main === module) { const args = require('minimist')(process.argv.slice(2), { boolean: ['ghRelease', 'armTest'] - }) - const targetBranch = args._[0] + }); + const targetBranch = args._[0]; 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] [--ghRelease] [--armTest] [--circleBuildNum=xxx] [--appveyorJobId=xxx] TARGET_BRANCH - `) - process.exit(0) + `); + process.exit(0); } - runRelease(targetBranch, args) + runRelease(targetBranch, args); } diff --git a/script/release/find-github-release.js b/script/release/find-github-release.js index 4551133c1d693..0b47c8692ebfc 100644 --- a/script/release/find-github-release.js +++ b/script/release/find-github-release.js @@ -1,38 +1,38 @@ -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) +}); if (process.argv.length < 3) { - console.log('Usage: find-release version') - process.exit(1) + console.log('Usage: find-release version'); + process.exit(1); } -const version = process.argv[2] +const version = process.argv[2]; async function findRelease () { const releases = await octokit.repos.listReleases({ owner: 'electron', repo: version.indexOf('nightly') > 0 ? 'nightlies' : 'electron' - }) + }); - const targetRelease = releases.data.find(release => release.tag_name === version) - let returnObject = {} + const targetRelease = releases.data.find(release => release.tag_name === version); + let returnObject = {}; if (targetRelease) { returnObject = { id: targetRelease.id, draft: targetRelease.draft, exists: true - } + }; } else { returnObject = { exists: false, draft: false - } + }; } - console.log(JSON.stringify(returnObject)) + console.log(JSON.stringify(returnObject)); } -findRelease() +findRelease(); diff --git a/script/release/notes/index.js b/script/release/notes/index.js index 8de0101dcf65c..42fe90830e4ad 100755 --- a/script/release/notes/index.js +++ b/script/release/notes/index.js @@ -1,53 +1,53 @@ #!/usr/bin/env node -const { GitProcess } = require('dugite') -const minimist = require('minimist') -const path = require('path') -const semver = require('semver') +const { GitProcess } = require('dugite'); +const minimist = require('minimist'); +const path = require('path'); +const semver = require('semver'); -const { ELECTRON_DIR } = require('../../lib/utils') -const notesGenerator = require('./notes.js') +const { ELECTRON_DIR } = require('../../lib/utils'); +const notesGenerator = require('./notes.js'); -const semverify = version => version.replace(/^origin\//, '').replace('x', '0').replace(/-/g, '.') +const semverify = version => version.replace(/^origin\//, '').replace('x', '0').replace(/-/g, '.'); const runGit = async (args) => { - const response = await GitProcess.exec(args, ELECTRON_DIR) + const response = await GitProcess.exec(args, ELECTRON_DIR); if (response.exitCode !== 0) { - throw new Error(response.stderr.trim()) + throw new Error(response.stderr.trim()); } - return response.stdout.trim() -} + return response.stdout.trim(); +}; -const tagIsSupported = tag => tag && !tag.includes('nightly') && !tag.includes('unsupported') -const tagIsBeta = tag => tag.includes('beta') -const tagIsStable = tag => tagIsSupported(tag) && !tagIsBeta(tag) +const tagIsSupported = tag => tag && !tag.includes('nightly') && !tag.includes('unsupported'); +const tagIsBeta = tag => tag.includes('beta'); +const tagIsStable = tag => tagIsSupported(tag) && !tagIsBeta(tag); const getTagsOf = async (point) => { return (await runGit(['tag', '--merged', point])) .split('\n') .map(tag => tag.trim()) .filter(tag => semver.valid(tag)) - .sort(semver.compare) -} + .sort(semver.compare); +}; const getTagsOnBranch = async (point) => { - const masterTags = await getTagsOf('master') + const masterTags = await getTagsOf('master'); if (point === 'master') { - return masterTags + return masterTags; } - const masterTagsSet = new Set(masterTags) - return (await getTagsOf(point)).filter(tag => !masterTagsSet.has(tag)) -} + const masterTagsSet = new Set(masterTags); + return (await getTagsOf(point)).filter(tag => !masterTagsSet.has(tag)); +}; const getBranchOf = async (point) => { const branches = (await runGit(['branch', '-a', '--contains', point])) .split('\n') .map(branch => branch.trim()) - .filter(branch => !!branch) - const current = branches.find(branch => branch.startsWith('* ')) - return current ? current.slice(2) : branches.shift() -} + .filter(branch => !!branch); + const current = branches.find(branch => branch.startsWith('* ')); + return current ? current.slice(2) : branches.shift(); +}; const getAllBranches = async () => { return (await runGit(['branch', '--remote'])) @@ -55,101 +55,101 @@ const getAllBranches = async () => { .map(branch => branch.trim()) .filter(branch => !!branch) .filter(branch => branch !== 'origin/HEAD -> origin/master') - .sort() -} + .sort(); +}; const getStabilizationBranches = async () => { return (await getAllBranches()) - .filter(branch => /^origin\/\d+-\d+-x$/.test(branch)) -} + .filter(branch => /^origin\/\d+-\d+-x$/.test(branch)); +}; const getPreviousStabilizationBranch = async (current) => { const stabilizationBranches = (await getStabilizationBranches()) - .filter(branch => branch !== current && branch !== `origin/${current}`) + .filter(branch => branch !== current && branch !== `origin/${current}`); if (!semver.valid(current)) { // since we don't seem to be on a stabilization branch right now, // pick a placeholder name that will yield the newest branch // as a comparison point. - current = 'v999.999.999' + current = 'v999.999.999'; } - let newestMatch = null + let newestMatch = null; for (const branch of stabilizationBranches) { if (semver.gte(semverify(branch), semverify(current))) { - continue + continue; } if (newestMatch && semver.lte(semverify(branch), semverify(newestMatch))) { - continue + continue; } - newestMatch = branch + newestMatch = branch; } - return newestMatch -} + return newestMatch; +}; const getPreviousPoint = async (point) => { - const currentBranch = await getBranchOf(point) - const currentTag = (await getTagsOf(point)).filter(tag => tagIsSupported(tag)).pop() - const currentIsStable = tagIsStable(currentTag) + const currentBranch = await getBranchOf(point); + const currentTag = (await getTagsOf(point)).filter(tag => tagIsSupported(tag)).pop(); + const currentIsStable = tagIsStable(currentTag); try { // First see if there's an earlier tag on the same branch // that can serve as a reference point. - let tags = (await getTagsOnBranch(`${point}^`)).filter(tag => tagIsSupported(tag)) + let tags = (await getTagsOnBranch(`${point}^`)).filter(tag => tagIsSupported(tag)); if (currentIsStable) { - tags = tags.filter(tag => tagIsStable(tag)) + tags = tags.filter(tag => tagIsStable(tag)); } if (tags.length) { - return tags.pop() + return tags.pop(); } } catch (error) { - console.log('error', error) + console.log('error', error); } // Otherwise, use the newest stable release that preceeds this branch. // To reach that you may have to walk past >1 branch, e.g. to get past // 2-1-x which never had a stable release. - let branch = currentBranch + let branch = currentBranch; while (branch) { - const prevBranch = await getPreviousStabilizationBranch(branch) - const tags = (await getTagsOnBranch(prevBranch)).filter(tag => tagIsStable(tag)) + const prevBranch = await getPreviousStabilizationBranch(branch); + const tags = (await getTagsOnBranch(prevBranch)).filter(tag => tagIsStable(tag)); if (tags.length) { - return tags.pop() + return tags.pop(); } - branch = prevBranch + branch = prevBranch; } -} +}; async function getReleaseNotes (range, newVersion, explicitLinks) { - const rangeList = range.split('..') || ['HEAD'] - const to = rangeList.pop() - const from = rangeList.pop() || (await getPreviousPoint(to)) + const rangeList = range.split('..') || ['HEAD']; + const to = rangeList.pop(); + const from = rangeList.pop() || (await getPreviousPoint(to)); if (!newVersion) { - newVersion = to + newVersion = to; } - console.log(`Generating release notes between ${from} and ${to} for version ${newVersion}`) - const notes = await notesGenerator.get(from, to, newVersion) + console.log(`Generating release notes between ${from} and ${to} for version ${newVersion}`); + const notes = await notesGenerator.get(from, to, newVersion); const ret = { text: notesGenerator.render(notes, explicitLinks) - } + }; if (notes.unknown.length) { - ret.warning = `You have ${notes.unknown.length} unknown release notes. Please fix them before releasing.` + ret.warning = `You have ${notes.unknown.length} unknown release notes. Please fix them before releasing.`; } - return ret + return ret; } async function main () { const opts = minimist(process.argv.slice(2), { boolean: [ 'explicit-links', 'help' ], string: [ 'version' ] - }) - opts.range = opts._.shift() + }); + opts.range = opts._.shift(); if (opts.help || !opts.range) { - const name = path.basename(process.argv[1]) + const name = path.basename(process.argv[1]); console.log(` easy usage: ${name} version @@ -165,22 +165,22 @@ full usage: ${name} [begin..]end [--version version] [--explicit-links] For example, these invocations are equivalent: ${process.argv[1]} v4.0.1 ${process.argv[1]} v4.0.0..v4.0.1 --version v4.0.1 -`) - return 0 +`); + return 0; } - const notes = await getReleaseNotes(opts.range, opts.version, opts['explicit-links']) - console.log(notes.text) + const notes = await getReleaseNotes(opts.range, opts.version, opts['explicit-links']); + console.log(notes.text); if (notes.warning) { - throw new Error(notes.warning) + throw new Error(notes.warning); } } if (process.mainModule === module) { main().catch((err) => { - console.error('Error Occurred:', err) - process.exit(1) - }) + console.error('Error Occurred:', err); + process.exit(1); + }); } -module.exports = getReleaseNotes +module.exports = getReleaseNotes; diff --git a/script/release/notes/notes.js b/script/release/notes/notes.js index 7f187c5c2a728..bbbb4f2c0d906 100644 --- a/script/release/notes/notes.js +++ b/script/release/notes/notes.js @@ -1,75 +1,75 @@ #!/usr/bin/env node -const childProcess = require('child_process') -const fs = require('fs') -const os = require('os') -const path = require('path') +const childProcess = require('child_process'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); -const { GitProcess } = require('dugite') +const { GitProcess } = require('dugite'); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) -const semver = require('semver') +}); +const semver = require('semver'); -const { ELECTRON_VERSION, SRC_DIR } = require('../../lib/utils') +const { ELECTRON_VERSION, SRC_DIR } = require('../../lib/utils'); -const MAX_FAIL_COUNT = 3 -const CHECK_INTERVAL = 5000 +const MAX_FAIL_COUNT = 3; +const CHECK_INTERVAL = 5000; -const CACHE_DIR = path.resolve(__dirname, '.cache') -const NO_NOTES = 'No notes' -const FOLLOW_REPOS = [ 'electron/electron', 'electron/libchromiumcontent', 'electron/node' ] +const CACHE_DIR = path.resolve(__dirname, '.cache'); +const NO_NOTES = 'No notes'; +const FOLLOW_REPOS = [ 'electron/electron', 'electron/node' ]; -const breakTypes = new Set(['breaking-change']) -const docTypes = new Set(['doc', 'docs']) -const featTypes = new Set(['feat', 'feature']) -const fixTypes = new Set(['fix']) -const otherTypes = new Set(['spec', 'build', 'test', 'chore', 'deps', 'refactor', 'tools', 'vendor', 'perf', 'style', 'ci']) -const knownTypes = new Set([...breakTypes.keys(), ...docTypes.keys(), ...featTypes.keys(), ...fixTypes.keys(), ...otherTypes.keys()]) +const breakTypes = new Set(['breaking-change']); +const docTypes = new Set(['doc', 'docs']); +const featTypes = new Set(['feat', 'feature']); +const fixTypes = new Set(['fix']); +const otherTypes = new Set(['spec', 'build', 'test', 'chore', 'deps', 'refactor', 'tools', 'vendor', 'perf', 'style', 'ci']); +const knownTypes = new Set([...breakTypes.keys(), ...docTypes.keys(), ...featTypes.keys(), ...fixTypes.keys(), ...otherTypes.keys()]); const runGit = async (dir, args) => { - const response = await GitProcess.exec(args, dir) + const response = await GitProcess.exec(args, dir); if (response.exitCode !== 0) { - throw new Error(response.stderr.trim()) + throw new Error(response.stderr.trim()); } - return response.stdout.trim() -} + return response.stdout.trim(); +}; const getCommonAncestor = async (dir, point1, point2) => { - return runGit(dir, ['merge-base', point1, point2]) -} + return runGit(dir, ['merge-base', point1, point2]); +}; const setPullRequest = (commit, owner, repo, number) => { if (!owner || !repo || !number) { - throw new Error(JSON.stringify({ owner, repo, number }, null, 2)) + throw new Error(JSON.stringify({ owner, repo, number }, null, 2)); } if (!commit.originalPr) { - commit.originalPr = commit.pr + commit.originalPr = commit.pr; } - commit.pr = { owner, repo, number } + commit.pr = { owner, repo, number }; if (!commit.originalPr) { - commit.originalPr = commit.pr + commit.originalPr = commit.pr; } -} +}; const getNoteFromClerk = async (number, owner, repo) => { - const comments = await getComments(number, owner, repo) - if (!comments || !comments.data) return + const comments = await getComments(number, owner, repo); + if (!comments || !comments.data) return; - const CLERK_LOGIN = 'release-clerk[bot]' - const CLERK_NO_NOTES = '**No Release Notes**' - const PERSIST_LEAD = '**Release Notes Persisted**\n\n' - const QUOTE_LEAD = '> ' + const CLERK_LOGIN = 'release-clerk[bot]'; + const CLERK_NO_NOTES = '**No Release Notes**'; + const PERSIST_LEAD = '**Release Notes Persisted**\n\n'; + const QUOTE_LEAD = '> '; for (const comment of comments.data.reverse()) { if (comment.user.login !== CLERK_LOGIN) { - continue + continue; } if (comment.body === CLERK_NO_NOTES) { - return NO_NOTES + return NO_NOTES; } if (comment.body.startsWith(PERSIST_LEAD)) { return comment.body @@ -79,10 +79,10 @@ const getNoteFromClerk = async (number, owner, repo) => { .filter(line => line.startsWith(QUOTE_LEAD)) // notes are quoted .map(line => line.slice(QUOTE_LEAD.length)) // unquote the lines .join(' ') // join the note lines - .trim() + .trim(); } } -} +}; // copied from https://github.com/electron/clerk/blob/master/src/index.ts#L4-L13 const OMIT_FROM_RELEASE_NOTES_KEYS = [ @@ -94,36 +94,36 @@ const OMIT_FROM_RELEASE_NOTES_KEYS = [ 'nothing', 'empty', 'blank' -] +]; const getNoteFromBody = body => { if (!body) { - return null + return null; } - const NOTE_PREFIX = 'Notes: ' - const NOTE_HEADER = '#### Release Notes' + const NOTE_PREFIX = 'Notes: '; + const NOTE_HEADER = '#### Release Notes'; let note = body .split(/\r?\n\r?\n/) // split into paragraphs .map(paragraph => paragraph.trim()) .map(paragraph => paragraph.startsWith(NOTE_HEADER) ? paragraph.slice(NOTE_HEADER.length).trim() : paragraph) - .find(paragraph => paragraph.startsWith(NOTE_PREFIX)) + .find(paragraph => paragraph.startsWith(NOTE_PREFIX)); if (note) { note = note .slice(NOTE_PREFIX.length) .replace(//, '') // '' .replace(/\r?\n/, ' ') // remove newlines - .trim() + .trim(); } if (note && OMIT_FROM_RELEASE_NOTES_KEYS.includes(note.toLowerCase())) { - return NO_NOTES + return NO_NOTES; } - return note -} + return note; +}; /** * Looks for our project's conventions in the commit message: @@ -138,71 +138,71 @@ const getNoteFromBody = body => { */ const parseCommitMessage = (commitMessage, owner, repo, commit = {}) => { // split commitMessage into subject & body - let subject = commitMessage - let body = '' - const pos = subject.indexOf('\n') + let subject = commitMessage; + let body = ''; + const pos = subject.indexOf('\n'); if (pos !== -1) { - body = subject.slice(pos).trim() - subject = subject.slice(0, pos).trim() + body = subject.slice(pos).trim(); + subject = subject.slice(0, pos).trim(); } if (!commit.originalSubject) { - commit.originalSubject = subject + commit.originalSubject = subject; } if (body) { - commit.body = body + commit.body = body; - const note = getNoteFromBody(body) - if (note) { commit.note = note } + const note = getNoteFromBody(body); + if (note) { commit.note = note; } } // if the subject ends in ' (#dddd)', treat it as a pull request id - let match + let match; if ((match = subject.match(/^(.*)\s\(#(\d+)\)$/))) { - setPullRequest(commit, owner, repo, parseInt(match[2])) - subject = match[1] + setPullRequest(commit, owner, repo, parseInt(match[2])); + subject = match[1]; } // if the subject begins with 'word:', treat it as a semantic commit if ((match = subject.match(/^(\w+):\s(.*)$/))) { - const type = match[1].toLocaleLowerCase() + const type = match[1].toLocaleLowerCase(); if (knownTypes.has(type)) { - commit.type = type - subject = match[2] + commit.type = type; + subject = match[2]; } } // Check for GitHub commit message that indicates a PR if ((match = subject.match(/^Merge pull request #(\d+) from (.*)$/))) { - setPullRequest(commit, owner, repo, parseInt(match[1])) - commit.pr.branch = match[2].trim() + setPullRequest(commit, owner, repo, parseInt(match[1])); + commit.pr.branch = match[2].trim(); } // Check for a trop comment that indicates a PR if ((match = commitMessage.match(/\bBackport of #(\d+)\b/))) { - setPullRequest(commit, owner, repo, parseInt(match[1])) + setPullRequest(commit, owner, repo, parseInt(match[1])); } // https://help.github.com/articles/closing-issues-using-keywords/ if ((match = subject.match(/\b(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved|for)\s#(\d+)\b/))) { - commit.issueNumber = parseInt(match[1]) + commit.issueNumber = parseInt(match[1]); if (!commit.type) { - commit.type = 'fix' + commit.type = 'fix'; } } // look for 'fixes' in markdown; e.g. 'Fixes [#8952](https://github.com/electron/electron/issues/8952)' if (!commit.issueNumber && ((match = commitMessage.match(/Fixes \[#(\d+)\]\(https:\/\/github.com\/(\w+)\/(\w+)\/issues\/(\d+)\)/)))) { - commit.issueNumber = parseInt(match[1]) + commit.issueNumber = parseInt(match[1]); if (commit.pr && commit.pr.number === commit.issueNumber) { - commit.pr = null + commit.pr = null; } if (commit.originalPr && commit.originalPr.number === commit.issueNumber) { - commit.originalPr = null + commit.originalPr = null; } if (!commit.type) { - commit.type = 'fix' + commit.type = 'fix'; } } @@ -211,55 +211,55 @@ const parseCommitMessage = (commitMessage, owner, repo, commit = {}) => { .split(/\r?\n/) // split into lines .map(line => line.trim()) .some(line => line.startsWith('BREAKING CHANGE'))) { - commit.type = 'breaking-change' + commit.type = 'breaking-change'; } // Check for a reversion commit if ((match = body.match(/This reverts commit ([a-f0-9]{40})\./))) { - commit.revertHash = match[1] + commit.revertHash = match[1]; } // Edge case: manual backport where commit has `owner/repo#pull` notation if (commitMessage.toLowerCase().includes('backport') && ((match = commitMessage.match(/\b(\w+)\/(\w+)#(\d+)\b/)))) { - const [ , owner, repo, number ] = match + const [ , owner, repo, number ] = match; if (FOLLOW_REPOS.includes(`${owner}/${repo}`)) { - setPullRequest(commit, owner, repo, number) + setPullRequest(commit, owner, repo, number); } } // Edge case: manual backport where commit has a link to the backport PR if (commitMessage.includes('ackport') && ((match = commitMessage.match(/https:\/\/github\.com\/(\w+)\/(\w+)\/pull\/(\d+)/)))) { - const [ , owner, repo, number ] = match + const [ , owner, repo, number ] = match; if (FOLLOW_REPOS.includes(`${owner}/${repo}`)) { - setPullRequest(commit, owner, repo, number) + setPullRequest(commit, owner, repo, number); } } // Legacy commits: pre-semantic commits if (!commit.type || commit.type === 'chore') { - const commitMessageLC = commitMessage.toLocaleLowerCase() + const commitMessageLC = commitMessage.toLocaleLowerCase(); if ((match = commitMessageLC.match(/\bchore\((\w+)\):/))) { // example: 'Chore(docs): description' - commit.type = knownTypes.has(match[1]) ? match[1] : 'chore' + commit.type = knownTypes.has(match[1]) ? match[1] : 'chore'; } else if (commitMessageLC.match(/\b(?:fix|fixes|fixed)/)) { // example: 'fix a bug' - commit.type = 'fix' + commit.type = 'fix'; } else if (commitMessageLC.match(/\[(?:docs|doc)\]/)) { // example: '[docs] - commit.type = 'doc' + commit.type = 'doc'; } } - commit.subject = subject.trim() - return commit -} + commit.subject = subject.trim(); + return commit; +}; const getLocalCommitHashes = async (dir, ref) => { - const args = ['log', '-z', `--format=%H`, ref] - return (await runGit(dir, args)).split(`\0`).map(hash => hash.trim()) -} + const args = ['log', '-z', `--format=%H`, ref]; + return (await runGit(dir, args)).split(`\0`).map(hash => hash.trim()); +}; /* * possible properties: @@ -267,137 +267,99 @@ const getLocalCommitHashes = async (dir, ref) => { * pr { owner, repo, number, branch }, revertHash, subject, type */ const getLocalCommitDetails = async (module, point1, point2) => { - const { owner, repo, dir } = module + const { owner, repo, dir } = module; - const fieldSep = '||' - const format = ['%H', '%P', '%aE', '%B'].join(fieldSep) - const args = ['log', '-z', '--cherry-pick', '--right-only', '--first-parent', `--format=${format}`, `${point1}..${point2}`] - const commits = (await runGit(dir, args)).split(`\0`).map(field => field.trim()) - const details = [] + const fieldSep = '||'; + const format = ['%H', '%P', '%aE', '%B'].join(fieldSep); + const args = ['log', '-z', '--cherry-pick', '--right-only', '--first-parent', `--format=${format}`, `${point1}..${point2}`]; + const commits = (await runGit(dir, args)).split(`\0`).map(field => field.trim()); + const details = []; for (const commit of commits) { if (!commit) { - continue + continue; } - const [ hash, parentHashes, email, commitMessage ] = commit.split(fieldSep, 4).map(field => field.trim()) + const [ hash, parentHashes, email, commitMessage ] = commit.split(fieldSep, 4).map(field => field.trim()); details.push(parseCommitMessage(commitMessage, owner, repo, { email, hash, owner, repo, parentHashes: parentHashes.split() - })) + })); } - return details -} + return details; +}; const checkCache = async (name, operation) => { - const filename = path.resolve(CACHE_DIR, name) + const filename = path.resolve(CACHE_DIR, name); if (fs.existsSync(filename)) { - return JSON.parse(fs.readFileSync(filename, 'utf8')) + return JSON.parse(fs.readFileSync(filename, 'utf8')); } - const response = await operation() + const response = await operation(); if (response) { - fs.writeFileSync(filename, JSON.stringify(response)) + fs.writeFileSync(filename, JSON.stringify(response)); } - return response -} + return response; +}; // helper function to add some resiliency to volatile GH api endpoints async function runRetryable (fn, maxRetries) { - let lastError + let lastError; for (let i = 0; i < maxRetries; i++) { try { - return await fn() + return await fn(); } catch (error) { - await new Promise((resolve, reject) => setTimeout(resolve, CHECK_INTERVAL)) - lastError = error + await new Promise((resolve, reject) => setTimeout(resolve, CHECK_INTERVAL)); + lastError = error; } } // Silently eat 404s. - if (lastError.status !== 404) throw lastError + if (lastError.status !== 404) throw lastError; } const getPullRequest = async (number, owner, repo) => { - const name = `${owner}-${repo}-pull-${number}` - const retryableFunc = () => octokit.pulls.get({ pull_number: number, owner, repo }) - return checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT)) -} + const name = `${owner}-${repo}-pull-${number}`; + const retryableFunc = () => octokit.pulls.get({ pull_number: number, owner, repo }); + return checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT)); +}; const getComments = async (number, owner, repo) => { - const name = `${owner}-${repo}-issue-${number}-comments` - const retryableFunc = () => octokit.issues.listComments({ issue_number: number, owner, repo, per_page: 100 }) - return checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT)) -} + const name = `${owner}-${repo}-issue-${number}-comments`; + const retryableFunc = () => octokit.issues.listComments({ issue_number: number, owner, repo, per_page: 100 }); + return checkCache(name, () => runRetryable(retryableFunc, MAX_FAIL_COUNT)); +}; const addRepoToPool = async (pool, repo, from, to) => { - const commonAncestor = await getCommonAncestor(repo.dir, from, to) - const oldHashes = await getLocalCommitHashes(repo.dir, from) - oldHashes.forEach(hash => { pool.processedHashes.add(hash) }) - const commits = await getLocalCommitDetails(repo, commonAncestor, to) - pool.commits.push(...commits) -} + const commonAncestor = await getCommonAncestor(repo.dir, from, to); + const oldHashes = await getLocalCommitHashes(repo.dir, from); + oldHashes.forEach(hash => { pool.processedHashes.add(hash); }); + const commits = await getLocalCommitDetails(repo, commonAncestor, to); + pool.commits.push(...commits); +}; /*** **** Other Repos ***/ -// other repos - gyp - -const getGypSubmoduleRef = async (dir, point) => { - // example: '160000 commit 028b0af83076cec898f4ebce208b7fadb715656e libchromiumcontent' - const response = await runGit( - path.dirname(dir), - ['ls-tree', '-t', point, path.basename(dir)] - ) - - const line = response.split('\n').filter(line => line.startsWith('160000')).shift() - const tokens = line ? line.split(/\s/).map(token => token.trim()) : null - const ref = tokens && tokens.length >= 3 ? tokens[2] : null - - return ref -} - -const getDependencyCommitsGyp = async (pool, fromRef, toRef) => { - const commits = [] - - const repos = [{ - owner: 'electron', - repo: 'libchromiumcontent', - dir: path.resolve(ELECTRON_VERSION, 'vendor', 'libchromiumcontent') - }, { - owner: 'electron', - repo: 'node', - dir: path.resolve(ELECTRON_VERSION, 'vendor', 'node') - }] - - for (const repo of repos) { - const from = await getGypSubmoduleRef(repo.dir, fromRef) - const to = await getGypSubmoduleRef(repo.dir, toRef) - await addRepoToPool(pool, repo, from, to) - } - - return commits -} - // other repos - gn const getDepsVariable = async (ref, key) => { // get a copy of that reference point's DEPS file - const deps = await runGit(ELECTRON_VERSION, ['show', `${ref}:DEPS`]) - const filename = path.resolve(os.tmpdir(), 'DEPS') - fs.writeFileSync(filename, deps) + const deps = await runGit(ELECTRON_VERSION, ['show', `${ref}:DEPS`]); + const filename = path.resolve(os.tmpdir(), 'DEPS'); + fs.writeFileSync(filename, deps); // query the DEPS file const response = childProcess.spawnSync( 'gclient', ['getdep', '--deps-file', filename, '--var', key], { encoding: 'utf8' } - ) + ); // cleanup - fs.unlinkSync(filename) - return response.stdout.trim() -} + fs.unlinkSync(filename); + return response.stdout.trim(); +}; const getDependencyCommitsGN = async (pool, fromRef, toRef) => { const repos = [{ // just node @@ -405,27 +367,16 @@ const getDependencyCommitsGN = async (pool, fromRef, toRef) => { repo: 'node', dir: path.resolve(SRC_DIR, 'third_party', 'electron_node'), deps_variable_name: 'node_version' - }] + }]; for (const repo of repos) { // the 'DEPS' file holds the dependency reference point - const key = repo.deps_variable_name - const from = await getDepsVariable(fromRef, key) - const to = await getDepsVariable(toRef, key) - await addRepoToPool(pool, repo, from, to) + const key = repo.deps_variable_name; + const from = await getDepsVariable(fromRef, key); + const to = await getDepsVariable(toRef, key); + await addRepoToPool(pool, repo, from, to); } -} - -// other repos - controller - -const getDependencyCommits = async (pool, from, to) => { - const filename = path.resolve(ELECTRON_VERSION, 'vendor', 'libchromiumcontent') - const useGyp = fs.existsSync(filename) - - return useGyp - ? getDependencyCommitsGyp(pool, from, to) - : getDependencyCommitsGN(pool, from, to) -} +}; // Changes are interesting if they make a change relative to a previous // release in the same series. For example if you fix a Y.0.0 bug, that @@ -437,17 +388,17 @@ const getDependencyCommits = async (pool, from, to) => { // branches' changes. Otherwise we will have an overwhelmingly long // list of mostly-irrelevant changes. const shouldIncludeMultibranchChanges = (version) => { - let show = true + let show = true; if (semver.valid(version)) { - const prerelease = semver.prerelease(version) + const prerelease = semver.prerelease(version); show = prerelease ? parseInt(prerelease.pop()) > 1 - : semver.patch(version) > 0 + : semver.patch(version) > 0; } - return show -} + return show; +}; /*** **** Main @@ -455,132 +406,132 @@ const shouldIncludeMultibranchChanges = (version) => { const getNotes = async (fromRef, toRef, newVersion) => { if (!fs.existsSync(CACHE_DIR)) { - fs.mkdirSync(CACHE_DIR) + fs.mkdirSync(CACHE_DIR); } const pool = { processedHashes: new Set(), commits: [] - } + }; // get the electron/electron commits - const electron = { owner: 'electron', repo: 'electron', dir: ELECTRON_VERSION } - await addRepoToPool(pool, electron, fromRef, toRef) + const electron = { owner: 'electron', repo: 'electron', dir: ELECTRON_VERSION }; + await addRepoToPool(pool, electron, fromRef, toRef); // Don't include submodules if comparing across major versions; // there's just too much churn otherwise. const includeDeps = semver.valid(fromRef) && semver.valid(toRef) && - semver.major(fromRef) === semver.major(toRef) + semver.major(fromRef) === semver.major(toRef); if (includeDeps) { - await getDependencyCommits(pool, fromRef, toRef) + await getDependencyCommitsGN(pool, fromRef, toRef); } // remove any old commits - pool.commits = pool.commits.filter(commit => !pool.processedHashes.has(commit.hash)) + pool.commits = pool.commits.filter(commit => !pool.processedHashes.has(commit.hash)); // if a commmit _and_ revert occurred in the unprocessed set, skip them both for (const commit of pool.commits) { - const revertHash = commit.revertHash + const revertHash = commit.revertHash; if (!revertHash) { - continue + continue; } - const revert = pool.commits.find(commit => commit.hash === revertHash) + const revert = pool.commits.find(commit => commit.hash === revertHash); if (!revert) { - continue + continue; } - commit.note = NO_NOTES - revert.note = NO_NOTES - pool.processedHashes.add(commit.hash) - pool.processedHashes.add(revertHash) + commit.note = NO_NOTES; + revert.note = NO_NOTES; + pool.processedHashes.add(commit.hash); + pool.processedHashes.add(revertHash); } // scrape PRs for release note 'Notes:' comments for (const commit of pool.commits) { - let pr = commit.pr + let pr = commit.pr; - let prSubject + let prSubject; while (pr && !commit.note) { - const note = await getNoteFromClerk(pr.number, pr.owner, pr.repo) + const note = await getNoteFromClerk(pr.number, pr.owner, pr.repo); if (note) { - commit.note = note + commit.note = note; } // if we already have all the data we need, stop scraping the PRs if (commit.note && commit.type && prSubject) { - break + break; } - const prData = await getPullRequest(pr.number, pr.owner, pr.repo) + const prData = await getPullRequest(pr.number, pr.owner, pr.repo); if (!prData || !prData.data) { - break + break; } // try to pull a release note from the pull comment - const prParsed = parseCommitMessage(`${prData.data.title}\n\n${prData.data.body}`, pr.owner, pr.repo) + const prParsed = parseCommitMessage(`${prData.data.title}\n\n${prData.data.body}`, pr.owner, pr.repo); if (!commit.note) { - commit.note = prParsed.note + commit.note = prParsed.note; } if (!commit.type || prParsed.type === 'breaking-change') { - commit.type = prParsed.type + commit.type = prParsed.type; } - prSubject = prSubject || prParsed.subject + prSubject = prSubject || prParsed.subject; - pr = prParsed.pr && (prParsed.pr.number !== pr.number) ? prParsed.pr : null + pr = prParsed.pr && (prParsed.pr.number !== pr.number) ? prParsed.pr : null; } // if we still don't have a note, it's because someone missed a 'Notes: // comment in a PR somewhere... use the PR subject as a fallback. - commit.note = commit.note || prSubject + commit.note = commit.note || prSubject; } // remove non-user-facing commits pool.commits = pool.commits .filter(commit => commit.note !== NO_NOTES) - .filter(commit => !((commit.note || commit.subject).match(/^[Bb]ump v\d+\.\d+\.\d+/))) + .filter(commit => !((commit.note || commit.subject).match(/^[Bb]ump v\d+\.\d+\.\d+/))); if (!shouldIncludeMultibranchChanges(newVersion)) { // load all the prDatas await Promise.all( pool.commits.map(commit => new Promise(async (resolve) => { - const { pr } = commit + const { pr } = commit; if (typeof pr === 'object') { - const prData = await getPullRequest(pr.number, pr.owner, pr.repo) + const prData = await getPullRequest(pr.number, pr.owner, pr.repo); if (prData) { - commit.prData = prData + commit.prData = prData; } } - resolve() + resolve(); })) - ) + ); // remove items that already landed in a previous major/minor series pool.commits = pool.commits .filter(commit => { if (!commit.prData) { - return true + return true; } const reducer = (accumulator, current) => { - if (!semver.valid(accumulator)) { return current } - if (!semver.valid(current)) { return accumulator } - return semver.lt(accumulator, current) ? accumulator : current - } + if (!semver.valid(accumulator)) { return current; } + if (!semver.valid(current)) { return accumulator; } + return semver.lt(accumulator, current) ? accumulator : current; + }; const earliestRelease = commit.prData.data.labels .map(label => label.name.match(/merged\/(\d+)-(\d+)-x/)) .filter(label => !!label) .map(label => `${label[1]}.${label[2]}.0`) - .reduce(reducer, null) + .reduce(reducer, null); if (!semver.valid(earliestRelease)) { - return true + return true; } - return semver.diff(earliestRelease, newVersion).includes('patch') - }) + return semver.diff(earliestRelease, newVersion).includes('patch'); + }); } - pool.commits = removeSupercededChromiumUpdates(pool.commits) + pool.commits = removeSupercededChromiumUpdates(pool.commits); const notes = { breaking: [], @@ -590,78 +541,78 @@ const getNotes = async (fromRef, toRef, newVersion) => { other: [], unknown: [], name: newVersion - } + }; pool.commits.forEach(commit => { - const str = commit.type + const str = commit.type; if (!str) { - notes.unknown.push(commit) + notes.unknown.push(commit); } else if (breakTypes.has(str)) { - notes.breaking.push(commit) + notes.breaking.push(commit); } else if (docTypes.has(str)) { - notes.docs.push(commit) + notes.docs.push(commit); } else if (featTypes.has(str)) { - notes.feat.push(commit) + notes.feat.push(commit); } else if (fixTypes.has(str)) { - notes.fix.push(commit) + notes.fix.push(commit); } else if (otherTypes.has(str)) { - notes.other.push(commit) + notes.other.push(commit); } else { - notes.unknown.push(commit) + notes.unknown.push(commit); } - }) + }); - return notes -} + return notes; +}; const removeSupercededChromiumUpdates = (commits) => { - const chromiumRegex = /^Updated Chromium to \d+\.\d+\.\d+\.\d+/ - const updates = commits.filter(commit => (commit.note || commit.subject).match(chromiumRegex)) - const keepers = commits.filter(commit => !updates.includes(commit)) + const chromiumRegex = /^Updated Chromium to \d+\.\d+\.\d+\.\d+/; + const updates = commits.filter(commit => (commit.note || commit.subject).match(chromiumRegex)); + const keepers = commits.filter(commit => !updates.includes(commit)); // keep the newest update. if (updates.length) { - updates.sort((a, b) => a.originalPr.number - b.originalPr.number) - keepers.push(updates.pop()) + updates.sort((a, b) => a.originalPr.number - b.originalPr.number); + keepers.push(updates.pop()); } - return keepers -} + return keepers; +}; /*** **** Render ***/ const renderLink = (commit, explicitLinks) => { - let link - const pr = commit.originalPr + let link; + const pr = commit.originalPr; if (pr) { - const { owner, repo, number } = pr - const url = `https://github.com/${owner}/${repo}/pull/${number}` + const { owner, repo, number } = pr; + const url = `https://github.com/${owner}/${repo}/pull/${number}`; const text = owner === 'electron' && repo === 'electron' ? `#${number}` - : `${owner}/${repo}#${number}` - link = explicitLinks ? `[${text}](${url})` : text + : `${owner}/${repo}#${number}`; + link = explicitLinks ? `[${text}](${url})` : text; } else { - const { owner, repo, hash } = commit - const url = `https://github.com/${owner}/${repo}/commit/${hash}` + const { owner, repo, hash } = commit; + const url = `https://github.com/${owner}/${repo}/commit/${hash}`; const text = owner === 'electron' && repo === 'electron' ? `${hash.slice(0, 8)}` - : `${owner}/${repo}@${hash.slice(0, 8)}` - link = explicitLinks ? `[${text}](${url})` : text + : `${owner}/${repo}@${hash.slice(0, 8)}`; + link = explicitLinks ? `[${text}](${url})` : text; } - return link -} + return link; +}; const renderCommit = (commit, explicitLinks) => { // clean up the note - let note = commit.note || commit.subject - note = note.trim() + let note = commit.note || commit.subject; + note = note.trim(); if (note.length !== 0) { - note = note[0].toUpperCase() + note.substr(1) + note = note[0].toUpperCase() + note.substr(1); if (!note.endsWith('.')) { - note = note + '.' + note = note + '.'; } const commonVerbs = { @@ -681,57 +632,57 @@ const renderCommit = (commit, explicitLinks) => { 'Stopped': [ 'Stop' ], 'Updated': [ 'Update' ], 'Upgraded': [ 'Upgrade' ] - } + }; for (const [key, values] of Object.entries(commonVerbs)) { for (const value of values) { - const start = `${value} ` + const start = `${value} `; if (note.startsWith(start)) { - note = `${key} ${note.slice(start.length)}` + note = `${key} ${note.slice(start.length)}`; } } } } - const link = renderLink(commit, explicitLinks) + const link = renderLink(commit, explicitLinks); - return { note, link } -} + return { note, link }; +}; const renderNotes = (notes, explicitLinks) => { - const rendered = [ `# Release Notes for ${notes.name}\n\n` ] + const rendered = [ `# Release Notes for ${notes.name}\n\n` ]; const renderSection = (title, commits) => { if (commits.length === 0) { - return + return; } - const notes = new Map() + const notes = new Map(); for (const note of commits.map(commit => renderCommit(commit, explicitLinks))) { if (!notes.has(note.note)) { - notes.set(note.note, [note.link]) + notes.set(note.note, [note.link]); } else { - notes.get(note.note).push(note.link) + notes.get(note.note).push(note.link); } } - rendered.push(`## ${title}\n\n`) - const lines = [] - notes.forEach((links, key) => lines.push(` * ${key} ${links.map(link => link.toString()).sort().join(', ')}\n`)) - rendered.push(...lines.sort(), '\n') - } + rendered.push(`## ${title}\n\n`); + const lines = []; + notes.forEach((links, key) => lines.push(` * ${key} ${links.map(link => link.toString()).sort().join(', ')}\n`)); + rendered.push(...lines.sort(), '\n'); + }; - renderSection('Breaking Changes', notes.breaking) - renderSection('Features', notes.feat) - renderSection('Fixes', notes.fix) - renderSection('Other Changes', notes.other) + renderSection('Breaking Changes', notes.breaking); + renderSection('Features', notes.feat); + renderSection('Fixes', notes.fix); + renderSection('Other Changes', notes.other); if (notes.docs.length) { - const docs = notes.docs.map(commit => renderLink(commit, explicitLinks)).sort() - rendered.push('## Documentation\n\n', ` * Documentation changes: ${docs.join(', ')}\n`, '\n') + const docs = notes.docs.map(commit => renderLink(commit, explicitLinks)).sort(); + rendered.push('## Documentation\n\n', ` * Documentation changes: ${docs.join(', ')}\n`, '\n'); } - renderSection('Unknown', notes.unknown) + renderSection('Unknown', notes.unknown); - return rendered.join('') -} + return rendered.join(''); +}; /*** **** Module @@ -740,4 +691,4 @@ const renderNotes = (notes, explicitLinks) => { module.exports = { get: getNotes, render: renderNotes -} +}; diff --git a/script/release/prepare-release.js b/script/release/prepare-release.js index e720240a673a4..7cb6451139f51 100755 --- a/script/release/prepare-release.js +++ b/script/release/prepare-release.js @@ -1,105 +1,105 @@ #!/usr/bin/env node -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); const args = require('minimist')(process.argv.slice(2), { boolean: ['automaticRelease', 'notesOnly', 'stable'] -}) -const ciReleaseBuild = require('./ci-release-build') +}); +const ciReleaseBuild = require('./ci-release-build'); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) -const { execSync } = require('child_process') -const { GitProcess } = require('dugite') +}); +const { execSync } = require('child_process'); +const { GitProcess } = require('dugite'); -const path = require('path') -const readline = require('readline') -const releaseNotesGenerator = require('./notes/index.js') -const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils.js') -const bumpType = args._[0] -const targetRepo = bumpType === 'nightly' ? 'nightlies' : 'electron' +const path = require('path'); +const readline = require('readline'); +const releaseNotesGenerator = require('./notes/index.js'); +const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils.js'); +const bumpType = args._[0]; +const targetRepo = bumpType === 'nightly' ? 'nightlies' : 'electron'; -require('colors') -const pass = '✓'.green -const fail = '✗'.red +require('colors'); +const pass = '✓'.green; +const fail = '✗'.red; if (!bumpType && !args.notesOnly) { console.log(`Usage: prepare-release [stable | minor | beta | nightly]` + - ` (--stable) (--notesOnly) (--automaticRelease) (--branch)`) - process.exit(1) + ` (--stable) (--notesOnly) (--automaticRelease) (--branch)`); + process.exit(1); } async function getNewVersion (dryRun) { if (!dryRun) { - console.log(`Bumping for new "${bumpType}" version.`) + console.log(`Bumping for new "${bumpType}" version.`); } - const bumpScript = path.join(__dirname, 'version-bumper.js') - const scriptArgs = ['node', bumpScript, `--bump=${bumpType}`] - if (dryRun) scriptArgs.push('--dryRun') + const bumpScript = path.join(__dirname, 'version-bumper.js'); + const scriptArgs = ['node', bumpScript, `--bump=${bumpType}`]; + if (dryRun) scriptArgs.push('--dryRun'); try { - let bumpVersion = execSync(scriptArgs.join(' '), { encoding: 'UTF-8' }) - bumpVersion = bumpVersion.substr(bumpVersion.indexOf(':') + 1).trim() - const newVersion = `v${bumpVersion}` + let bumpVersion = execSync(scriptArgs.join(' '), { encoding: 'UTF-8' }); + bumpVersion = bumpVersion.substr(bumpVersion.indexOf(':') + 1).trim(); + const newVersion = `v${bumpVersion}`; if (!dryRun) { - console.log(`${pass} Successfully bumped version to ${newVersion}`) + console.log(`${pass} Successfully bumped version to ${newVersion}`); } - return newVersion + return newVersion; } catch (err) { - console.log(`${fail} Could not bump version, error was:`, err) - throw err + console.log(`${fail} Could not bump version, error was:`, err); + throw err; } } async function getReleaseNotes (currentBranch, newVersion) { if (bumpType === 'nightly') { - return { text: 'Nightlies do not get release notes, please compare tags for info.' } + return { text: 'Nightlies do not get release notes, please compare tags for info.' }; } - console.log(`Generating release notes for ${currentBranch}.`) - const releaseNotes = await releaseNotesGenerator(currentBranch, newVersion) + console.log(`Generating release notes for ${currentBranch}.`); + const releaseNotes = await releaseNotesGenerator(currentBranch, newVersion); if (releaseNotes.warning) { - console.warn(releaseNotes.warning) + console.warn(releaseNotes.warning); } - return releaseNotes + return releaseNotes; } async function createRelease (branchToTarget, isBeta) { - const newVersion = await getNewVersion() - const releaseNotes = await getReleaseNotes(branchToTarget, newVersion) - await tagRelease(newVersion) + const newVersion = await getNewVersion(); + const releaseNotes = await getReleaseNotes(branchToTarget, newVersion); + await tagRelease(newVersion); - console.log(`Checking for existing draft release.`) + console.log(`Checking for existing draft release.`); const releases = await octokit.repos.listReleases({ owner: 'electron', repo: targetRepo }).catch(err => { - console.log(`${fail} Could not get releases. Error was: `, err) - }) + console.log(`${fail} Could not get releases. Error was: `, err); + }); const drafts = releases.data.filter(release => release.draft && - release.tag_name === newVersion) + release.tag_name === newVersion); if (drafts.length > 0) { console.log(`${fail} Aborting because draft release for - ${drafts[0].tag_name} already exists.`) - process.exit(1) + ${drafts[0].tag_name} already exists.`); + process.exit(1); } - console.log(`${pass} A draft release does not exist; creating one.`) + console.log(`${pass} A draft release does not exist; creating one.`); - let releaseBody - let releaseIsPrelease = false + let releaseBody; + let releaseIsPrelease = false; if (isBeta) { if (newVersion.indexOf('nightly') > 0) { releaseBody = `Note: This is a nightly release. Please file new issues ` + `for any bugs you find in it.\n \n This release is published to npm ` + `under the nightly tag and can be installed via npm install electron@nightly, ` + - `or npm i electron-nightly@${newVersion.substr(1)}.\n \n ${releaseNotes.text}` + `or npm i electron-nightly@${newVersion.substr(1)}.\n \n ${releaseNotes.text}`; } else { releaseBody = `Note: This is a beta release. Please file new issues ` + `for any bugs you find in it.\n \n This release is published to npm ` + `under the beta tag and can be installed via npm install electron@beta, ` + - `or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes.text}` + `or npm i electron@${newVersion.substr(1)}.\n \n ${releaseNotes.text}`; } - releaseIsPrelease = true + releaseIsPrelease = true; } else { - releaseBody = releaseNotes.text + releaseBody = releaseNotes.text; } const release = await octokit.repos.createRelease({ @@ -112,22 +112,22 @@ async function createRelease (branchToTarget, isBeta) { prerelease: releaseIsPrelease, target_commitish: newVersion.indexOf('nightly') !== -1 ? 'master' : branchToTarget }).catch(err => { - console.log(`${fail} Error creating new release: `, err) - process.exit(1) - }) + console.log(`${fail} Error creating new release: `, err); + process.exit(1); + }); - console.log(`Release has been created with id: ${release.data.id}.`) - console.log(`${pass} Draft release for ${newVersion} successful.`) + console.log(`Release has been created with id: ${release.data.id}.`); + console.log(`${pass} Draft release for ${newVersion} successful.`); } async function pushRelease (branch) { - const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR) + const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR); if (pushDetails.exitCode === 0) { console.log(`${pass} Successfully pushed the release. Wait for ` + - `release builds to finish before running "npm run release".`) + `release builds to finish before running "npm run release".`); } else { - console.log(`${fail} Error pushing the release: ${pushDetails.stderr}`) - process.exit(1) + console.log(`${fail} Error pushing the release: ${pushDetails.stderr}`); + process.exit(1); } } @@ -135,34 +135,34 @@ async function runReleaseBuilds (branch) { await ciReleaseBuild(branch, { ghRelease: true, automaticRelease: args.automaticRelease - }) + }); } async function tagRelease (version) { - console.log(`Tagging release ${version}.`) - const checkoutDetails = await GitProcess.exec([ 'tag', '-a', '-m', version, version ], ELECTRON_DIR) + console.log(`Tagging release ${version}.`); + const checkoutDetails = await GitProcess.exec([ 'tag', '-a', '-m', version, version ], ELECTRON_DIR); if (checkoutDetails.exitCode === 0) { - console.log(`${pass} Successfully tagged ${version}.`) + console.log(`${pass} Successfully tagged ${version}.`); } else { console.log(`${fail} Error tagging ${version}: ` + - `${checkoutDetails.stderr}`) - process.exit(1) + `${checkoutDetails.stderr}`); + process.exit(1); } } async function verifyNewVersion () { - const newVersion = await getNewVersion(true) - let response + const newVersion = await getNewVersion(true); + let response; if (args.automaticRelease) { - response = 'y' + response = 'y'; } else { - response = await promptForVersion(newVersion) + response = await promptForVersion(newVersion); } if (response.match(/^y/i)) { - console.log(`${pass} Starting release of ${newVersion}`) + console.log(`${pass} Starting release of ${newVersion}`); } else { - console.log(`${fail} Aborting release of ${newVersion}`) - process.exit() + console.log(`${fail} Aborting release of ${newVersion}`); + process.exit(); } } @@ -171,44 +171,44 @@ async function promptForVersion (version) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout - }) + }); rl.question(`Do you want to create the release ${version.green} (y/N)? `, (answer) => { - rl.close() - resolve(answer) - }) - }) + rl.close(); + resolve(answer); + }); + }); } // function to determine if there have been commits to master 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) - return !lastCommitWasRelease.test(lastCommit.stdout) + 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); + return !lastCommitWasRelease.test(lastCommit.stdout); } async function prepareRelease (isBeta, notesOnly) { if (args.dryRun) { - const newVersion = await getNewVersion(true) - console.log(newVersion) + const newVersion = await getNewVersion(true); + console.log(newVersion); } else { - const currentBranch = (args.branch) ? args.branch : await getCurrentBranch(ELECTRON_DIR) + const currentBranch = (args.branch) ? args.branch : await getCurrentBranch(ELECTRON_DIR); if (notesOnly) { - const newVersion = await getNewVersion(true) - const releaseNotes = await getReleaseNotes(currentBranch, newVersion) - console.log(`Draft release notes are: \n${releaseNotes.text}`) + const newVersion = await getNewVersion(true); + const releaseNotes = await getReleaseNotes(currentBranch, newVersion); + console.log(`Draft release notes are: \n${releaseNotes.text}`); } else { - const changes = await changesToRelease(currentBranch) + const changes = await changesToRelease(currentBranch); if (changes) { - await verifyNewVersion() - await createRelease(currentBranch, isBeta) - await pushRelease(currentBranch) - await runReleaseBuilds(currentBranch) + await verifyNewVersion(); + await createRelease(currentBranch, isBeta); + await pushRelease(currentBranch); + await runReleaseBuilds(currentBranch); } else { - console.log(`There are no new changes to this branch since the last release, aborting release.`) - process.exit(1) + console.log(`There are no new changes to this branch since the last release, aborting release.`); + process.exit(1); } } } } -prepareRelease(!args.stable, args.notesOnly) +prepareRelease(!args.stable, args.notesOnly); diff --git a/script/release/publish-to-npm.js b/script/release/publish-to-npm.js index 007823e2a50df..93cb57d65a5a2 100644 --- a/script/release/publish-to-npm.js +++ b/script/release/publish-to-npm.js @@ -1,22 +1,22 @@ -const temp = require('temp') -const fs = require('fs') -const path = require('path') -const childProcess = require('child_process') -const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils') -const request = require('request') -const semver = require('semver') -const rootPackageJson = require('../../package.json') +const temp = require('temp'); +const fs = require('fs'); +const path = require('path'); +const childProcess = require('child_process'); +const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils'); +const request = require('request'); +const semver = require('semver'); +const rootPackageJson = require('../../package.json'); const octokit = require('@octokit/rest')({ headers: { 'User-Agent': 'electron-npm-publisher' } -}) +}); if (!process.env.ELECTRON_NPM_OTP) { - console.error('Please set ELECTRON_NPM_OTP') - process.exit(1) + console.error('Please set ELECTRON_NPM_OTP'); + process.exit(1); } -let tempDir -temp.track() // track and cleanup files at exit +let tempDir; +temp.track(); // track and cleanup files at exit const files = [ 'cli.js', @@ -25,7 +25,7 @@ const files = [ 'package.json', 'README.md', 'LICENSE' -] +]; const jsonFields = [ 'name', @@ -35,58 +35,58 @@ const jsonFields = [ 'license', 'author', 'keywords' -] +]; -let npmTag = '' +let npmTag = ''; new Promise((resolve, reject) => { temp.mkdir('electron-npm', (err, dirPath) => { if (err) { - reject(err) + reject(err); } else { - resolve(dirPath) + resolve(dirPath); } - }) + }); }) .then((dirPath) => { - tempDir = dirPath + tempDir = dirPath; // copy files from `/npm` to temp directory files.forEach((name) => { - const noThirdSegment = name === 'README.md' || name === 'LICENSE' + const noThirdSegment = name === 'README.md' || name === 'LICENSE'; fs.writeFileSync( path.join(tempDir, name), fs.readFileSync(path.join(ELECTRON_DIR, noThirdSegment ? '' : 'npm', name)) - ) - }) + ); + }); // copy from root package.json to temp/package.json - const packageJson = require(path.join(tempDir, 'package.json')) + const packageJson = require(path.join(tempDir, 'package.json')); jsonFields.forEach((fieldName) => { - packageJson[fieldName] = rootPackageJson[fieldName] - }) + packageJson[fieldName] = rootPackageJson[fieldName]; + }); fs.writeFileSync( path.join(tempDir, 'package.json'), JSON.stringify(packageJson, null, 2) - ) + ); return octokit.repos.listReleases({ owner: 'electron', repo: rootPackageJson.version.indexOf('nightly') > 0 ? 'nightlies' : 'electron' - }) + }); }) .then((releases) => { // download electron.d.ts from release const release = releases.data.find( (release) => release.tag_name === `v${rootPackageJson.version}` - ) + ); if (!release) { - throw new Error(`cannot find release with tag v${rootPackageJson.version}`) + throw new Error(`cannot find release with tag v${rootPackageJson.version}`); } - return release + return release; }) .then((release) => { - const tsdAsset = release.assets.find((asset) => asset.name === 'electron.d.ts') + const tsdAsset = release.assets.find((asset) => asset.name === 'electron.d.ts'); if (!tsdAsset) { - throw new Error(`cannot find electron.d.ts from v${rootPackageJson.version} release assets`) + throw new Error(`cannot find electron.d.ts from v${rootPackageJson.version} release assets`); } return new Promise((resolve, reject) => { request.get({ @@ -97,78 +97,78 @@ new Promise((resolve, reject) => { } }, (err, response, body) => { if (err || response.statusCode !== 200) { - reject(err || new Error('Cannot download electron.d.ts')) + reject(err || new Error('Cannot download electron.d.ts')); } else { - fs.writeFileSync(path.join(tempDir, 'electron.d.ts'), body) - resolve(release) + fs.writeFileSync(path.join(tempDir, 'electron.d.ts'), body); + resolve(release); } - }) - }) + }); + }); }) .then(async (release) => { - const currentBranch = await getCurrentBranch() + 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 - npmTag = 'latest' + npmTag = 'latest'; } else { - npmTag = `nightly-${currentBranch}` + npmTag = `nightly-${currentBranch}`; } - const currentJson = JSON.parse(fs.readFileSync(path.join(tempDir, 'package.json'), 'utf8')) - currentJson.name = 'electron-nightly' - rootPackageJson.name = 'electron-nightly' + const currentJson = JSON.parse(fs.readFileSync(path.join(tempDir, 'package.json'), 'utf8')); + currentJson.name = 'electron-nightly'; + rootPackageJson.name = 'electron-nightly'; fs.writeFileSync( path.join(tempDir, 'package.json'), JSON.stringify(currentJson, null, 2) - ) + ); } else { if (currentBranch === 'master') { // This should never happen, master releases should be nightly releases // this is here just-in-case - npmTag = 'master' + npmTag = 'master'; } else if (!release.prerelease) { // Tag the release with a `2-0-x` style tag - npmTag = currentBranch + npmTag = currentBranch; } else { // Tag the release with a `beta-3-0-x` style tag - npmTag = `beta-${currentBranch}` + npmTag = `beta-${currentBranch}`; } } }) .then(() => childProcess.execSync('npm pack', { cwd: tempDir })) .then(() => { // test that the package can install electron prebuilt from github release - const tarballPath = path.join(tempDir, `${rootPackageJson.name}-${rootPackageJson.version}.tgz`) + const tarballPath = path.join(tempDir, `${rootPackageJson.name}-${rootPackageJson.version}.tgz`); return new Promise((resolve, reject) => { childProcess.execSync(`npm install ${tarballPath} --force --silent`, { env: Object.assign({}, process.env, { electron_config_cache: tempDir }), cwd: tempDir - }) - resolve(tarballPath) - }) + }); + resolve(tarballPath); + }); }) .then((tarballPath) => 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 - const parsedLocalVersion = semver.parse(localVersion) + const currentTags = JSON.parse(childProcess.execSync('npm show electron dist-tags --json').toString()); + const localVersion = rootPackageJson.version; + const parsedLocalVersion = semver.parse(localVersion); if (rootPackageJson.name === 'electron') { // We should only customly add dist tags for non-nightly releases where the package name is still // "electron" if (parsedLocalVersion.prerelease.length === 0 && semver.gt(localVersion, currentTags.latest)) { - childProcess.execSync(`npm dist-tag add electron@${localVersion} latest --otp=${process.env.ELECTRON_NPM_OTP}`) + childProcess.execSync(`npm dist-tag add electron@${localVersion} latest --otp=${process.env.ELECTRON_NPM_OTP}`); } if (parsedLocalVersion.prerelease[0] === 'beta' && semver.gt(localVersion, currentTags.beta)) { - childProcess.execSync(`npm dist-tag add electron@${localVersion} beta --otp=${process.env.ELECTRON_NPM_OTP}`) + childProcess.execSync(`npm dist-tag add electron@${localVersion} beta --otp=${process.env.ELECTRON_NPM_OTP}`); } } }) .catch((err) => { - console.error(`Error: ${err}`) - process.exit(1) - }) + console.error(`Error: ${err}`); + process.exit(1); + }); diff --git a/script/release/release-artifact-cleanup.js b/script/release/release-artifact-cleanup.js index 561ecbf12d54c..57bbf2c5e6503 100755 --- a/script/release/release-artifact-cleanup.js +++ b/script/release/release-artifact-cleanup.js @@ -1,39 +1,39 @@ #!/usr/bin/env node -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); const args = require('minimist')(process.argv.slice(2), { string: ['tag', 'releaseID'], default: { releaseID: '' } -}) -const path = require('path') -const { execSync } = require('child_process') -const { GitProcess } = require('dugite') -const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils.js') +}); +const path = require('path'); +const { execSync } = require('child_process'); +const { GitProcess } = require('dugite'); +const { getCurrentBranch, ELECTRON_DIR } = require('../lib/utils.js'); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) +}); -require('colors') -const pass = '✓'.green -const fail = '✗'.red +require('colors'); +const pass = '✓'.green; +const fail = '✗'.red; function getLastBumpCommit (tag) { - const data = execSync(`git log -n1 --grep "Bump ${tag}" --format='format:{"hash": "%H", "message": "%s"}'`).toString() - return JSON.parse(data) + const data = execSync(`git log -n1 --grep "Bump ${tag}" --format='format:{"hash": "%H", "message": "%s"}'`).toString(); + return JSON.parse(data); } async function revertBumpCommit (tag) { - const branch = await getCurrentBranch() - const commitToRevert = getLastBumpCommit(tag).hash - await GitProcess.exec(['revert', commitToRevert], ELECTRON_DIR) - const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR) + const branch = await getCurrentBranch(); + const commitToRevert = getLastBumpCommit(tag).hash; + await GitProcess.exec(['revert', commitToRevert], ELECTRON_DIR); + const pushDetails = await GitProcess.exec(['push', 'origin', `HEAD:${branch}`, '--follow-tags'], ELECTRON_DIR); if (pushDetails.exitCode === 0) { - console.log(`${pass} successfully reverted release commit.`) + console.log(`${pass} successfully reverted release commit.`); } else { - const error = GitProcess.parseError(pushDetails.stderr) - console.error(`${fail} could not push release commit: `, error) - process.exit(1) + const error = GitProcess.parseError(pushDetails.stderr); + console.error(`${fail} could not push release commit: `, error); + process.exit(1); } } @@ -43,23 +43,22 @@ async function deleteDraft (releaseId, targetRepo) { owner: 'electron', repo: targetRepo, release_id: parseInt(releaseId, 10) - }) - console.log(result) + }); if (!result.data.draft) { - console.log(`${fail} published releases cannot be deleted.`) - return false + console.log(`${fail} published releases cannot be deleted.`); + return false; } else { await octokit.repos.deleteRelease({ owner: 'electron', repo: targetRepo, release_id: result.data.id - }) + }); } - console.log(`${pass} successfully deleted draft with id ${releaseId} from ${targetRepo}`) - return true + console.log(`${pass} successfully deleted draft with id ${releaseId} from ${targetRepo}`); + return true; } catch (err) { - console.error(`${fail} couldn't delete draft with id ${releaseId} from ${targetRepo}: `, err) - return false + console.error(`${fail} couldn't delete draft with id ${releaseId} from ${targetRepo}: `, err); + return false; } } @@ -69,46 +68,42 @@ async function deleteTag (tag, targetRepo) { owner: 'electron', repo: targetRepo, ref: `tags/${tag}` - }) - console.log(`${pass} successfully deleted tag ${tag} from ${targetRepo}`) + }); + console.log(`${pass} successfully deleted tag ${tag} from ${targetRepo}`); } catch (err) { - console.log(`${fail} couldn't delete tag ${tag} from ${targetRepo}: `, err) + console.log(`${fail} couldn't delete tag ${tag} from ${targetRepo}: `, err); } } async function cleanReleaseArtifacts () { - const releaseId = args.releaseID.length > 0 ? args.releaseID : null - const isNightly = args.tag.includes('nightly') + const releaseId = args.releaseID.length > 0 ? args.releaseID : null; + const isNightly = args.tag.includes('nightly'); // try to revert commit regardless of tag and draft deletion status - await revertBumpCommit(args.tag) + await revertBumpCommit(args.tag); if (releaseId) { if (isNightly) { - const deletedNightlyDraft = await deleteDraft(releaseId, 'nightlies') + await deleteDraft(releaseId, 'nightlies'); - // don't delete tag unless draft deleted successfully - if (deletedNightlyDraft) { - await Promise.all([ - deleteTag(args.tag, 'electron'), - deleteTag(args.tag, 'nightlies') - ]) - } + // We only need to delete the Electron tag since the + // nightly tag is only created at publish-time. + await deleteTag(args.tag, 'electron'); } else { - const deletedElectronDraft = await deleteDraft(releaseId, 'electron') + const deletedElectronDraft = await deleteDraft(releaseId, 'electron'); // don't delete tag unless draft deleted successfully if (deletedElectronDraft) { - await deleteTag(args.tag, 'electron') + await deleteTag(args.tag, 'electron'); } } } else { await Promise.all([ deleteTag(args.tag, 'electron'), deleteTag(args.tag, 'nightlies') - ]) + ]); } - console.log(`${pass} failed release artifact cleanup complete`) + console.log(`${pass} failed release artifact cleanup complete`); } -cleanReleaseArtifacts() +cleanReleaseArtifacts(); diff --git a/script/release/release.js b/script/release/release.js index 43f0e647ca169..91c6abc4f8aec 100755 --- a/script/release/release.js +++ b/script/release/release.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); const args = require('minimist')(process.argv.slice(2), { boolean: [ @@ -10,87 +10,87 @@ const args = require('minimist')(process.argv.slice(2), { 'verboseNugget' ], default: { 'verboseNugget': false } -}) -const fs = require('fs') -const { execSync } = require('child_process') -const nugget = require('nugget') -const got = require('got') -const pkg = require('../../package.json') -const pkgVersion = `v${pkg.version}` -const path = require('path') -const sumchecker = require('sumchecker') -const temp = require('temp').track() -const { URL } = require('url') - -require('colors') -const pass = '✓'.green -const fail = '✗'.red - -const { ELECTRON_DIR } = require('../lib/utils') +}); +const fs = require('fs'); +const { execSync } = require('child_process'); +const nugget = require('nugget'); +const got = require('got'); +const pkg = require('../../package.json'); +const pkgVersion = `v${pkg.version}`; +const path = require('path'); +const sumchecker = require('sumchecker'); +const temp = require('temp').track(); +const { URL } = require('url'); + +require('colors'); +const pass = '✓'.green; +const fail = '✗'.red; + +const { ELECTRON_DIR } = require('../lib/utils'); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) +}); -const targetRepo = pkgVersion.indexOf('nightly') > 0 ? 'nightlies' : 'electron' -let failureCount = 0 +const targetRepo = pkgVersion.indexOf('nightly') > 0 ? 'nightlies' : 'electron'; +let failureCount = 0; async function getDraftRelease (version, skipValidation) { const releaseInfo = await octokit.repos.listReleases({ owner: 'electron', repo: targetRepo - }) + }); - const versionToCheck = version || pkgVersion + const versionToCheck = version || pkgVersion; const drafts = releaseInfo.data.filter(release => { - return release.tag_name === versionToCheck && release.draft === true - }) + return release.tag_name === versionToCheck && release.draft === true; + }); - const draft = drafts[0] + const draft = drafts[0]; if (!skipValidation) { - failureCount = 0 - check(drafts.length === 1, 'one draft exists', true) + failureCount = 0; + check(drafts.length === 1, 'one draft exists', true); if (versionToCheck.indexOf('beta') > -1) { - check(draft.prerelease, 'draft is a prerelease') + check(draft.prerelease, 'draft is a prerelease'); } - check(draft.body.length > 50 && !draft.body.includes('(placeholder)'), 'draft has release notes') - check((failureCount === 0), `Draft release looks good to go.`, true) + check(draft.body.length > 50 && !draft.body.includes('(placeholder)'), 'draft has release notes'); + check((failureCount === 0), `Draft release looks good to go.`, true); } - return draft + return draft; } async function validateReleaseAssets (release, validatingRelease) { - const requiredAssets = assetsForVersion(release.tag_name, validatingRelease).sort() - const extantAssets = release.assets.map(asset => asset.name).sort() - const downloadUrls = release.assets.map(asset => asset.browser_download_url).sort() + const requiredAssets = assetsForVersion(release.tag_name, validatingRelease).sort(); + const extantAssets = release.assets.map(asset => asset.name).sort(); + const downloadUrls = release.assets.map(asset => asset.browser_download_url).sort(); - failureCount = 0 + failureCount = 0; requiredAssets.forEach(asset => { - check(extantAssets.includes(asset), asset) - }) - check((failureCount === 0), `All required GitHub assets exist for release`, true) + check(extantAssets.includes(asset), asset); + }); + check((failureCount === 0), `All required GitHub assets exist for release`, true); if (!validatingRelease || !release.draft) { if (release.draft) { - await verifyAssets(release) + await verifyAssets(release); } else { await verifyShasums(downloadUrls) .catch(err => { - console.log(`${fail} error verifyingShasums`, err) - }) + console.log(`${fail} error verifyingShasums`, err); + }); } - const s3Urls = s3UrlsForVersion(release.tag_name) - await verifyShasums(s3Urls, true) + const s3Urls = s3UrlsForVersion(release.tag_name); + await verifyShasums(s3Urls, true); } } function check (condition, statement, exitIfFail = false) { if (condition) { - console.log(`${pass} ${statement}`) + console.log(`${pass} ${statement}`); } else { - failureCount++ - console.log(`${fail} ${statement}`) - if (exitIfFail) process.exit(1) + failureCount++; + console.log(`${fail} ${statement}`); + if (exitIfFail) process.exit(1); } } @@ -114,6 +114,7 @@ function assetsForVersion (version, validatingRelease) { `electron-${version}-linux-armv7l.zip`, `electron-${version}-linux-ia32-symbols.zip`, `electron-${version}-linux-ia32.zip`, + `electron-${version}-linux-x64-debug.zip`, `electron-${version}-linux-x64-symbols.zip`, `electron-${version}-linux-x64.zip`, `electron-${version}-mas-x64-dsym.zip`, @@ -130,6 +131,7 @@ function assetsForVersion (version, validatingRelease) { `electron-${version}-win32-arm64.zip`, `electron-api.json`, `electron.d.ts`, + `hunspell_dictionaries.zip`, `ffmpeg-${version}-darwin-x64.zip`, `ffmpeg-${version}-linux-arm64.zip`, `ffmpeg-${version}-linux-armv7l.zip`, @@ -148,15 +150,15 @@ function assetsForVersion (version, validatingRelease) { `mksnapshot-${version}-win32-ia32.zip`, `mksnapshot-${version}-win32-x64.zip`, `mksnapshot-${version}-win32-arm64-x64.zip` - ] + ]; if (!validatingRelease) { - patterns.push('SHASUMS256.txt') + patterns.push('SHASUMS256.txt'); } - return patterns + return patterns; } function s3UrlsForVersion (version) { - const bucket = `https://gh-contractor-zcbenz.s3.amazonaws.com/` + const bucket = `https://gh-contractor-zcbenz.s3.amazonaws.com/`; const patterns = [ `${bucket}atom-shell/dist/${version}/iojs-${version}-headers.tar.gz`, `${bucket}atom-shell/dist/${version}/iojs-${version}.tar.gz`, @@ -168,66 +170,66 @@ function s3UrlsForVersion (version) { `${bucket}atom-shell/dist/${version}/SHASUMS.txt`, `${bucket}atom-shell/dist/${version}/SHASUMS256.txt`, `${bucket}atom-shell/dist/index.json` - ] - return patterns + ]; + return patterns; } function runScript (scriptName, scriptArgs, cwd) { - const scriptCommand = `${scriptName} ${scriptArgs.join(' ')}` + const scriptCommand = `${scriptName} ${scriptArgs.join(' ')}`; const scriptOptions = { encoding: 'UTF-8' - } - if (cwd) scriptOptions.cwd = cwd + }; + if (cwd) scriptOptions.cwd = cwd; try { - return execSync(scriptCommand, scriptOptions) + return execSync(scriptCommand, scriptOptions); } catch (err) { - console.log(`${fail} Error running ${scriptName}`, err) - process.exit(1) + console.log(`${fail} Error running ${scriptName}`, err); + process.exit(1); } } function uploadNodeShasums () { - console.log('Uploading Node SHASUMS file to S3.') - const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-node-checksums.py') - runScript(scriptPath, ['-v', pkgVersion]) - console.log(`${pass} Done uploading Node SHASUMS file to S3.`) + console.log('Uploading Node SHASUMS file to S3.'); + const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-node-checksums.py'); + runScript(scriptPath, ['-v', pkgVersion]); + console.log(`${pass} Done uploading Node SHASUMS file to S3.`); } function uploadIndexJson () { - console.log('Uploading index.json to S3.') - const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-index-json.py') - runScript(scriptPath, [pkgVersion]) - console.log(`${pass} Done uploading index.json to S3.`) + console.log('Uploading index.json to S3.'); + const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'uploaders', 'upload-index-json.py'); + runScript(scriptPath, [pkgVersion]); + console.log(`${pass} Done uploading index.json to S3.`); } async function createReleaseShasums (release) { - const fileName = 'SHASUMS256.txt' - const existingAssets = release.assets.filter(asset => asset.name === fileName) + const fileName = 'SHASUMS256.txt'; + const existingAssets = release.assets.filter(asset => asset.name === fileName); if (existingAssets.length > 0) { - console.log(`${fileName} already exists on GitHub; deleting before creating new file.`) + console.log(`${fileName} already exists on GitHub; deleting before creating new file.`); await octokit.repos.deleteReleaseAsset({ owner: 'electron', repo: targetRepo, asset_id: existingAssets[0].id }).catch(err => { - console.log(`${fail} Error deleting ${fileName} on GitHub:`, err) - }) + console.log(`${fail} Error deleting ${fileName} on GitHub:`, err); + }); } - console.log(`Creating and uploading the release ${fileName}.`) - const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'merge-electron-checksums.py') - const checksums = runScript(scriptPath, ['-v', pkgVersion]) + console.log(`Creating and uploading the release ${fileName}.`); + const scriptPath = path.join(ELECTRON_DIR, 'script', 'release', 'merge-electron-checksums.py'); + const checksums = runScript(scriptPath, ['-v', pkgVersion]); - console.log(`${pass} Generated release SHASUMS.`) - const filePath = await saveShaSumFile(checksums, fileName) + console.log(`${pass} Generated release SHASUMS.`); + const filePath = await saveShaSumFile(checksums, fileName); - console.log(`${pass} Created ${fileName} file.`) - await uploadShasumFile(filePath, fileName, release.id) + console.log(`${pass} Created ${fileName} file.`); + await uploadShasumFile(filePath, fileName, release.id); - console.log(`${pass} Successfully uploaded ${fileName} to GitHub.`) + console.log(`${pass} Successfully uploaded ${fileName} to GitHub.`); } async function uploadShasumFile (filePath, fileName, releaseId) { - const uploadUrl = `https://uploads.github.com/repos/electron/${targetRepo}/releases/${releaseId}/assets{?name,label}` + const uploadUrl = `https://uploads.github.com/repos/electron/${targetRepo}/releases/${releaseId}/assets{?name,label}`; return octokit.repos.uploadReleaseAsset({ url: uploadUrl, headers: { @@ -237,29 +239,29 @@ async function uploadShasumFile (filePath, fileName, releaseId) { file: fs.createReadStream(filePath), name: fileName }).catch(err => { - console.log(`${fail} Error uploading ${filePath} to GitHub:`, err) - process.exit(1) - }) + console.log(`${fail} Error uploading ${filePath} to GitHub:`, err); + process.exit(1); + }); } function saveShaSumFile (checksums, fileName) { return new Promise((resolve, reject) => { temp.open(fileName, (err, info) => { if (err) { - console.log(`${fail} Could not create ${fileName} file`) - process.exit(1) + console.log(`${fail} Could not create ${fileName} file`); + process.exit(1); } else { - fs.writeFileSync(info.fd, checksums) + fs.writeFileSync(info.fd, checksums); fs.close(info.fd, (err) => { if (err) { - console.log(`${fail} Could close ${fileName} file`) - process.exit(1) + console.log(`${fail} Could close ${fileName} file`); + process.exit(1); } - resolve(info.path) - }) + resolve(info.path); + }); } - }) - }) + }); + }); } async function publishRelease (release) { @@ -270,34 +272,34 @@ async function publishRelease (release) { tag_name: release.tag_name, draft: false }).catch(err => { - console.log(`${fail} Error publishing release:`, err) - process.exit(1) - }) + console.log(`${fail} Error publishing release:`, err); + process.exit(1); + }); } async function makeRelease (releaseToValidate) { if (releaseToValidate) { if (releaseToValidate === true) { - releaseToValidate = pkgVersion + releaseToValidate = pkgVersion; } else { - console.log('Release to validate !=== true') + console.log('Release to validate !=== true'); } - console.log(`Validating release ${releaseToValidate}`) - const release = await getDraftRelease(releaseToValidate) - await validateReleaseAssets(release, true) + console.log(`Validating release ${releaseToValidate}`); + const release = await getDraftRelease(releaseToValidate); + await validateReleaseAssets(release, true); } else { - let draftRelease = await getDraftRelease() - uploadNodeShasums() - uploadIndexJson() + let draftRelease = await getDraftRelease(); + uploadNodeShasums(); + uploadIndexJson(); - await createReleaseShasums(draftRelease) + await createReleaseShasums(draftRelease); // Fetch latest version of release before verifying - draftRelease = await getDraftRelease(pkgVersion, true) - await validateReleaseAssets(draftRelease) - await publishRelease(draftRelease) + draftRelease = await getDraftRelease(pkgVersion, true); + await validateReleaseAssets(draftRelease); + await publishRelease(draftRelease); console.log(`${pass} SUCCESS!!! Release has been published. Please run ` + - `"npm run publish-to-npm" to publish release to npm.`) + `"npm run publish-to-npm" to publish release to npm.`); } } @@ -305,19 +307,19 @@ async function makeTempDir () { return new Promise((resolve, reject) => { temp.mkdir('electron-publish', (err, dirPath) => { if (err) { - reject(err) + reject(err); } else { - resolve(dirPath) + resolve(dirPath); } - }) - }) + }); + }); } async function verifyAssets (release) { - const downloadDir = await makeTempDir() + const downloadDir = await makeTempDir(); - console.log(`Downloading files from GitHub to verify shasums`) - const shaSumFile = 'SHASUMS256.txt' + console.log(`Downloading files from GitHub to verify shasums`); + const shaSumFile = 'SHASUMS256.txt'; let filesToCheck = await Promise.all(release.assets.map(async asset => { const requestOptions = await octokit.repos.getReleaseAsset.endpoint({ @@ -327,26 +329,26 @@ async function verifyAssets (release) { headers: { Accept: 'application/octet-stream' } - }) + }); - const { url, headers } = requestOptions - headers.authorization = `token ${process.env.ELECTRON_GITHUB_TOKEN}` + const { url, headers } = requestOptions; + headers.authorization = `token ${process.env.ELECTRON_GITHUB_TOKEN}`; const response = await got(url, { followRedirect: false, method: 'HEAD', headers - }) + }); - await downloadFiles(response.headers.location, downloadDir, asset.name) - return asset.name + await downloadFiles(response.headers.location, downloadDir, asset.name); + return asset.name; })).catch(err => { - console.log(`${fail} Error downloading files from GitHub`, err) - process.exit(1) - }) + console.log(`${fail} Error downloading files from GitHub`, err); + process.exit(1); + }); - filesToCheck = filesToCheck.filter(fileName => fileName !== shaSumFile) - let checkerOpts + filesToCheck = filesToCheck.filter(fileName => fileName !== shaSumFile); + let checkerOpts; await validateChecksums({ algorithm: 'sha256', filesToCheck, @@ -354,71 +356,71 @@ async function verifyAssets (release) { shaSumFile, checkerOpts, fileSource: 'GitHub' - }) + }); } function downloadFiles (urls, directory, targetName) { return new Promise((resolve, reject) => { - const nuggetOpts = { dir: directory } - nuggetOpts.quiet = !args.verboseNugget - if (targetName) nuggetOpts.target = targetName + const nuggetOpts = { dir: directory }; + nuggetOpts.quiet = !args.verboseNugget; + if (targetName) nuggetOpts.target = targetName; nugget(urls, nuggetOpts, (err) => { if (err) { - reject(err) + reject(err); } else { - console.log(`${pass} all files downloaded successfully!`) - resolve() + console.log(`${pass} all files downloaded successfully!`); + resolve(); } - }) - }) + }); + }); } async function verifyShasums (urls, isS3) { - const fileSource = isS3 ? 'S3' : 'GitHub' - console.log(`Downloading files from ${fileSource} to verify shasums`) - const downloadDir = await makeTempDir() - let filesToCheck = [] + const fileSource = isS3 ? 'S3' : 'GitHub'; + console.log(`Downloading files from ${fileSource} to verify shasums`); + const downloadDir = await makeTempDir(); + let filesToCheck = []; try { if (!isS3) { - await downloadFiles(urls, downloadDir) + await downloadFiles(urls, downloadDir); filesToCheck = urls.map(url => { - const currentUrl = new URL(url) - return path.basename(currentUrl.pathname) - }).filter(file => file.indexOf('SHASUMS') === -1) + const currentUrl = new URL(url); + return path.basename(currentUrl.pathname); + }).filter(file => file.indexOf('SHASUMS') === -1); } else { - const s3VersionPath = `/atom-shell/dist/${pkgVersion}/` + const s3VersionPath = `/atom-shell/dist/${pkgVersion}/`; await Promise.all(urls.map(async (url) => { - const currentUrl = new URL(url) - const dirname = path.dirname(currentUrl.pathname) - const filename = path.basename(currentUrl.pathname) - const s3VersionPathIdx = dirname.indexOf(s3VersionPath) + const currentUrl = new URL(url); + const dirname = path.dirname(currentUrl.pathname); + const filename = path.basename(currentUrl.pathname); + const s3VersionPathIdx = dirname.indexOf(s3VersionPath); if (s3VersionPathIdx === -1 || dirname === s3VersionPath) { if (s3VersionPathIdx !== -1 && filename.indexof('SHASUMS') === -1) { - filesToCheck.push(filename) + filesToCheck.push(filename); } - await downloadFiles(url, downloadDir) + await downloadFiles(url, downloadDir); } else { - const subDirectory = dirname.substr(s3VersionPathIdx + s3VersionPath.length) - const fileDirectory = path.join(downloadDir, subDirectory) + const subDirectory = dirname.substr(s3VersionPathIdx + s3VersionPath.length); + const fileDirectory = path.join(downloadDir, subDirectory); try { - fs.statSync(fileDirectory) + fs.statSync(fileDirectory); } catch (err) { - fs.mkdirSync(fileDirectory) + fs.mkdirSync(fileDirectory); } - filesToCheck.push(path.join(subDirectory, filename)) - await downloadFiles(url, fileDirectory) + filesToCheck.push(path.join(subDirectory, filename)); + await downloadFiles(url, fileDirectory); } - })) + })); } } catch (err) { - console.log(`${fail} Error downloading files from ${fileSource}`, err) - process.exit(1) + console.log(`${fail} Error downloading files from ${fileSource}`, err); + process.exit(1); } - console.log(`${pass} Successfully downloaded the files from ${fileSource}.`) - let checkerOpts + console.log(`${pass} Successfully downloaded the files from ${fileSource}.`); + let checkerOpts; if (isS3) { - checkerOpts = { defaultTextEncoding: 'binary' } + checkerOpts = { defaultTextEncoding: 'binary' }; } await validateChecksums({ @@ -428,7 +430,7 @@ async function verifyShasums (urls, isS3) { shaSumFile: 'SHASUMS256.txt', checkerOpts, fileSource - }) + }); if (isS3) { await validateChecksums({ @@ -438,37 +440,37 @@ async function verifyShasums (urls, isS3) { shaSumFile: 'SHASUMS.txt', checkerOpts, fileSource - }) + }); } } async function validateChecksums (validationArgs) { console.log(`Validating checksums for files from ${validationArgs.fileSource} ` + - `against ${validationArgs.shaSumFile}.`) - const shaSumFilePath = path.join(validationArgs.fileDirectory, validationArgs.shaSumFile) + `against ${validationArgs.shaSumFile}.`); + const shaSumFilePath = path.join(validationArgs.fileDirectory, validationArgs.shaSumFile); const checker = new sumchecker.ChecksumValidator(validationArgs.algorithm, - shaSumFilePath, validationArgs.checkerOpts) + shaSumFilePath, validationArgs.checkerOpts); await checker.validate(validationArgs.fileDirectory, validationArgs.filesToCheck) .catch(err => { if (err instanceof sumchecker.ChecksumMismatchError) { console.error(`${fail} The checksum of ${err.filename} from ` + `${validationArgs.fileSource} did not match the shasum in ` + - `${validationArgs.shaSumFile}`) + `${validationArgs.shaSumFile}`); } else if (err instanceof sumchecker.ChecksumParseError) { console.error(`${fail} The checksum file ${validationArgs.shaSumFile} ` + - `from ${validationArgs.fileSource} could not be parsed.`, err) + `from ${validationArgs.fileSource} could not be parsed.`, err); } else if (err instanceof sumchecker.NoChecksumFoundError) { console.error(`${fail} The file ${err.filename} from ` + `${validationArgs.fileSource} was not in the shasum file ` + - `${validationArgs.shaSumFile}.`) + `${validationArgs.shaSumFile}.`); } else { console.error(`${fail} Error matching files from ` + - `${validationArgs.fileSource} shasums in ${validationArgs.shaSumFile}.`, err) + `${validationArgs.fileSource} shasums in ${validationArgs.shaSumFile}.`, err); } - process.exit(1) - }) + process.exit(1); + }); console.log(`${pass} All files from ${validationArgs.fileSource} match ` + - `shasums defined in ${validationArgs.shaSumFile}.`) + `shasums defined in ${validationArgs.shaSumFile}.`); } -makeRelease(args.validateRelease) +makeRelease(args.validateRelease); diff --git a/script/release/uploaders/upload-symbols.py b/script/release/uploaders/upload-symbols.py index 9a97705813082..a320dd3871161 100755 --- a/script/release/uploaders/upload-symbols.py +++ b/script/release/uploaders/upload-symbols.py @@ -30,7 +30,7 @@ def main(): run_symstore(pdb, SYMBOLS_DIR, PRODUCT_NAME) files = glob.glob(SYMBOLS_DIR + '/*.pdb/*/*.pdb') else: - files = glob.glob(SYMBOLS_DIR + '/*/*/*.sym') + files = glob.glob(SYMBOLS_DIR + '/*/*/*.sym') + glob.glob(SYMBOLS_DIR + '/*/*/*.src.zip') # The file upload needs to be atom-shell/symbols/:symbol_name/:hash/:symbol os.chdir(SYMBOLS_DIR) diff --git a/script/release/uploaders/upload-to-github.js b/script/release/uploaders/upload-to-github.js index f53646b4884ba..549788edb273a 100644 --- a/script/release/uploaders/upload-to-github.js +++ b/script/release/uploaders/upload-to-github.js @@ -1,40 +1,40 @@ -if (!process.env.CI) require('dotenv-safe').load() +if (!process.env.CI) require('dotenv-safe').load(); -const fs = require('fs') +const fs = require('fs'); const octokit = require('@octokit/rest')({ auth: process.env.ELECTRON_GITHUB_TOKEN -}) +}); if (process.argv.length < 6) { - console.log('Usage: upload-to-github filePath fileName releaseId') - process.exit(1) + console.log('Usage: upload-to-github filePath fileName releaseId'); + process.exit(1); } -const filePath = process.argv[2] -const fileName = process.argv[3] -const releaseId = process.argv[4] -const releaseVersion = process.argv[5] +const filePath = process.argv[2]; +const fileName = process.argv[3]; +const releaseId = process.argv[4]; +const releaseVersion = process.argv[5]; const getHeaders = (filePath, fileName) => { - const extension = fileName.split('.').pop() - const size = fs.statSync(filePath).size + const extension = fileName.split('.').pop(); + const size = fs.statSync(filePath).size; const options = { 'json': 'text/json', 'zip': 'application/zip', 'txt': 'text/plain', 'ts': 'application/typescript' - } + }; return { 'content-type': options[extension], 'content-length': size - } -} + }; +}; -const targetRepo = releaseVersion.indexOf('nightly') > 0 ? 'nightlies' : 'electron' -const uploadUrl = `https://uploads.github.com/repos/electron/${targetRepo}/releases/${releaseId}/assets{?name,label}` -let retry = 0 +const targetRepo = releaseVersion.indexOf('nightly') > 0 ? 'nightlies' : 'electron'; +const uploadUrl = `https://uploads.github.com/repos/electron/${targetRepo}/releases/${releaseId}/assets{?name,label}`; +let retry = 0; function uploadToGitHub () { octokit.repos.uploadReleaseAsset({ @@ -43,12 +43,12 @@ function uploadToGitHub () { file: fs.createReadStream(filePath), name: fileName }).then(() => { - console.log(`Successfully uploaded ${fileName} to GitHub.`) - process.exit() + console.log(`Successfully uploaded ${fileName} to GitHub.`); + process.exit(); }).catch((err) => { if (retry < 4) { - console.log(`Error uploading ${fileName} to GitHub, will retry. Error was:`, err) - retry++ + console.log(`Error uploading ${fileName} to GitHub, will retry. Error was:`, err); + retry++; octokit.repos.listAssetsForRelease({ owner: 'electron', @@ -56,31 +56,31 @@ function uploadToGitHub () { release_id: releaseId, per_page: 100 }).then(assets => { - console.log('Got list of assets for existing release:') - console.log(JSON.stringify(assets.data, null, ' ')) - const existingAssets = assets.data.filter(asset => asset.name === fileName) + console.log('Got list of assets for existing release:'); + console.log(JSON.stringify(assets.data, null, ' ')); + const existingAssets = assets.data.filter(asset => asset.name === fileName); if (existingAssets.length > 0) { - console.log(`${fileName} already exists; will delete before retrying upload.`) + console.log(`${fileName} already exists; will delete before retrying upload.`); octokit.repos.deleteReleaseAsset({ owner: 'electron', repo: targetRepo, asset_id: existingAssets[0].id }).catch((deleteErr) => { - console.log(`Failed to delete existing asset ${fileName}. Error was:`, deleteErr) - }).then(uploadToGitHub) + console.log(`Failed to delete existing asset ${fileName}. Error was:`, deleteErr); + }).then(uploadToGitHub); } else { - console.log(`Current asset ${fileName} not found in existing assets; retrying upload.`) - uploadToGitHub() + console.log(`Current asset ${fileName} not found in existing assets; retrying upload.`); + uploadToGitHub(); } }).catch((getReleaseErr) => { - console.log(`Fatal: Unable to get current release assets via getRelease! Error was:`, getReleaseErr) - }) + console.log(`Fatal: Unable to get current release assets via getRelease! Error was:`, getReleaseErr); + }); } else { - console.log(`Error retrying uploading ${fileName} to GitHub:`, err) - process.exitCode = 1 + console.log(`Error retrying uploading ${fileName} to GitHub:`, err); + process.exitCode = 1; } - }) + }); } -uploadToGitHub() +uploadToGitHub(); diff --git a/script/release/uploaders/upload.py b/script/release/uploaders/upload.py index de0954a9148e6..23902f0b15b36 100755 --- a/script/release/uploaders/upload.py +++ b/script/release/uploaders/upload.py @@ -35,6 +35,7 @@ SYMBOLS_NAME = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'symbols') DSYM_NAME = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'dsym') PDB_NAME = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'pdb') +DEBUG_NAME = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, 'debug') def main(): @@ -83,6 +84,10 @@ def main(): pdb_zip = os.path.join(OUT_DIR, PDB_NAME) shutil.copy2(os.path.join(OUT_DIR, 'pdb.zip'), pdb_zip) upload_electron(release, pdb_zip, args) + elif PLATFORM == 'linux': + debug_zip = os.path.join(OUT_DIR, DEBUG_NAME) + shutil.copy2(os.path.join(OUT_DIR, 'debug.zip'), debug_zip) + upload_electron(release, debug_zip, args) # Upload free version of ffmpeg. ffmpeg = get_zip_name('ffmpeg', ELECTRON_VERSION) @@ -106,6 +111,11 @@ def main(): shutil.copy2(os.path.join(OUT_DIR, 'mksnapshot.zip'), mksnapshot_zip) upload_electron(release, mksnapshot_zip, args) + if PLATFORM == 'linux' and get_target_arch() == 'x64': + # Upload the hunspell dictionaries only from the linux x64 build + hunspell_dictionaries_zip = os.path.join(OUT_DIR, 'hunspell_dictionaries.zip') + upload_electron(release, hunspell_dictionaries_zip, args) + if not tag_exists and not args.upload_to_s3: # Upload symbols to symbol server. run_python_upload_script('upload-symbols.py') diff --git a/script/release/version-bumper.js b/script/release/version-bumper.js index 96ef9c3dadc47..6b6edd38e7827 100644 --- a/script/release/version-bumper.js +++ b/script/release/version-bumper.js @@ -1,26 +1,26 @@ #!/usr/bin/env node -const { GitProcess } = require('dugite') -const fs = require('fs') -const semver = require('semver') -const path = require('path') -const { promisify } = require('util') -const minimist = require('minimist') +const { GitProcess } = require('dugite'); +const fs = require('fs'); +const semver = require('semver'); +const path = require('path'); +const { promisify } = require('util'); +const minimist = require('minimist'); -const { ELECTRON_DIR } = require('../lib/utils') -const versionUtils = require('./version-utils') +const { ELECTRON_DIR } = require('../lib/utils'); +const versionUtils = require('./version-utils'); -const writeFile = promisify(fs.writeFile) -const readFile = promisify(fs.readFile) +const writeFile = promisify(fs.writeFile); +const readFile = promisify(fs.readFile); function parseCommandLine () { - let help + let help; const opts = minimist(process.argv.slice(2), { string: [ 'bump', 'version' ], boolean: [ 'dryRun', 'help' ], alias: { 'version': ['v'] }, - unknown: arg => { help = true } - }) + unknown: arg => { help = true; } + }); if (help || opts.help || !opts.bump) { console.log(` Bump release version number. Possible arguments:\n @@ -28,30 +28,30 @@ function parseCommandLine () { --version={version} to set version number directly\n --dryRun to print the next version without updating files Note that you can use both --bump and --stable simultaneously. - `) - process.exit(0) + `); + process.exit(0); } - return opts + return opts; } // run the script async function main () { - const opts = parseCommandLine() - const currentVersion = await versionUtils.getElectronVersion() - const version = await nextVersion(opts.bump, currentVersion) + const opts = parseCommandLine(); + const currentVersion = await versionUtils.getElectronVersion(); + const version = await nextVersion(opts.bump, currentVersion); - const parsed = semver.parse(version) + const parsed = semver.parse(version); const components = { major: parsed.major, minor: parsed.minor, patch: parsed.patch, pre: parsed.prerelease - } + }; // print would-be new version and exit early if (opts.dryRun) { - console.log(`new version number would be: ${version}\n`) - return 0 + console.log(`new version number would be: ${version}\n`); + return 0; } // update all version-related files @@ -59,12 +59,12 @@ async function main () { updateVersion(version), updatePackageJSON(version), updateWinRC(components) - ]) + ]); // commit all updated version-related files - await commitVersionBump(version) + await commitVersionBump(version); - console.log(`Bumped to version: ${version}`) + console.log(`Bumped to version: ${version}`); } // get next version for release based on [nightly, beta, stable] @@ -72,81 +72,81 @@ async function nextVersion (bumpType, version) { if (versionUtils.isNightly(version) || versionUtils.isBeta(version)) { switch (bumpType) { case 'nightly': - version = await versionUtils.nextNightly(version) - break + version = await versionUtils.nextNightly(version); + break; case 'beta': - version = await versionUtils.nextBeta(version) - break + version = await versionUtils.nextBeta(version); + break; case 'stable': - version = semver.valid(semver.coerce(version)) - break + version = semver.valid(semver.coerce(version)); + break; default: - throw new Error('Invalid bump type.') + throw new Error('Invalid bump type.'); } } else if (versionUtils.isStable(version)) { switch (bumpType) { case 'nightly': - version = versionUtils.nextNightly(version) - break + version = versionUtils.nextNightly(version); + break; case 'beta': - throw new Error('Cannot bump to beta from stable.') + throw new Error('Cannot bump to beta from stable.'); case 'minor': - version = semver.inc(version, 'minor') - break + version = semver.inc(version, 'minor'); + break; case 'stable': - version = semver.inc(version, 'patch') - break + version = semver.inc(version, 'patch'); + break; default: - throw new Error('Invalid bump type.') + throw new Error('Invalid bump type.'); } } else { - throw new Error(`Invalid current version: ${version}`) + throw new Error(`Invalid current version: ${version}`); } - return version + return version; } // update VERSION file with latest release info async function updateVersion (version) { - const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION') - await writeFile(versionPath, version, 'utf8') + const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION'); + await writeFile(versionPath, version, 'utf8'); } // update package metadata files with new version async function updatePackageJSON (version) { - const filePath = path.resolve(ELECTRON_DIR, 'package.json') - const file = require(filePath) - file.version = version - await writeFile(filePath, JSON.stringify(file, null, 2)) + const filePath = path.resolve(ELECTRON_DIR, 'package.json'); + const file = require(filePath); + file.version = version; + await writeFile(filePath, JSON.stringify(file, null, 2)); } // push bump commit to release branch async function commitVersionBump (version) { - const gitArgs = ['commit', '-a', '-m', `Bump v${version}`, '-n'] - await GitProcess.exec(gitArgs, ELECTRON_DIR) + const gitArgs = ['commit', '-a', '-m', `Bump v${version}`, '-n']; + await GitProcess.exec(gitArgs, ELECTRON_DIR); } // updates atom.rc file with new semver values async function updateWinRC (components) { - const filePath = path.resolve(ELECTRON_DIR, 'shell', 'browser', 'resources', 'win', 'atom.rc') - const data = await readFile(filePath, 'utf8') - const arr = data.split('\n') + const filePath = path.resolve(ELECTRON_DIR, 'shell', 'browser', 'resources', 'win', 'atom.rc'); + const data = await readFile(filePath, 'utf8'); + const arr = data.split('\n'); arr.forEach((line, idx) => { if (line.includes('FILEVERSION')) { - arr[idx] = ` FILEVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}` - arr[idx + 1] = ` PRODUCTVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}` + arr[idx] = ` FILEVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}`; + arr[idx + 1] = ` PRODUCTVERSION ${versionUtils.makeVersion(components, ',', versionUtils.preType.PARTIAL)}`; } else if (line.includes('FileVersion')) { - arr[idx] = ` VALUE "FileVersion", "${versionUtils.makeVersion(components, '.')}"` - arr[idx + 5] = ` VALUE "ProductVersion", "${versionUtils.makeVersion(components, '.')}"` + arr[idx] = ` VALUE "FileVersion", "${versionUtils.makeVersion(components, '.')}"`; + arr[idx + 5] = ` VALUE "ProductVersion", "${versionUtils.makeVersion(components, '.')}"`; } - }) - await writeFile(filePath, arr.join('\n')) + }); + await writeFile(filePath, arr.join('\n')); } if (process.mainModule === module) { main().catch((error) => { - console.error(error) - process.exit(1) - }) + console.error(error); + process.exit(1); + }); } -module.exports = { nextVersion } +module.exports = { nextVersion }; diff --git a/script/release/version-utils.js b/script/release/version-utils.js index 3c4d355544140..356cdfbce11f0 100644 --- a/script/release/version-utils.js +++ b/script/release/version-utils.js @@ -1,90 +1,90 @@ -const path = require('path') -const fs = require('fs') -const semver = require('semver') -const { GitProcess } = require('dugite') -const { promisify } = require('util') +const path = require('path'); +const fs = require('fs'); +const semver = require('semver'); +const { GitProcess } = require('dugite'); +const { promisify } = require('util'); -const { ELECTRON_DIR } = require('../lib/utils') +const { ELECTRON_DIR } = require('../lib/utils'); -const readFile = promisify(fs.readFile) +const readFile = promisify(fs.readFile); const preType = { NONE: 'none', PARTIAL: 'partial', FULL: 'full' -} +}; const getCurrentDate = () => { - const d = new Date() - const dd = `${d.getDate()}`.padStart(2, '0') - const mm = `${d.getMonth() + 1}`.padStart(2, '0') - const yyyy = d.getFullYear() - return `${yyyy}${mm}${dd}` -} - -const isNightly = v => v.includes('nightly') -const isBeta = v => v.includes('beta') + const d = new Date(); + const dd = `${d.getDate()}`.padStart(2, '0'); + const mm = `${d.getMonth() + 1}`.padStart(2, '0'); + const yyyy = d.getFullYear(); + return `${yyyy}${mm}${dd}`; +}; + +const isNightly = v => v.includes('nightly'); +const isBeta = v => v.includes('beta'); const isStable = v => { - const parsed = semver.parse(v) - return !!(parsed && parsed.prerelease.length === 0) -} + const parsed = semver.parse(v); + return !!(parsed && parsed.prerelease.length === 0); +}; const makeVersion = (components, delim, pre = preType.NONE) => { - let version = [components.major, components.minor, components.patch].join(delim) + let version = [components.major, components.minor, components.patch].join(delim); if (pre === preType.PARTIAL) { - version += `${delim}${components.pre[1] || 0}` + version += `${delim}${components.pre[1] || 0}`; } else if (pre === preType.FULL) { - version += `-${components.pre[0]}${delim}${components.pre[1]}` + version += `-${components.pre[0]}${delim}${components.pre[1]}`; } - return version -} + return version; +}; async function nextBeta (v) { - const next = semver.coerce(semver.clean(v)) + const next = semver.coerce(semver.clean(v)); - const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-beta.*`], ELECTRON_DIR) - const tags = tagBlob.stdout.split('\n').filter(e => e !== '') - tags.sort((t1, t2) => semver.gt(t1, t2)) + const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-beta.*`], ELECTRON_DIR); + const tags = tagBlob.stdout.split('\n').filter(e => e !== ''); + tags.sort((t1, t2) => semver.gt(t1, t2)); // increment the latest existing beta tag or start at beta.1 if it's a new beta line - return tags.length === 0 ? `${next}-beta.1` : semver.inc(tags.pop(), 'prerelease') + return tags.length === 0 ? `${next}-beta.1` : semver.inc(tags.pop(), 'prerelease'); } async function getElectronVersion () { - const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION') - const version = await readFile(versionPath, 'utf8') - return version.trim() + const versionPath = path.resolve(ELECTRON_DIR, 'ELECTRON_VERSION'); + const version = await readFile(versionPath, 'utf8'); + return version.trim(); } async function nextNightly (v) { - let next = semver.valid(semver.coerce(v)) - const pre = `nightly.${getCurrentDate()}` + let next = semver.valid(semver.coerce(v)); + const pre = `nightly.${getCurrentDate()}`; - const branch = (await GitProcess.exec(['rev-parse', '--abbrev-ref', 'HEAD'], ELECTRON_DIR)).stdout.trim() + const branch = (await GitProcess.exec(['rev-parse', '--abbrev-ref', 'HEAD'], ELECTRON_DIR)).stdout.trim(); if (branch === 'master') { - next = semver.inc(await getLastMajorForMaster(), 'major') + next = semver.inc(await getLastMajorForMaster(), 'major'); } else if (isStable(v)) { - next = semver.inc(next, 'patch') + next = semver.inc(next, 'patch'); } - return `${next}-${pre}` + return `${next}-${pre}`; } async function getLastMajorForMaster () { - let branchNames - const result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]-[0-9]-x'], ELECTRON_DIR) + let branchNames; + const result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]-[0-9]-x'], ELECTRON_DIR); if (result.exitCode === 0) { - branchNames = result.stdout.trim().split('\n') - const filtered = branchNames.map(b => b.replace('origin/', '')) - return getNextReleaseBranch(filtered) + branchNames = result.stdout.trim().split('\n'); + const filtered = branchNames.map(b => b.replace('origin/', '')); + return getNextReleaseBranch(filtered); } else { - throw new Error('Release branches could not be fetched.') + throw new Error('Release branches could not be fetched.'); } } function getNextReleaseBranch (branches) { - const converted = branches.map(b => b.replace(/-/g, '.').replace('x', '0')) - return converted.reduce((v1, v2) => semver.gt(v1, v2) ? v1 : v2) + const converted = branches.map(b => b.replace(/-/g, '.').replace('x', '0')); + return converted.reduce((v1, v2) => semver.gt(v1, v2) ? v1 : v2); } module.exports = { @@ -96,4 +96,4 @@ module.exports = { getElectronVersion, nextNightly, preType -} +}; diff --git a/script/spec-runner.js b/script/spec-runner.js index 5d9c1e278db6a..8364e3abd3fcd 100755 --- a/script/spec-runner.js +++ b/script/spec-runner.js @@ -1,76 +1,76 @@ #!/usr/bin/env node -const childProcess = require('child_process') -const crypto = require('crypto') -const fs = require('fs') -const { hashElement } = require('folder-hash') -const path = require('path') -const unknownFlags = [] +const childProcess = require('child_process'); +const crypto = require('crypto'); +const fs = require('fs'); +const { hashElement } = require('folder-hash'); +const path = require('path'); +const unknownFlags = []; -require('colors') -const pass = '✓'.green -const fail = '✗'.red +require('colors'); +const pass = '✓'.green; +const fail = '✗'.red; const args = require('minimist')(process.argv, { string: ['runners', 'target'], boolean: ['buildNativeTests'], unknown: arg => unknownFlags.push(arg) -}) +}); -const unknownArgs = [] +const unknownArgs = []; for (const flag of unknownFlags) { - unknownArgs.push(flag) - const onlyFlag = flag.replace(/^-+/, '') + unknownArgs.push(flag); + const onlyFlag = flag.replace(/^-+/, ''); if (args[onlyFlag]) { - unknownArgs.push(args[onlyFlag]) + unknownArgs.push(args[onlyFlag]); } } -const utils = require('./lib/utils') -const { YARN_VERSION } = require('./yarn') +const utils = require('./lib/utils'); +const { YARN_VERSION } = require('./yarn'); -const BASE = path.resolve(__dirname, '../..') -const NPM_CMD = process.platform === 'win32' ? 'npm.cmd' : 'npm' -const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx' +const BASE = path.resolve(__dirname, '../..'); +const NPM_CMD = process.platform === 'win32' ? 'npm.cmd' : 'npm'; +const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx'; const runners = new Map([ ['main', { description: 'Main process specs', run: runMainProcessElectronTests }], ['remote', { description: 'Remote based specs', run: runRemoteBasedElectronTests }], ['native', { description: 'Native specs', run: runNativeElectronTests }] -]) +]); -const specHashPath = path.resolve(__dirname, '../spec/.hash') +const specHashPath = path.resolve(__dirname, '../spec/.hash'); -let runnersToRun = null +let runnersToRun = null; if (args.runners) { - runnersToRun = args.runners.split(',') + runnersToRun = args.runners.split(','); if (!runnersToRun.every(r => [...runners.keys()].includes(r))) { - console.log(`${fail} ${runnersToRun} must be a subset of [${[...runners.keys()].join(' | ')}]`) - process.exit(1) + console.log(`${fail} ${runnersToRun} must be a subset of [${[...runners.keys()].join(' | ')}]`); + process.exit(1); } - console.log('Only running:', runnersToRun) + console.log('Only running:', runnersToRun); } else { - console.log(`Triggering runners: ${[...runners.keys()].join(', ')}`) + console.log(`Triggering runners: ${[...runners.keys()].join(', ')}`); } async function main () { - const [lastSpecHash, lastSpecInstallHash] = loadLastSpecHash() - const [currentSpecHash, currentSpecInstallHash] = await getSpecHash() + const [lastSpecHash, lastSpecInstallHash] = loadLastSpecHash(); + const [currentSpecHash, currentSpecInstallHash] = await getSpecHash(); const somethingChanged = (currentSpecHash !== lastSpecHash) || - (lastSpecInstallHash !== currentSpecInstallHash) + (lastSpecInstallHash !== currentSpecInstallHash); if (somethingChanged) { - await installSpecModules(path.resolve(__dirname, '..', 'spec')) - await installSpecModules(path.resolve(__dirname, '..', 'spec-main')) - await getSpecHash().then(saveSpecHash) + await installSpecModules(path.resolve(__dirname, '..', 'spec')); + await installSpecModules(path.resolve(__dirname, '..', 'spec-main')); + await getSpecHash().then(saveSpecHash); } if (!fs.existsSync(path.resolve(__dirname, '../electron.d.ts'))) { - console.log('Generating electron.d.ts as it is missing') - generateTypeDefinitions() + console.log('Generating electron.d.ts as it is missing'); + generateTypeDefinitions(); } - await runElectronTests() + await runElectronTests(); } function generateTypeDefinitions () { @@ -78,80 +78,80 @@ function generateTypeDefinitions () { cwd: path.resolve(__dirname, '..'), stdio: 'inherit', shell: true - }) + }); if (status !== 0) { - throw new Error(`Electron typescript definition generation failed with exit code: ${status}.`) + throw new Error(`Electron typescript definition generation failed with exit code: ${status}.`); } } function loadLastSpecHash () { return fs.existsSync(specHashPath) ? fs.readFileSync(specHashPath, 'utf8').split('\n') - : [null, null] + : [null, null]; } function saveSpecHash ([newSpecHash, newSpecInstallHash]) { - fs.writeFileSync(specHashPath, `${newSpecHash}\n${newSpecInstallHash}`) + fs.writeFileSync(specHashPath, `${newSpecHash}\n${newSpecInstallHash}`); } async function runElectronTests () { - const errors = [] + const errors = []; - const testResultsDir = process.env.ELECTRON_TEST_RESULTS_DIR + const testResultsDir = process.env.ELECTRON_TEST_RESULTS_DIR; for (const [runnerId, { description, run }] of runners) { if (runnersToRun && !runnersToRun.includes(runnerId)) { - console.info('\nSkipping:', description) - continue + console.info('\nSkipping:', description); + continue; } try { - console.info('\nRunning:', description) + console.info('\nRunning:', description); if (testResultsDir) { - process.env.MOCHA_FILE = path.join(testResultsDir, `test-results-${runnerId}.xml`) + process.env.MOCHA_FILE = path.join(testResultsDir, `test-results-${runnerId}.xml`); } - await run() + await run(); } catch (err) { - errors.push([runnerId, err]) + errors.push([runnerId, err]); } } if (errors.length !== 0) { for (const err of errors) { - console.error('\n\nRunner Failed:', err[0]) - console.error(err[1]) + console.error('\n\nRunner Failed:', err[0]); + console.error(err[1]); } - console.log(`${fail} Electron test runners have failed`) - process.exit(1) + console.log(`${fail} Electron test runners have failed`); + process.exit(1); } } async function runRemoteBasedElectronTests () { - let exe = path.resolve(BASE, utils.getElectronExec()) - const runnerArgs = ['electron/spec', ...unknownArgs.slice(2)] + let exe = path.resolve(BASE, utils.getElectronExec()); + const runnerArgs = ['electron/spec', ...unknownArgs.slice(2)]; if (process.platform === 'linux') { - runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe) - exe = 'python' + runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe); + exe = 'python'; } const { status } = childProcess.spawnSync(exe, runnerArgs, { cwd: path.resolve(__dirname, '../..'), stdio: 'inherit' - }) + }); if (status !== 0) { - const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString() - console.log(`${fail} Electron tests failed with code ${textStatus}.`) - process.exit(1) + const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString(); + console.log(`${fail} Electron tests failed with code ${textStatus}.`); + process.exit(1); } - console.log(`${pass} Electron remote process tests passed.`) + console.log(`${pass} Electron remote process tests passed.`); } async function runNativeElectronTests () { - let testTargets = require('./native-test-targets.json') - const outDir = `out/${utils.getOutDir(false)}` + let testTargets = require('./native-test-targets.json'); + const outDir = `out/${utils.getOutDir(false)}`; // If native tests are being run, only one arg would be relevant if (args.target && !testTargets.includes(args.target)) { - console.log(`${fail} ${args.target} must be a subset of [${[testTargets].join(', ')}]`) - process.exit(1) + console.log(`${fail} ${args.target} must be a subset of [${[testTargets].join(', ')}]`); + process.exit(1); } // Optionally build all native test targets @@ -160,104 +160,104 @@ async function runNativeElectronTests () { const build = childProcess.spawnSync('ninja', ['-C', outDir, target], { cwd: path.resolve(__dirname, '../..'), stdio: 'inherit' - }) + }); // Exit if test target failed to build if (build.status !== 0) { - console.log(`${fail} ${target} failed to build.`) - process.exit(1) + console.log(`${fail} ${target} failed to build.`); + process.exit(1); } } } // If a specific target was passed, only build and run that target - if (args.target) testTargets = [args.target] + if (args.target) testTargets = [args.target]; // Run test targets - const failures = [] + const failures = []; for (const target of testTargets) { - console.info('\nRunning native test for target:', target) + console.info('\nRunning native test for target:', target); const testRun = childProcess.spawnSync(`./${outDir}/${target}`, { cwd: path.resolve(__dirname, '../..'), stdio: 'inherit' - }) + }); // Collect failures and log at end - if (testRun.status !== 0) failures.push({ target }) + if (testRun.status !== 0) failures.push({ target }); } // Exit if any failures if (failures.length > 0) { - console.log(`${fail} Electron native tests failed for the following targets: `, failures) - process.exit(1) + console.log(`${fail} Electron native tests failed for the following targets: `, failures); + process.exit(1); } - console.log(`${pass} Electron native tests passed.`) + console.log(`${pass} Electron native tests passed.`); } async function runMainProcessElectronTests () { - let exe = path.resolve(BASE, utils.getElectronExec()) - const runnerArgs = ['electron/spec-main', ...unknownArgs.slice(2)] + let exe = path.resolve(BASE, utils.getElectronExec()); + const runnerArgs = ['electron/spec-main', ...unknownArgs.slice(2)]; if (process.platform === 'linux') { - runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe) - exe = 'python' + runnerArgs.unshift(path.resolve(__dirname, 'dbus_mock.py'), exe); + exe = 'python'; } const { status } = childProcess.spawnSync(exe, runnerArgs, { cwd: path.resolve(__dirname, '../..'), stdio: 'inherit' - }) + }); if (status !== 0) { - const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : status.toString() - console.log(`${fail} Electron tests failed with code ${textStatus}.`) - process.exit(1) + const textStatus = process.platform === 'win32' ? `0x${status.toString(16)}` : `${status}`; + console.log(`${fail} Electron tests failed with code ${textStatus}.`); + process.exit(1); } - console.log(`${pass} Electron main process tests passed.`) + console.log(`${pass} Electron main process tests passed.`); } async function installSpecModules (dir) { - const nodeDir = path.resolve(BASE, `out/${utils.getOutDir(true)}/gen/node_headers`) + const nodeDir = path.resolve(BASE, `out/${utils.getOutDir(true)}/gen/node_headers`); const env = Object.assign({}, process.env, { npm_config_nodedir: nodeDir, - npm_config_msvs_version: '2017' - }) + npm_config_msvs_version: '2019' + }); const { status } = childProcess.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install', '--frozen-lockfile'], { env, cwd: dir, stdio: 'inherit' - }) + }); if (status !== 0 && !process.env.IGNORE_YARN_INSTALL_ERROR) { - console.log(`${fail} Failed to yarn install in '${dir}'`) - process.exit(1) + console.log(`${fail} Failed to yarn install in '${dir}'`); + process.exit(1); } } function getSpecHash () { return Promise.all([ (async () => { - const hasher = crypto.createHash('SHA256') - hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/package.json'))) - hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec-main/package.json'))) - hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/yarn.lock'))) - hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec-main/yarn.lock'))) - return hasher.digest('hex') + const hasher = crypto.createHash('SHA256'); + hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/package.json'))); + hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec-main/package.json'))); + hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/yarn.lock'))); + hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec-main/yarn.lock'))); + return hasher.digest('hex'); })(), (async () => { - const specNodeModulesPath = path.resolve(__dirname, '../spec/node_modules') + const specNodeModulesPath = path.resolve(__dirname, '../spec/node_modules'); if (!fs.existsSync(specNodeModulesPath)) { - return null + return null; } const { hash } = await hashElement(specNodeModulesPath, { folders: { exclude: ['.bin'] } - }) - return hash + }); + return hash; })() - ]) + ]); } main().catch((error) => { - console.error('An error occurred inside the spec runner:', error) - process.exit(1) -}) + console.error('An error occurred inside the spec runner:', error); + process.exit(1); +}); diff --git a/script/start-goma.ps1 b/script/start-goma.ps1 new file mode 100755 index 0000000000000..d53fc2965cd71 --- /dev/null +++ b/script/start-goma.ps1 @@ -0,0 +1,10 @@ +param([string]$gomaDir=$PWD) +$cmdPath = Join-Path -Path $gomaDir -ChildPath "goma_ctl.py" +Start-Process -FilePath cmd -ArgumentList "/C", "python", "$cmdPath", "ensure_start" +$timedOut = $false; $waitTime = 0; $waitIncrement = 5; $timeout=120; +Do { sleep $waitIncrement; $timedOut = (($waitTime+=$waitIncrement) -gt $timeout); iex "$gomaDir\gomacc.exe port 2" > $null; } Until(($LASTEXITCODE -eq 0) -or $timedOut) +if ($timedOut) { + write-error 'Timed out waiting for goma to start'; exit 1; +} else { + Write-Output "Successfully started goma!" +} diff --git a/script/start.js b/script/start.js index cadba0030c43d..0ba6e56a1fcb1 100644 --- a/script/start.js +++ b/script/start.js @@ -1,16 +1,16 @@ -const cp = require('child_process') -const utils = require('./lib/utils') -const electronPath = utils.getAbsoluteElectronExec() +const cp = require('child_process'); +const utils = require('./lib/utils'); +const electronPath = utils.getAbsoluteElectronExec(); -const child = cp.spawn(electronPath, process.argv.slice(2), { stdio: 'inherit' }) -child.on('close', (code) => process.exit(code)) +const child = cp.spawn(electronPath, process.argv.slice(2), { stdio: 'inherit' }); +child.on('close', (code) => process.exit(code)); const handleTerminationSignal = (signal) => process.on(signal, () => { if (!child.killed) { - child.kill(signal) + child.kill(signal); } - }) + }); -handleTerminationSignal('SIGINT') -handleTerminationSignal('SIGTERM') +handleTerminationSignal('SIGINT'); +handleTerminationSignal('SIGTERM'); diff --git a/script/strip-binaries.py b/script/strip-binaries.py index eda7101fbb3ef..6ffdcaeebbb28 100755 --- a/script/strip-binaries.py +++ b/script/strip-binaries.py @@ -4,21 +4,11 @@ import os import sys +from lib.config import LINUX_BINARIES from lib.util import execute, get_out_dir -LINUX_BINARIES_TO_STRIP = [ - 'electron', - 'chrome-sandbox', - 'libffmpeg.so', - 'libGLESv2.so', - 'libEGL.so', - 'swiftshader/libGLESv2.so', - 'swiftshader/libEGL.so', - 'libvk_swiftshader.so' -] - def strip_binaries(directory, target_cpu): - for binary in LINUX_BINARIES_TO_STRIP: + for binary in LINUX_BINARIES: binary_path = os.path.join(directory, binary) if os.path.isfile(binary_path): strip_binary(binary_path, target_cpu) @@ -32,11 +22,10 @@ def strip_binary(binary_path, target_cpu): strip = 'mips64el-redhat-linux-strip' else: strip = 'strip' - execute([strip, binary_path]) + execute([strip, '--preserve-dates', binary_path]) def main(): args = parse_args() - print(args) if args.file: strip_binary(args.file, args.target_cpu) else: diff --git a/script/verify-mksnapshot.py b/script/verify-mksnapshot.py index bcec6131cfda1..9255ecc5b407b 100755 --- a/script/verify-mksnapshot.py +++ b/script/verify-mksnapshot.py @@ -27,7 +27,7 @@ def main(): if args.snapshot_files_dir is None: mkargs = [ get_binary_path('mksnapshot', app_path), \ SNAPSHOT_SOURCE, '--startup_blob', 'snapshot_blob.bin', \ - '--no-native-code-counters' ] + '--no-native-code-counters', '--turbo_instruction_scheduling' ] subprocess.check_call(mkargs) print('ok mksnapshot successfully created snapshot_blob.bin.') context_snapshot = 'v8_context_snapshot.bin' diff --git a/script/yarn.js b/script/yarn.js index f8a5e94eb164e..7217501d3f241 100644 --- a/script/yarn.js +++ b/script/yarn.js @@ -1,18 +1,18 @@ -const cp = require('child_process') -const fs = require('fs') -const path = require('path') +const cp = require('child_process'); +const fs = require('fs'); +const path = require('path'); -const YARN_VERSION = /'yarn_version': '(.+?)'/.exec(fs.readFileSync(path.resolve(__dirname, '../DEPS'), 'utf8'))[1] +const YARN_VERSION = /'yarn_version': '(.+?)'/.exec(fs.readFileSync(path.resolve(__dirname, '../DEPS'), 'utf8'))[1]; -exports.YARN_VERSION = YARN_VERSION +exports.YARN_VERSION = YARN_VERSION; // If we are running "node script/yarn" run as the yarn CLI if (process.mainModule === module) { - const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx' + const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx'; const child = cp.spawn(NPX_CMD, [`yarn@${YARN_VERSION}`, ...process.argv.slice(2)], { stdio: 'inherit' - }) + }); - child.on('exit', code => process.exit(code)) + child.on('exit', code => process.exit(code)); } diff --git a/script/zip-symbols.py b/script/zip-symbols.py index 1407f195a4962..a97c6f5ff028b 100755 --- a/script/zip-symbols.py +++ b/script/zip-symbols.py @@ -43,6 +43,13 @@ def main(): pdb_zip_file = os.path.join(args.build_dir, pdb_name) print('Making pdb zip: ' + pdb_zip_file) make_zip(pdb_zip_file, pdbs + licenses, []) + elif PLATFORM == 'linux': + debug_name = 'debug.zip' + with scoped_cwd(args.build_dir): + dirs = ['debug'] + debug_zip_file = os.path.join(args.build_dir, debug_name) + print('Making debug zip: ' + debug_zip_file) + make_zip(debug_zip_file, licenses, dirs) def parse_args(): parser = argparse.ArgumentParser(description='Zip symbols') diff --git a/script/zip_manifests/dist_zip.linux.arm.manifest b/script/zip_manifests/dist_zip.linux.arm.manifest index b6c6b70af545c..53463595ee5ec 100644 --- a/script/zip_manifests/dist_zip.linux.arm.manifest +++ b/script/zip_manifests/dist_zip.linux.arm.manifest @@ -62,12 +62,12 @@ locales/uk.pak locales/vi.pak locales/zh-CN.pak locales/zh-TW.pak -natives_blob.bin resources.pak resources/default_app.asar snapshot_blob.bin swiftshader/libEGL.so swiftshader/libGLESv2.so -swiftshader/libvk_swiftshader_icd.json +vk_swiftshader_icd.json +crashpad_handler v8_context_snapshot.bin version diff --git a/script/zip_manifests/dist_zip.linux.arm64.manifest b/script/zip_manifests/dist_zip.linux.arm64.manifest index b6c6b70af545c..53463595ee5ec 100644 --- a/script/zip_manifests/dist_zip.linux.arm64.manifest +++ b/script/zip_manifests/dist_zip.linux.arm64.manifest @@ -62,12 +62,12 @@ locales/uk.pak locales/vi.pak locales/zh-CN.pak locales/zh-TW.pak -natives_blob.bin resources.pak resources/default_app.asar snapshot_blob.bin swiftshader/libEGL.so swiftshader/libGLESv2.so -swiftshader/libvk_swiftshader_icd.json +vk_swiftshader_icd.json +crashpad_handler v8_context_snapshot.bin version diff --git a/script/zip_manifests/dist_zip.linux.x64.manifest b/script/zip_manifests/dist_zip.linux.x64.manifest index b6c6b70af545c..53463595ee5ec 100644 --- a/script/zip_manifests/dist_zip.linux.x64.manifest +++ b/script/zip_manifests/dist_zip.linux.x64.manifest @@ -62,12 +62,12 @@ locales/uk.pak locales/vi.pak locales/zh-CN.pak locales/zh-TW.pak -natives_blob.bin resources.pak resources/default_app.asar snapshot_blob.bin swiftshader/libEGL.so swiftshader/libGLESv2.so -swiftshader/libvk_swiftshader_icd.json +vk_swiftshader_icd.json +crashpad_handler v8_context_snapshot.bin version diff --git a/script/zip_manifests/dist_zip.linux.x86.manifest b/script/zip_manifests/dist_zip.linux.x86.manifest index b6c6b70af545c..53463595ee5ec 100644 --- a/script/zip_manifests/dist_zip.linux.x86.manifest +++ b/script/zip_manifests/dist_zip.linux.x86.manifest @@ -62,12 +62,12 @@ locales/uk.pak locales/vi.pak locales/zh-CN.pak locales/zh-TW.pak -natives_blob.bin resources.pak resources/default_app.asar snapshot_blob.bin swiftshader/libEGL.so swiftshader/libGLESv2.so -swiftshader/libvk_swiftshader_icd.json +vk_swiftshader_icd.json +crashpad_handler v8_context_snapshot.bin version diff --git a/script/zip_manifests/dist_zip.mac.x64.manifest b/script/zip_manifests/dist_zip.mac.x64.manifest index 57030d97fb46f..12bbc905264f1 100644 --- a/script/zip_manifests/dist_zip.mac.x64.manifest +++ b/script/zip_manifests/dist_zip.mac.x64.manifest @@ -87,7 +87,6 @@ Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resourc Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/mr.lproj/locale.pak Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/ms.lproj/ Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/ms.lproj/locale.pak -Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/natives_blob.bin Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/nb.lproj/ Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/nb.lproj/locale.pak Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/nl.lproj/ diff --git a/script/zip_manifests/dist_zip.mac_mas.x64.manifest b/script/zip_manifests/dist_zip.mac_mas.x64.manifest index b382a6d7ef6d0..8c8a921dfe00d 100644 --- a/script/zip_manifests/dist_zip.mac_mas.x64.manifest +++ b/script/zip_manifests/dist_zip.mac_mas.x64.manifest @@ -86,7 +86,6 @@ Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resourc Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/mr.lproj/locale.pak Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/ms.lproj/ Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/ms.lproj/locale.pak -Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/natives_blob.bin Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/nb.lproj/ Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/nb.lproj/locale.pak Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Resources/nl.lproj/ diff --git a/script/zip_manifests/dist_zip.win.arm64.manifest b/script/zip_manifests/dist_zip.win.arm64.manifest index 2938904697a06..2629a9d72599a 100755 --- a/script/zip_manifests/dist_zip.win.arm64.manifest +++ b/script/zip_manifests/dist_zip.win.arm64.manifest @@ -60,13 +60,12 @@ locales/uk.pak locales/vi.pak locales/zh-CN.pak locales/zh-TW.pak -natives_blob.bin resources.pak resources/default_app.asar snapshot_blob.bin swiftshader/libEGL.dll swiftshader/libGLESv2.dll -swiftshader/libvk_swiftshader_icd.json +vk_swiftshader_icd.json vk_swiftshader.dll v8_context_snapshot.bin version diff --git a/script/zip_manifests/dist_zip.win.ia32.manifest b/script/zip_manifests/dist_zip.win.ia32.manifest index cdbc6278049ab..99542c62893ed 100644 --- a/script/zip_manifests/dist_zip.win.ia32.manifest +++ b/script/zip_manifests/dist_zip.win.ia32.manifest @@ -61,13 +61,12 @@ locales/uk.pak locales/vi.pak locales/zh-CN.pak locales/zh-TW.pak -natives_blob.bin resources.pak resources/default_app.asar snapshot_blob.bin swiftshader/libEGL.dll swiftshader/libGLESv2.dll -swiftshader/libvk_swiftshader_icd.json +vk_swiftshader_icd.json vk_swiftshader.dll v8_context_snapshot.bin version diff --git a/script/zip_manifests/dist_zip.win.x64.manifest b/script/zip_manifests/dist_zip.win.x64.manifest index cdbc6278049ab..99542c62893ed 100644 --- a/script/zip_manifests/dist_zip.win.x64.manifest +++ b/script/zip_manifests/dist_zip.win.x64.manifest @@ -61,13 +61,12 @@ locales/uk.pak locales/vi.pak locales/zh-CN.pak locales/zh-TW.pak -natives_blob.bin resources.pak resources/default_app.asar snapshot_blob.bin swiftshader/libEGL.dll swiftshader/libGLESv2.dll -swiftshader/libvk_swiftshader_icd.json +vk_swiftshader_icd.json vk_swiftshader.dll v8_context_snapshot.bin version diff --git a/shell/app/atom_content_client.cc b/shell/app/electron_content_client.cc similarity index 92% rename from shell/app/atom_content_client.cc rename to shell/app/electron_content_client.cc index 359fa171406c5..cf7f2bc640a2e 100644 --- a/shell/app/atom_content_client.cc +++ b/shell/app/electron_content_client.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/app/atom_content_client.h" +#include "shell/app/electron_content_client.h" #include #include @@ -17,13 +17,13 @@ #include "content/public/common/content_constants.h" #include "electron/buildflags/buildflags.h" #include "ppapi/buildflags/buildflags.h" -#include "shell/browser/atom_paths.h" +#include "shell/browser/electron_paths.h" #include "shell/common/options_switches.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "url/url_constants.h" // In SHARED_INTERMEDIATE_DIR. -#include "widevine_cdm_version.h" // NOLINT(build/include) +#include "widevine_cdm_version.h" // NOLINT(build/include_directory) #if defined(WIDEVINE_CDM_AVAILABLE) #include "base/native_library.h" @@ -33,7 +33,7 @@ #if BUILDFLAG(ENABLE_PDF_VIEWER) #include "pdf/pdf.h" -#include "shell/common/atom_constants.h" +#include "shell/common/electron_constants.h" #endif // BUILDFLAG(ENABLE_PDF_VIEWER) #if BUILDFLAG(ENABLE_PLUGINS) @@ -185,33 +185,33 @@ void AppendDelimitedSwitchToVector(const base::StringPiece cmd_switch, } // namespace -AtomContentClient::AtomContentClient() = default; +ElectronContentClient::ElectronContentClient() = default; -AtomContentClient::~AtomContentClient() = default; +ElectronContentClient::~ElectronContentClient() = default; -base::string16 AtomContentClient::GetLocalizedString(int message_id) { +base::string16 ElectronContentClient::GetLocalizedString(int message_id) { return l10n_util::GetStringUTF16(message_id); } -base::StringPiece AtomContentClient::GetDataResource( +base::StringPiece ElectronContentClient::GetDataResource( int resource_id, ui::ScaleFactor scale_factor) { return ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale( resource_id, scale_factor); } -gfx::Image& AtomContentClient::GetNativeImageNamed(int resource_id) { +gfx::Image& ElectronContentClient::GetNativeImageNamed(int resource_id) { return ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed( resource_id); } -base::RefCountedMemory* AtomContentClient::GetDataResourceBytes( +base::RefCountedMemory* ElectronContentClient::GetDataResourceBytes( int resource_id) { return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes( resource_id); } -void AtomContentClient::AddAdditionalSchemes(Schemes* schemes) { +void ElectronContentClient::AddAdditionalSchemes(Schemes* schemes) { AppendDelimitedSwitchToVector(switches::kServiceWorkerSchemes, &schemes->service_worker_schemes); AppendDelimitedSwitchToVector(switches::kStandardSchemes, @@ -227,7 +227,7 @@ void AtomContentClient::AddAdditionalSchemes(Schemes* schemes) { schemes->standard_schemes.emplace_back("chrome-extension"); } -void AtomContentClient::AddPepperPlugins( +void ElectronContentClient::AddPepperPlugins( std::vector* plugins) { #if BUILDFLAG(ENABLE_PEPPER_FLASH) base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); @@ -238,7 +238,7 @@ void AtomContentClient::AddPepperPlugins( #endif // BUILDFLAG(ENABLE_PLUGINS) } -void AtomContentClient::AddContentDecryptionModules( +void ElectronContentClient::AddContentDecryptionModules( std::vector* cdms, std::vector* cdm_host_file_paths) { if (cdms) { diff --git a/shell/app/atom_content_client.h b/shell/app/electron_content_client.h similarity index 75% rename from shell/app/atom_content_client.h rename to shell/app/electron_content_client.h index 92a34c10f771b..0e6eb9715e064 100644 --- a/shell/app/atom_content_client.h +++ b/shell/app/electron_content_client.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_APP_ATOM_CONTENT_CLIENT_H_ -#define SHELL_APP_ATOM_CONTENT_CLIENT_H_ +#ifndef SHELL_APP_ELECTRON_CONTENT_CLIENT_H_ +#define SHELL_APP_ELECTRON_CONTENT_CLIENT_H_ #include #include @@ -14,10 +14,10 @@ namespace electron { -class AtomContentClient : public content::ContentClient { +class ElectronContentClient : public content::ContentClient { public: - AtomContentClient(); - ~AtomContentClient() override; + ElectronContentClient(); + ~ElectronContentClient() override; protected: // content::ContentClient: @@ -33,9 +33,9 @@ class AtomContentClient : public content::ContentClient { std::vector* cdm_host_file_paths) override; private: - DISALLOW_COPY_AND_ASSIGN(AtomContentClient); + DISALLOW_COPY_AND_ASSIGN(ElectronContentClient); }; } // namespace electron -#endif // SHELL_APP_ATOM_CONTENT_CLIENT_H_ +#endif // SHELL_APP_ELECTRON_CONTENT_CLIENT_H_ diff --git a/shell/app/atom_library_main.h b/shell/app/electron_library_main.h similarity index 54% rename from shell/app/atom_library_main.h rename to shell/app/electron_library_main.h index c5d6f6542580b..ffc3eb923547f 100644 --- a/shell/app/atom_library_main.h +++ b/shell/app/electron_library_main.h @@ -2,22 +2,22 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_APP_ATOM_LIBRARY_MAIN_H_ -#define SHELL_APP_ATOM_LIBRARY_MAIN_H_ +#ifndef SHELL_APP_ELECTRON_LIBRARY_MAIN_H_ +#define SHELL_APP_ELECTRON_LIBRARY_MAIN_H_ #include "build/build_config.h" #include "electron/buildflags/buildflags.h" #if defined(OS_MACOSX) extern "C" { -__attribute__((visibility("default"))) int AtomMain(int argc, char* argv[]); +__attribute__((visibility("default"))) int ElectronMain(int argc, char* argv[]); #if BUILDFLAG(ENABLE_RUN_AS_NODE) -__attribute__((visibility("default"))) int AtomInitializeICUandStartNode( +__attribute__((visibility("default"))) int ElectronInitializeICUandStartNode( int argc, char* argv[]); #endif } #endif // OS_MACOSX -#endif // SHELL_APP_ATOM_LIBRARY_MAIN_H_ +#endif // SHELL_APP_ELECTRON_LIBRARY_MAIN_H_ diff --git a/shell/app/atom_library_main.mm b/shell/app/electron_library_main.mm similarity index 74% rename from shell/app/atom_library_main.mm rename to shell/app/electron_library_main.mm index b53e296716ab3..58baed01c7da2 100644 --- a/shell/app/atom_library_main.mm +++ b/shell/app/electron_library_main.mm @@ -2,29 +2,29 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/app/atom_library_main.h" +#include "shell/app/electron_library_main.h" #include "base/at_exit.h" #include "base/i18n/icu_util.h" #include "base/mac/bundle_locations.h" #include "base/mac/scoped_nsautorelease_pool.h" #include "content/public/app/content_main.h" -#include "shell/app/atom_main_delegate.h" +#include "shell/app/electron_main_delegate.h" #include "shell/app/node_main.h" -#include "shell/common/atom_command_line.h" +#include "shell/common/electron_command_line.h" #include "shell/common/mac/main_application_bundle.h" -int AtomMain(int argc, char* argv[]) { - electron::AtomMainDelegate delegate; +int ElectronMain(int argc, char* argv[]) { + electron::ElectronMainDelegate delegate; content::ContentMainParams params(&delegate); params.argc = argc; params.argv = const_cast(argv); - electron::AtomCommandLine::Init(argc, argv); + electron::ElectronCommandLine::Init(argc, argv); return content::ContentMain(params); } #if BUILDFLAG(ENABLE_RUN_AS_NODE) -int AtomInitializeICUandStartNode(int argc, char* argv[]) { +int ElectronInitializeICUandStartNode(int argc, char* argv[]) { base::AtExitManager atexit_manager; base::mac::ScopedNSAutoreleasePool pool; base::mac::SetOverrideFrameworkBundlePath( diff --git a/shell/app/atom_login_helper.mm b/shell/app/electron_login_helper.mm similarity index 100% rename from shell/app/atom_login_helper.mm rename to shell/app/electron_login_helper.mm diff --git a/shell/app/atom_main.cc b/shell/app/electron_main.cc similarity index 91% rename from shell/app/atom_main.cc rename to shell/app/electron_main.cc index e09c23d73339f..0a4af08d8339d 100644 --- a/shell/app/atom_main.cc +++ b/shell/app/electron_main.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/app/atom_main.h" +#include "shell/app/electron_main.h" #include #include @@ -23,27 +23,27 @@ #include "base/win/windows_version.h" #include "content/public/app/sandbox_helper_win.h" #include "sandbox/win/src/sandbox_types.h" -#include "shell/app/atom_main_delegate.h" #include "shell/app/command_line_args.h" +#include "shell/app/electron_main_delegate.h" #include "shell/common/crash_reporter/win/crash_service_main.h" #elif defined(OS_LINUX) // defined(OS_WIN) #include #include #include "content/public/app/content_main.h" -#include "shell/app/atom_main_delegate.h" // NOLINT -#else // defined(OS_LINUX) +#include "shell/app/electron_main_delegate.h" // NOLINT +#else // defined(OS_LINUX) #include #include #include -#include "shell/app/atom_library_main.h" +#include "shell/app/electron_library_main.h" #endif // defined(OS_MACOSX) #include "base/at_exit.h" #include "base/i18n/icu_util.h" #include "electron/buildflags/buildflags.h" #include "shell/app/node_main.h" -#include "shell/common/atom_command_line.h" -#include "shell/common/atom_constants.h" +#include "shell/common/electron_command_line.h" +#include "shell/common/electron_constants.h" #if defined(HELPER_EXECUTABLE) && !defined(MAS_BUILD) #include "sandbox/mac/seatbelt_exec.h" // nogncheck @@ -159,12 +159,12 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) { sandbox::SandboxInterfaceInfo sandbox_info = {0}; content::InitializeSandboxInfo(&sandbox_info); - electron::AtomMainDelegate delegate; + electron::ElectronMainDelegate delegate; content::ContentMainParams params(&delegate); params.instance = instance; params.sandbox_info = &sandbox_info; - electron::AtomCommandLine::Init(arguments.argc, arguments.argv); + electron::ElectronCommandLine::Init(arguments.argc, arguments.argv); return content::ContentMain(params); } @@ -181,11 +181,11 @@ int main(int argc, char* argv[]) { } #endif - electron::AtomMainDelegate delegate; + electron::ElectronMainDelegate delegate; content::ContentMainParams params(&delegate); params.argc = argc; params.argv = const_cast(argv); - electron::AtomCommandLine::Init(argc, argv); + electron::ElectronCommandLine::Init(argc, argv); return content::ContentMain(params); } @@ -196,7 +196,7 @@ int main(int argc, char* argv[]) { #if BUILDFLAG(ENABLE_RUN_AS_NODE) if (IsEnvSet(electron::kRunAsNode)) { - return AtomInitializeICUandStartNode(argc, argv); + return ElectronInitializeICUandStartNode(argc, argv); } #endif @@ -229,7 +229,7 @@ int main(int argc, char* argv[]) { } #endif // defined(HELPER_EXECUTABLE) && !defined(MAS_BUILD) - return AtomMain(argc, argv); + return ElectronMain(argc, argv); } #endif // defined(OS_MACOSX) diff --git a/shell/app/atom_main.h b/shell/app/electron_main.h similarity index 62% rename from shell/app/atom_main.h rename to shell/app/electron_main.h index 611eeec830e27..b49e5576e95f2 100644 --- a/shell/app/atom_main.h +++ b/shell/app/electron_main.h @@ -2,9 +2,9 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_APP_ATOM_MAIN_H_ -#define SHELL_APP_ATOM_MAIN_H_ +#ifndef SHELL_APP_ELECTRON_MAIN_H_ +#define SHELL_APP_ELECTRON_MAIN_H_ #include "content/public/app/content_main.h" -#endif // SHELL_APP_ATOM_MAIN_H_ +#endif // SHELL_APP_ELECTRON_MAIN_H_ diff --git a/shell/app/atom_main_delegate.cc b/shell/app/electron_main_delegate.cc similarity index 78% rename from shell/app/atom_main_delegate.cc rename to shell/app/electron_main_delegate.cc index 9f33fc12e64d3..8dd03530f03f2 100644 --- a/shell/app/atom_main_delegate.cc +++ b/shell/app/electron_main_delegate.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/app/atom_main_delegate.h" +#include "shell/app/electron_main_delegate.h" #include #include @@ -24,21 +24,22 @@ #include "ipc/ipc_buildflags.h" #include "services/service_manager/embedder/switches.h" #include "services/service_manager/sandbox/switches.h" -#include "shell/app/atom_content_client.h" -#include "shell/browser/atom_browser_client.h" -#include "shell/browser/atom_gpu_client.h" +#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h" +#include "shell/app/electron_content_client.h" +#include "shell/browser/electron_browser_client.h" +#include "shell/browser/electron_gpu_client.h" #include "shell/browser/feature_list.h" #include "shell/browser/relauncher.h" #include "shell/common/options_switches.h" -#include "shell/renderer/atom_renderer_client.h" -#include "shell/renderer/atom_sandboxed_renderer_client.h" -#include "shell/utility/atom_content_utility_client.h" +#include "shell/renderer/electron_renderer_client.h" +#include "shell/renderer/electron_sandboxed_renderer_client.h" +#include "shell/utility/electron_content_utility_client.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_switches.h" #if defined(OS_MACOSX) -#include "shell/app/atom_main_delegate_mac.h" +#include "shell/app/electron_main_delegate_mac.h" #endif #if defined(OS_WIN) @@ -126,11 +127,11 @@ void LoadResourceBundle(const std::string& locale) { #endif // BUILDFLAG(ENABLE_PDF_VIEWER) } -AtomMainDelegate::AtomMainDelegate() = default; +ElectronMainDelegate::ElectronMainDelegate() = default; -AtomMainDelegate::~AtomMainDelegate() = default; +ElectronMainDelegate::~ElectronMainDelegate() = default; -bool AtomMainDelegate::BasicStartupComplete(int* exit_code) { +bool ElectronMainDelegate::BasicStartupComplete(int* exit_code) { auto* command_line = base::CommandLine::ForCurrentProcess(); logging::LoggingSettings settings; @@ -182,6 +183,9 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) { if (env->HasVar("ELECTRON_DISABLE_SANDBOX")) command_line->AppendSwitch(service_manager::switches::kNoSandbox); + tracing_sampler_profiler_ = + tracing::TracingSamplerProfiler::CreateOnMainThread(); + chrome::RegisterPathProvider(); #if defined(OS_MACOSX) @@ -209,13 +213,28 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) { << " is not supported. See https://crbug.com/638180."; #endif - content_client_ = std::make_unique(); +#if defined(MAS_BUILD) + // In MAS build we are using --disable-remote-core-animation. + // + // According to ccameron: + // If you're running with --disable-remote-core-animation, you may want to + // also run with --disable-gpu-memory-buffer-compositor-resources as well. + // That flag makes it so we use regular GL textures instead of IOSurfaces + // for compositor resources. IOSurfaces are very heavyweight to + // create/destroy, but they can be displayed directly by CoreAnimation (and + // --disable-remote-core-animation makes it so we don't use this property, + // so they're just heavyweight with no upside). + command_line->AppendSwitch( + ::switches::kDisableGpuMemoryBufferCompositorResources); +#endif + + content_client_ = std::make_unique(); SetContentClient(content_client_.get()); return false; } -void AtomMainDelegate::PostEarlyInitialization(bool is_running_tests) { +void ElectronMainDelegate::PostEarlyInitialization(bool is_running_tests) { std::string custom_locale; ui::ResourceBundle::InitSharedInstanceWithLocale( custom_locale, nullptr, ui::ResourceBundle::LOAD_COMMON_RESOURCES); @@ -223,7 +242,7 @@ void AtomMainDelegate::PostEarlyInitialization(bool is_running_tests) { if (cmd_line->HasSwitch(::switches::kLang)) { const std::string locale = cmd_line->GetSwitchValueASCII(::switches::kLang); const base::FilePath locale_file_path = - ui::ResourceBundle::GetSharedInstance().GetLocaleFilePath(locale, true); + ui::ResourceBundle::GetSharedInstance().GetLocaleFilePath(locale); if (!locale_file_path.empty()) { custom_locale = locale; #if defined(OS_LINUX) @@ -241,11 +260,11 @@ void AtomMainDelegate::PostEarlyInitialization(bool is_running_tests) { LoadResourceBundle(custom_locale); - AtomBrowserClient::SetApplicationLocale( + ElectronBrowserClient::SetApplicationLocale( l10n_util::GetApplicationLocale(custom_locale)); } -void AtomMainDelegate::PreSandboxStartup() { +void ElectronMainDelegate::PreSandboxStartup() { auto* command_line = base::CommandLine::ForCurrentProcess(); std::string process_type = @@ -272,7 +291,7 @@ void AtomMainDelegate::PreSandboxStartup() { #endif } -void AtomMainDelegate::PreCreateMainMessageLoop() { +void ElectronMainDelegate::PreCreateMainMessageLoop() { // This is initialized early because the service manager reads some feature // flags and we need to make sure the feature list is initialized before the // service manager reads the features. @@ -282,35 +301,37 @@ void AtomMainDelegate::PreCreateMainMessageLoop() { #endif } -content::ContentBrowserClient* AtomMainDelegate::CreateContentBrowserClient() { - browser_client_ = std::make_unique(); +content::ContentBrowserClient* +ElectronMainDelegate::CreateContentBrowserClient() { + browser_client_ = std::make_unique(); return browser_client_.get(); } -content::ContentGpuClient* AtomMainDelegate::CreateContentGpuClient() { - gpu_client_ = std::make_unique(); +content::ContentGpuClient* ElectronMainDelegate::CreateContentGpuClient() { + gpu_client_ = std::make_unique(); return gpu_client_.get(); } content::ContentRendererClient* -AtomMainDelegate::CreateContentRendererClient() { +ElectronMainDelegate::CreateContentRendererClient() { auto* command_line = base::CommandLine::ForCurrentProcess(); if (IsSandboxEnabled(command_line)) { - renderer_client_ = std::make_unique(); + renderer_client_ = std::make_unique(); } else { - renderer_client_ = std::make_unique(); + renderer_client_ = std::make_unique(); } return renderer_client_.get(); } -content::ContentUtilityClient* AtomMainDelegate::CreateContentUtilityClient() { - utility_client_ = std::make_unique(); +content::ContentUtilityClient* +ElectronMainDelegate::CreateContentUtilityClient() { + utility_client_ = std::make_unique(); return utility_client_.get(); } -int AtomMainDelegate::RunProcess( +int ElectronMainDelegate::RunProcess( const std::string& process_type, const content::MainFunctionParams& main_function_params) { if (process_type == kRelauncherProcess) @@ -320,17 +341,17 @@ int AtomMainDelegate::RunProcess( } #if defined(OS_MACOSX) -bool AtomMainDelegate::DelaySandboxInitialization( +bool ElectronMainDelegate::DelaySandboxInitialization( const std::string& process_type) { return process_type == kRelauncherProcess; } #endif -bool AtomMainDelegate::ShouldLockSchemeRegistry() { +bool ElectronMainDelegate::ShouldLockSchemeRegistry() { return false; } -bool AtomMainDelegate::ShouldCreateFeatureList() { +bool ElectronMainDelegate::ShouldCreateFeatureList() { return false; } diff --git a/shell/app/atom_main_delegate.h b/shell/app/electron_main_delegate.h similarity index 79% rename from shell/app/atom_main_delegate.h rename to shell/app/electron_main_delegate.h index fa5d8fbc4bc6b..6933477986f0e 100644 --- a/shell/app/atom_main_delegate.h +++ b/shell/app/electron_main_delegate.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_APP_ATOM_MAIN_DELEGATE_H_ -#define SHELL_APP_ATOM_MAIN_DELEGATE_H_ +#ifndef SHELL_APP_ELECTRON_MAIN_DELEGATE_H_ +#define SHELL_APP_ELECTRON_MAIN_DELEGATE_H_ #include #include @@ -11,14 +11,18 @@ #include "content/public/app/content_main_delegate.h" #include "content/public/common/content_client.h" +namespace tracing { +class TracingSamplerProfiler; +} + namespace electron { void LoadResourceBundle(const std::string& locale); -class AtomMainDelegate : public content::ContentMainDelegate { +class ElectronMainDelegate : public content::ContentMainDelegate { public: - AtomMainDelegate(); - ~AtomMainDelegate() override; + ElectronMainDelegate(); + ~ElectronMainDelegate() override; protected: // content::ContentMainDelegate: @@ -51,10 +55,11 @@ class AtomMainDelegate : public content::ContentMainDelegate { std::unique_ptr gpu_client_; std::unique_ptr renderer_client_; std::unique_ptr utility_client_; + std::unique_ptr tracing_sampler_profiler_; - DISALLOW_COPY_AND_ASSIGN(AtomMainDelegate); + DISALLOW_COPY_AND_ASSIGN(ElectronMainDelegate); }; } // namespace electron -#endif // SHELL_APP_ATOM_MAIN_DELEGATE_H_ +#endif // SHELL_APP_ELECTRON_MAIN_DELEGATE_H_ diff --git a/shell/app/atom_main_delegate_mac.h b/shell/app/electron_main_delegate_mac.h similarity index 65% rename from shell/app/atom_main_delegate_mac.h rename to shell/app/electron_main_delegate_mac.h index ba49e2456e040..dc4004608cf23 100644 --- a/shell/app/atom_main_delegate_mac.h +++ b/shell/app/electron_main_delegate_mac.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_APP_ATOM_MAIN_DELEGATE_MAC_H_ -#define SHELL_APP_ATOM_MAIN_DELEGATE_MAC_H_ +#ifndef SHELL_APP_ELECTRON_MAIN_DELEGATE_MAC_H_ +#define SHELL_APP_ELECTRON_MAIN_DELEGATE_MAC_H_ namespace electron { @@ -12,4 +12,4 @@ void RegisterAtomCrApp(); } // namespace electron -#endif // SHELL_APP_ATOM_MAIN_DELEGATE_MAC_H_ +#endif // SHELL_APP_ELECTRON_MAIN_DELEGATE_MAC_H_ diff --git a/shell/app/atom_main_delegate_mac.mm b/shell/app/electron_main_delegate_mac.mm similarity index 91% rename from shell/app/atom_main_delegate_mac.mm rename to shell/app/electron_main_delegate_mac.mm index 48dc62a9ffeed..3805ad76a2d6b 100644 --- a/shell/app/atom_main_delegate_mac.mm +++ b/shell/app/electron_main_delegate_mac.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/app/atom_main_delegate.h" +#include "shell/app/electron_main_delegate.h" #include @@ -15,7 +15,7 @@ #include "base/strings/sys_string_conversions.h" #include "content/common/mac_helpers.h" #include "content/public/common/content_paths.h" -#include "shell/browser/mac/atom_application.h" +#include "shell/browser/mac/electron_application.h" #include "shell/common/application_info.h" #include "shell/common/mac/main_application_bundle.h" @@ -53,12 +53,12 @@ } // namespace -void AtomMainDelegate::OverrideFrameworkBundlePath() { +void ElectronMainDelegate::OverrideFrameworkBundlePath() { base::mac::SetOverrideFrameworkBundlePath( GetFrameworksPath().Append(ELECTRON_PRODUCT_NAME " Framework.framework")); } -void AtomMainDelegate::OverrideChildProcessPath() { +void ElectronMainDelegate::OverrideChildProcessPath() { base::FilePath frameworks_path = GetFrameworksPath(); base::FilePath helper_path = GetHelperAppPath(frameworks_path, ELECTRON_PRODUCT_NAME); @@ -69,7 +69,7 @@ base::PathService::Override(content::CHILD_PROCESS_EXE, helper_path); } -void AtomMainDelegate::SetUpBundleOverrides() { +void ElectronMainDelegate::SetUpBundleOverrides() { base::mac::ScopedNSAutoreleasePool pool; NSBundle* bundle = MainApplicationBundle(); std::string base_bundle_id = diff --git a/shell/app/node_main.cc b/shell/app/node_main.cc index e586e49b84170..e3c0a4407f4f3 100644 --- a/shell/app/node_main.cc +++ b/shell/app/node_main.cc @@ -29,6 +29,17 @@ #include "shell/common/crash_reporter/crash_reporter_win.h" #endif +namespace { + +bool AllowWasmCodeGenerationCallback(v8::Local context, + v8::Local) { + v8::Local wasm_code_gen = context->GetEmbedderData( + node::ContextEmbedderIndex::kAllowWasmCodeGeneration); + return wasm_code_gen->IsUndefined() || wasm_code_gen->IsTrue(); +} + +} // namespace + namespace electron { #if !defined(OS_LINUX) @@ -70,13 +81,13 @@ int NodeMain(int argc, char* argv[]) { gin::V8Initializer::LoadV8Snapshot( gin::V8Initializer::V8SnapshotFileType::kWithAdditionalContext); - gin::V8Initializer::LoadV8Natives(); // V8 requires a task scheduler apparently base::ThreadPoolInstance::CreateAndStartWithDefaultParams("Electron"); // Initialize gin::IsolateHolder. JavascriptEnvironment gin_env(loop); + gin_env.isolate()->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); node::IsolateData* isolate_data = node::CreateIsolateData(gin_env.isolate(), loop, gin_env.platform()); @@ -94,6 +105,10 @@ int NodeMain(int argc, char* argv[]) { node::BootstrapEnvironment(env); gin_helper::Dictionary process(gin_env.isolate(), env->process_object()); + + gin_env.isolate()->SetAllowWasmCodeGenerationCallback( + AllowWasmCodeGenerationCallback); + #if defined(OS_WIN) process.SetMethod("log", &ElectronBindings::Log); #endif diff --git a/shell/app/uv_task_runner.h b/shell/app/uv_task_runner.h index e3abf385f7749..cd7b55005c664 100644 --- a/shell/app/uv_task_runner.h +++ b/shell/app/uv_task_runner.h @@ -10,7 +10,7 @@ #include "base/callback.h" #include "base/location.h" #include "base/single_thread_task_runner.h" -#include "uv.h" // NOLINT(build/include) +#include "uv.h" // NOLINT(build/include_directory) namespace electron { diff --git a/shell/browser/api/atom_api_url_request_ns.cc b/shell/browser/api/atom_api_url_request_ns.cc deleted file mode 100644 index 664a39dc8fedc..0000000000000 --- a/shell/browser/api/atom_api_url_request_ns.cc +++ /dev/null @@ -1,551 +0,0 @@ -// Copyright (c) 2019 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "shell/browser/api/atom_api_url_request_ns.h" - -#include - -#include "gin/handle.h" -#include "mojo/public/cpp/bindings/receiver_set.h" -#include "mojo/public/cpp/system/string_data_source.h" -#include "net/http/http_util.h" -#include "services/network/public/mojom/chunked_data_pipe_getter.mojom.h" -#include "shell/browser/api/atom_api_session.h" -#include "shell/browser/atom_browser_context.h" -#include "shell/common/gin_converters/gurl_converter.h" -#include "shell/common/gin_converters/net_converter.h" -#include "shell/common/gin_helper/dictionary.h" -#include "shell/common/gin_helper/event_emitter_caller.h" -#include "shell/common/gin_helper/object_template_builder.h" - -#include "shell/common/node_includes.h" - -namespace gin { - -template <> -struct Converter { - static bool FromV8(v8::Isolate* isolate, - v8::Local val, - network::mojom::RedirectMode* out) { - std::string mode; - if (!ConvertFromV8(isolate, val, &mode)) - return false; - if (mode == "follow") - *out = network::mojom::RedirectMode::kFollow; - else if (mode == "error") - *out = network::mojom::RedirectMode::kError; - else if (mode == "manual") - *out = network::mojom::RedirectMode::kManual; - else - return false; - return true; - } -}; - -} // namespace gin - -namespace electron { - -namespace api { - -namespace { - -// Network state for request and response. -enum State { - STATE_STARTED = 1 << 0, - STATE_FINISHED = 1 << 1, - STATE_CANCELED = 1 << 2, - STATE_FAILED = 1 << 3, - STATE_CLOSED = 1 << 4, - STATE_ERROR = STATE_CANCELED | STATE_FAILED | STATE_CLOSED, -}; - -// Annotation tag passed to NetworkService. -const net::NetworkTrafficAnnotationTag kTrafficAnnotation = - net::DefineNetworkTrafficAnnotation("electron_net_module", R"( - semantics { - sender: "Electron Net module" - description: - "Issue HTTP/HTTPS requests using Chromium's native networking " - "library." - trigger: "Using the Net module" - data: "Anything the user wants to send." - destination: OTHER - } - policy { - cookies_allowed: YES - cookies_store: "user" - setting: "This feature cannot be disabled." - })"); - -} // namespace - -// Common class for streaming data. -class UploadDataPipeGetter { - public: - explicit UploadDataPipeGetter(URLRequestNS* request) : request_(request) {} - virtual ~UploadDataPipeGetter() = default; - - virtual void AttachToRequestBody(network::ResourceRequestBody* body) = 0; - - protected: - void SetCallback(network::mojom::DataPipeGetter::ReadCallback callback) { - request_->size_callback_ = std::move(callback); - } - - void SetPipe(mojo::ScopedDataPipeProducerHandle pipe) { - request_->producer_ = - std::make_unique(std::move(pipe)); - request_->StartWriting(); - } - - private: - URLRequestNS* request_; - - DISALLOW_COPY_AND_ASSIGN(UploadDataPipeGetter); -}; - -// Streaming multipart data to NetworkService. -class MultipartDataPipeGetter : public UploadDataPipeGetter, - public network::mojom::DataPipeGetter { - public: - explicit MultipartDataPipeGetter(URLRequestNS* request) - : UploadDataPipeGetter(request) {} - ~MultipartDataPipeGetter() override = default; - - void AttachToRequestBody(network::ResourceRequestBody* body) override { - mojo::PendingRemote data_pipe_getter_remote; - receivers_.Add(this, - data_pipe_getter_remote.InitWithNewPipeAndPassReceiver()); - body->AppendDataPipe(std::move(data_pipe_getter_remote)); - } - - private: - // network::mojom::DataPipeGetter: - void Read(mojo::ScopedDataPipeProducerHandle pipe, - ReadCallback callback) override { - SetCallback(std::move(callback)); - SetPipe(std::move(pipe)); - } - - void Clone( - mojo::PendingReceiver receiver) override { - receivers_.Add(this, std::move(receiver)); - } - - mojo::ReceiverSet receivers_; -}; - -// Streaming chunked data to NetworkService. -class ChunkedDataPipeGetter : public UploadDataPipeGetter, - public network::mojom::ChunkedDataPipeGetter { - public: - explicit ChunkedDataPipeGetter(URLRequestNS* request) - : UploadDataPipeGetter(request) {} - ~ChunkedDataPipeGetter() override = default; - - void AttachToRequestBody(network::ResourceRequestBody* body) override { - mojo::PendingRemote - data_pipe_getter_remote; - receiver_set_.Add(this, - data_pipe_getter_remote.InitWithNewPipeAndPassReceiver()); - body->SetToChunkedDataPipe(std::move(data_pipe_getter_remote)); - } - - private: - // network::mojom::ChunkedDataPipeGetter: - void GetSize(GetSizeCallback callback) override { - SetCallback(std::move(callback)); - } - - void StartReading(mojo::ScopedDataPipeProducerHandle pipe) override { - SetPipe(std::move(pipe)); - } - - mojo::ReceiverSet receiver_set_; -}; - -URLRequestNS::URLRequestNS(gin::Arguments* args) : weak_factory_(this) { - request_ = std::make_unique(); - gin_helper::Dictionary dict; - if (args->GetNext(&dict)) { - dict.Get("method", &request_->method); - dict.Get("url", &request_->url); - dict.Get("redirect", &redirect_mode_); - request_->redirect_mode = redirect_mode_; - } - - std::string partition; - mate::Handle session; - if (!dict.Get("session", &session)) { - if (dict.Get("partition", &partition)) - session = Session::FromPartition(args->isolate(), partition); - else // default session - session = Session::FromPartition(args->isolate(), ""); - } - - url_loader_factory_ = session->browser_context()->GetURLLoaderFactory(); - - InitWithArgs(args); -} - -URLRequestNS::~URLRequestNS() = default; - -bool URLRequestNS::NotStarted() const { - return request_state_ == 0; -} - -bool URLRequestNS::Finished() const { - return request_state_ & STATE_FINISHED; -} - -void URLRequestNS::Cancel() { - // Cancel only once. - if (request_state_ & (STATE_CANCELED | STATE_CLOSED)) - return; - - // Mark as canceled. - request_state_ |= STATE_CANCELED; - EmitEvent(EventType::kRequest, true, "abort"); - - if ((response_state_ & STATE_STARTED) && !(response_state_ & STATE_FINISHED)) - EmitEvent(EventType::kResponse, true, "aborted"); - - Close(); -} - -void URLRequestNS::Close() { - if (!(request_state_ & STATE_CLOSED)) { - request_state_ |= STATE_CLOSED; - if (response_state_ & STATE_STARTED) { - // Emit a close event if we really have a response object. - EmitEvent(EventType::kResponse, true, "close"); - } - EmitEvent(EventType::kRequest, true, "close"); - } - Unpin(); - loader_.reset(); -} - -bool URLRequestNS::Write(v8::Local data, bool is_last) { - if (request_state_ & (STATE_FINISHED | STATE_ERROR)) - return false; - - size_t length = node::Buffer::Length(data); - - if (!loader_) { - // Pin on first write. - request_state_ = STATE_STARTED; - Pin(); - - // Create the loader. - network::ResourceRequest* request_ref = request_.get(); - loader_ = network::SimpleURLLoader::Create(std::move(request_), - kTrafficAnnotation); - loader_->SetOnResponseStartedCallback(base::Bind( - &URLRequestNS::OnResponseStarted, weak_factory_.GetWeakPtr())); - loader_->SetOnRedirectCallback( - base::Bind(&URLRequestNS::OnRedirect, weak_factory_.GetWeakPtr())); - loader_->SetOnUploadProgressCallback(base::Bind( - &URLRequestNS::OnUploadProgress, weak_factory_.GetWeakPtr())); - - // Create upload data pipe if we have data to write. - if (length > 0) { - request_ref->request_body = new network::ResourceRequestBody(); - if (is_chunked_upload_) - data_pipe_getter_ = std::make_unique(this); - else - data_pipe_getter_ = std::make_unique(this); - data_pipe_getter_->AttachToRequestBody(request_ref->request_body.get()); - } - - // Start downloading. - loader_->DownloadAsStream(url_loader_factory_.get(), this); - } - - if (length > 0) - pending_writes_.emplace_back(node::Buffer::Data(data), length); - - if (is_last) { - // The ElementsUploadDataStream requires the knowledge of content length - // before doing upload, while Node's stream does not give us any size - // information. So the only option left for us is to keep all the write - // data in memory and flush them after the write is done. - // - // While this looks frustrating, it is actually the behavior of the non- - // NetworkService implementation, and we are not breaking anything. - if (!pending_writes_.empty()) { - last_chunk_written_ = true; - StartWriting(); - } - - request_state_ |= STATE_FINISHED; - EmitEvent(EventType::kRequest, true, "finish"); - } - return true; -} - -void URLRequestNS::FollowRedirect() { - if (request_state_ & (STATE_CANCELED | STATE_CLOSED)) - return; - follow_redirect_ = true; -} - -bool URLRequestNS::SetExtraHeader(const std::string& name, - const std::string& value) { - if (!request_) - return false; - if (!net::HttpUtil::IsValidHeaderName(name)) - return false; - if (!net::HttpUtil::IsValidHeaderValue(value)) - return false; - request_->headers.SetHeader(name, value); - return true; -} - -void URLRequestNS::RemoveExtraHeader(const std::string& name) { - if (request_) - request_->headers.RemoveHeader(name); -} - -void URLRequestNS::SetChunkedUpload(bool is_chunked_upload) { - if (request_) - is_chunked_upload_ = is_chunked_upload; -} - -gin::Dictionary URLRequestNS::GetUploadProgress() { - gin::Dictionary progress = gin::Dictionary::CreateEmpty(isolate()); - if (loader_) { - if (request_) - progress.Set("started", false); - else - progress.Set("started", true); - progress.Set("current", upload_position_); - progress.Set("total", upload_total_); - progress.Set("active", true); - } else { - progress.Set("active", false); - } - return progress; -} - -int URLRequestNS::StatusCode() const { - if (response_headers_) - return response_headers_->response_code(); - return -1; -} - -std::string URLRequestNS::StatusMessage() const { - if (response_headers_) - return response_headers_->GetStatusText(); - return ""; -} - -net::HttpResponseHeaders* URLRequestNS::RawResponseHeaders() const { - return response_headers_.get(); -} - -uint32_t URLRequestNS::ResponseHttpVersionMajor() const { - if (response_headers_) - return response_headers_->GetHttpVersion().major_value(); - return 0; -} - -uint32_t URLRequestNS::ResponseHttpVersionMinor() const { - if (response_headers_) - return response_headers_->GetHttpVersion().minor_value(); - return 0; -} - -void URLRequestNS::OnDataReceived(base::StringPiece data, - base::OnceClosure resume) { - // In case we received an unexpected event from Chromium net, don't emit any - // data event after request cancel/error/close. - if (!(request_state_ & STATE_ERROR) && !(response_state_ & STATE_ERROR)) { - v8::HandleScope handle_scope(isolate()); - v8::Local buffer; - auto maybe = node::Buffer::Copy(isolate(), data.data(), data.size()); - if (maybe.ToLocal(&buffer)) - Emit("data", buffer); - } - std::move(resume).Run(); -} - -void URLRequestNS::OnRetry(base::OnceClosure start_retry) {} - -void URLRequestNS::OnComplete(bool success) { - if (success) { - // In case we received an unexpected event from Chromium net, don't emit any - // data event after request cancel/error/close. - if (!(request_state_ & STATE_ERROR) && !(response_state_ & STATE_ERROR)) { - response_state_ |= STATE_FINISHED; - Emit("end"); - } - } else { // failed - // If response is started then emit response event, else emit request error. - // - // Error is only emitted when there is no previous failure. This is to align - // with the behavior of non-NetworkService implementation. - std::string error = net::ErrorToString(loader_->NetError()); - if (response_state_ & STATE_STARTED) { - if (!(response_state_ & STATE_FAILED)) - EmitError(EventType::kResponse, error); - } else { - if (!(request_state_ & STATE_FAILED)) - EmitError(EventType::kRequest, error); - } - } - - Close(); -} - -void URLRequestNS::OnResponseStarted( - const GURL& final_url, - const network::mojom::URLResponseHead& response_head) { - // Don't emit any event after request cancel. - if (request_state_ & STATE_ERROR) - return; - - response_headers_ = response_head.headers; - response_state_ |= STATE_STARTED; - Emit("response"); -} - -void URLRequestNS::OnRedirect( - const net::RedirectInfo& redirect_info, - const network::mojom::URLResponseHead& response_head, - std::vector* to_be_removed_headers) { - if (!loader_) - return; - - if (request_state_ & (STATE_CLOSED | STATE_CANCELED)) { - NOTREACHED(); - Cancel(); - return; - } - - switch (redirect_mode_) { - case network::mojom::RedirectMode::kError: - Cancel(); - EmitError( - EventType::kRequest, - "Request cannot follow redirect with the current redirect mode"); - break; - case network::mojom::RedirectMode::kManual: - // When redirect mode is "manual", the user has to explicitly call the - // FollowRedirect method to continue redirecting, otherwise the request - // would be cancelled. - // - // Note that the SimpleURLLoader always calls FollowRedirect and does not - // provide a formal way for us to cancel redirection, we have to cancel - // the request to prevent the redirection. - follow_redirect_ = false; - EmitEvent(EventType::kRequest, false, "redirect", - redirect_info.status_code, redirect_info.new_method, - redirect_info.new_url, response_head.headers.get()); - if (!follow_redirect_) - Cancel(); - break; - case network::mojom::RedirectMode::kFollow: - EmitEvent(EventType::kRequest, false, "redirect", - redirect_info.status_code, redirect_info.new_method, - redirect_info.new_url, response_head.headers.get()); - break; - } -} - -void URLRequestNS::OnUploadProgress(uint64_t position, uint64_t total) { - upload_position_ = position; - upload_total_ = total; -} - -void URLRequestNS::OnWrite(MojoResult result) { - if (result != MOJO_RESULT_OK) - return; - - // Continue the pending writes. - pending_writes_.pop_front(); - if (!pending_writes_.empty()) - DoWrite(); -} - -void URLRequestNS::DoWrite() { - DCHECK(producer_); - DCHECK(!pending_writes_.empty()); - producer_->Write( - std::make_unique( - pending_writes_.front(), mojo::StringDataSource::AsyncWritingMode:: - STRING_STAYS_VALID_UNTIL_COMPLETION), - base::BindOnce(&URLRequestNS::OnWrite, weak_factory_.GetWeakPtr())); -} - -void URLRequestNS::StartWriting() { - if (!last_chunk_written_ || size_callback_.is_null()) - return; - - size_t size = 0; - for (const auto& data : pending_writes_) - size += data.size(); - std::move(size_callback_).Run(net::OK, size); - DoWrite(); -} - -void URLRequestNS::Pin() { - if (wrapper_.IsEmpty()) { - wrapper_.Reset(isolate(), GetWrapper()); - } -} - -void URLRequestNS::Unpin() { - wrapper_.Reset(); -} - -void URLRequestNS::EmitError(EventType type, base::StringPiece message) { - if (type == EventType::kRequest) - request_state_ |= STATE_FAILED; - else - response_state_ |= STATE_FAILED; - v8::HandleScope handle_scope(isolate()); - auto error = v8::Exception::Error(gin::StringToV8(isolate(), message)); - EmitEvent(type, false, "error", error); -} - -template -void URLRequestNS::EmitEvent(EventType type, Args... args) { - const char* method = - type == EventType::kRequest ? "_emitRequestEvent" : "_emitResponseEvent"; - v8::HandleScope handle_scope(isolate()); - gin_helper::CustomEmit(isolate(), GetWrapper(), method, args...); -} - -// static -mate::WrappableBase* URLRequestNS::New(gin::Arguments* args) { - return new URLRequestNS(args); -} - -// static -void URLRequestNS::BuildPrototype(v8::Isolate* isolate, - v8::Local prototype) { - prototype->SetClassName(gin::StringToV8(isolate, "URLRequest")); - gin_helper::Destroyable::MakeDestroyable(isolate, prototype); - gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) - .SetMethod("write", &URLRequestNS::Write) - .SetMethod("cancel", &URLRequestNS::Cancel) - .SetMethod("setExtraHeader", &URLRequestNS::SetExtraHeader) - .SetMethod("removeExtraHeader", &URLRequestNS::RemoveExtraHeader) - .SetMethod("setChunkedUpload", &URLRequestNS::SetChunkedUpload) - .SetMethod("followRedirect", &URLRequestNS::FollowRedirect) - .SetMethod("getUploadProgress", &URLRequestNS::GetUploadProgress) - .SetProperty("notStarted", &URLRequestNS::NotStarted) - .SetProperty("finished", &URLRequestNS::Finished) - .SetProperty("statusCode", &URLRequestNS::StatusCode) - .SetProperty("statusMessage", &URLRequestNS::StatusMessage) - .SetProperty("rawResponseHeaders", &URLRequestNS::RawResponseHeaders) - .SetProperty("httpVersionMajor", &URLRequestNS::ResponseHttpVersionMajor) - .SetProperty("httpVersionMinor", &URLRequestNS::ResponseHttpVersionMinor); -} - -} // namespace api - -} // namespace electron diff --git a/shell/browser/api/atom_api_url_request_ns.h b/shell/browser/api/atom_api_url_request_ns.h deleted file mode 100644 index 433f97f27e353..0000000000000 --- a/shell/browser/api/atom_api_url_request_ns.h +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2019 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef SHELL_BROWSER_API_ATOM_API_URL_REQUEST_NS_H_ -#define SHELL_BROWSER_API_ATOM_API_URL_REQUEST_NS_H_ - -#include -#include -#include -#include - -#include "gin/arguments.h" -#include "gin/dictionary.h" -#include "mojo/public/cpp/system/data_pipe_producer.h" -#include "services/network/public/cpp/shared_url_loader_factory.h" -#include "services/network/public/cpp/simple_url_loader.h" -#include "services/network/public/cpp/simple_url_loader_stream_consumer.h" -#include "services/network/public/mojom/data_pipe_getter.mojom.h" -#include "shell/browser/api/event_emitter_deprecated.h" - -namespace electron { - -namespace api { - -class UploadDataPipeGetter; - -class URLRequestNS : public mate::EventEmitter, - public network::SimpleURLLoaderStreamConsumer { - public: - static mate::WrappableBase* New(gin::Arguments* args); - - static void BuildPrototype(v8::Isolate* isolate, - v8::Local prototype); - - protected: - explicit URLRequestNS(gin::Arguments* args); - ~URLRequestNS() override; - - bool NotStarted() const; - bool Finished() const; - - void Cancel(); - void Close(); - - bool Write(v8::Local data, bool is_last); - void FollowRedirect(); - bool SetExtraHeader(const std::string& name, const std::string& value); - void RemoveExtraHeader(const std::string& name); - void SetChunkedUpload(bool is_chunked_upload); - gin::Dictionary GetUploadProgress(); - int StatusCode() const; - std::string StatusMessage() const; - net::HttpResponseHeaders* RawResponseHeaders() const; - uint32_t ResponseHttpVersionMajor() const; - uint32_t ResponseHttpVersionMinor() const; - - // SimpleURLLoaderStreamConsumer: - void OnDataReceived(base::StringPiece string_piece, - base::OnceClosure resume) override; - void OnComplete(bool success) override; - void OnRetry(base::OnceClosure start_retry) override; - - private: - friend class UploadDataPipeGetter; - - void OnResponseStarted(const GURL& final_url, - const network::mojom::URLResponseHead& response_head); - void OnRedirect(const net::RedirectInfo& redirect_info, - const network::mojom::URLResponseHead& response_head, - std::vector* to_be_removed_headers); - void OnUploadProgress(uint64_t position, uint64_t total); - void OnWrite(MojoResult result); - - // Write the first data of |pending_writes_|. - void DoWrite(); - - // Start streaming. - void StartWriting(); - - // Manage lifetime of wrapper. - void Pin(); - void Unpin(); - - // Emit events. - enum class EventType { - kRequest, - kResponse, - }; - void EmitError(EventType type, base::StringPiece error); - template - void EmitEvent(EventType type, Args... args); - - std::unique_ptr request_; - std::unique_ptr loader_; - scoped_refptr url_loader_factory_; - scoped_refptr response_headers_; - - // Redirect mode. - // - // Note that we store it ourselves instead of reading from the one stored in - // |request_|, this is because with multiple redirections, the original one - // might be modified. - network::mojom::RedirectMode redirect_mode_ = - network::mojom::RedirectMode::kFollow; - - // The DataPipeGetter passed to reader. - bool is_chunked_upload_ = false; - std::unique_ptr data_pipe_getter_; - - // Passed from DataPipeGetter for streaming data. - network::mojom::DataPipeGetter::ReadCallback size_callback_; - std::unique_ptr producer_; - - // Whether request.end() has been called. - bool last_chunk_written_ = false; - - // Whether the redirect should be followed. - bool follow_redirect_ = true; - - // Upload progress. - uint64_t upload_position_ = 0; - uint64_t upload_total_ = 0; - - // Current status. - int request_state_ = 0; - int response_state_ = 0; - - // Pending writes that not yet sent to NetworkService. - std::list pending_writes_; - - // Used by pin/unpin to manage lifetime. - v8::Global wrapper_; - - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(URLRequestNS); -}; - -} // namespace api - -} // namespace electron - -#endif // SHELL_BROWSER_API_ATOM_API_URL_REQUEST_NS_H_ diff --git a/shell/browser/api/atom_api_app.cc b/shell/browser/api/electron_api_app.cc similarity index 95% rename from shell/browser/api/atom_api_app.cc rename to shell/browser/api/electron_api_app.cc index 32f80b92ea343..3ad66ebc947ad 100644 --- a/shell/browser/api/atom_api_app.cc +++ b/shell/browser/api/electron_api_app.cc @@ -2,13 +2,14 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_app.h" +#include "shell/browser/api/electron_api_app.h" #include #include #include +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/environment.h" #include "base/files/file_path.h" @@ -34,17 +35,17 @@ #include "net/ssl/client_cert_identity.h" #include "net/ssl/ssl_cert_request_info.h" #include "services/service_manager/sandbox/switches.h" -#include "shell/browser/api/atom_api_menu.h" -#include "shell/browser/api/atom_api_session.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_menu.h" +#include "shell/browser/api/electron_api_session.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/api/gpuinfo_manager.h" -#include "shell/browser/atom_browser_context.h" -#include "shell/browser/atom_browser_main_parts.h" -#include "shell/browser/atom_paths.h" +#include "shell/browser/electron_browser_context.h" +#include "shell/browser/electron_browser_main_parts.h" +#include "shell/browser/electron_paths.h" #include "shell/browser/login_handler.h" #include "shell/browser/relauncher.h" #include "shell/common/application_info.h" -#include "shell/common/atom_command_line.h" +#include "shell/common/electron_command_line.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/native_mate_converters/callback_converter_deprecated.h" #include "shell/common/native_mate_converters/file_path_converter.h" @@ -65,7 +66,7 @@ #if defined(OS_MACOSX) #include -#include "shell/browser/ui/cocoa/atom_bundle_mover.h" +#include "shell/browser/ui/cocoa/electron_bundle_mover.h" #endif using electron::Browser; @@ -498,15 +499,6 @@ void OnClientCertificateSelected( } } -void PassLoginInformation(scoped_refptr login_handler, - mate::Arguments* args) { - base::string16 username, password; - if (args->GetNext(&username) && args->GetNext(&password)) - login_handler->Login(username, password); - else - login_handler->CancelAuth(); -} - #if defined(USE_NSS_CERTS) int ImportIntoCertStore(CertificateManagerModel* model, const base::DictionaryValue& options) { @@ -546,7 +538,8 @@ void OnIconDataAvailable(util::Promise promise, gfx::Image icon) { } // namespace App::App(v8::Isolate* isolate) { - static_cast(AtomBrowserClient::Get())->set_delegate(this); + static_cast(ElectronBrowserClient::Get()) + ->set_delegate(this); Browser::Get()->AddObserver(this); content::GpuDataManager::GetInstance()->AddObserver(this); @@ -559,7 +552,7 @@ App::App(v8::Isolate* isolate) { } App::~App() { - static_cast(AtomBrowserClient::Get()) + static_cast(ElectronBrowserClient::Get()) ->set_delegate(nullptr); Browser::Get()->RemoveObserver(this); content::GpuDataManager::GetInstance()->RemoveObserver(this); @@ -583,7 +576,7 @@ void App::OnWindowAllClosed() { } void App::OnQuit() { - int exitCode = AtomBrowserMainParts::Get()->GetExitCode(); + int exitCode = ElectronBrowserMainParts::Get()->GetExitCode(); Emit("quit", exitCode); if (process_singleton_) { @@ -669,25 +662,6 @@ void App::OnNewWindowForTab() { } #endif -void App::OnLogin(scoped_refptr login_handler, - const base::DictionaryValue& request_details) { - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - bool prevent_default = false; - content::WebContents* web_contents = login_handler->GetWebContents(); - if (web_contents) { - prevent_default = - Emit("login", WebContents::FromOrCreate(isolate(), web_contents), - request_details, *login_handler->auth_info(), - base::BindOnce(&PassLoginInformation, - base::RetainedRef(login_handler))); - } - - // Default behavior is to always cancel the auth. - if (!prevent_default) - login_handler->CancelAuth(); -} - bool App::CanCreateWindow( content::RenderFrameHost* opener, const GURL& opener_url, @@ -727,17 +701,18 @@ void App::AllowCertificateError( const GURL& request_url, bool is_main_frame_request, bool strict_enforcement, - const base::RepeatingCallback& - callback) { + base::OnceCallback callback) { + auto adapted_callback = base::AdaptCallbackForRepeating(std::move(callback)); v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); - bool prevent_default = Emit( - "certificate-error", WebContents::FromOrCreate(isolate(), web_contents), - request_url, net::ErrorToString(cert_error), ssl_info.cert, callback); + bool prevent_default = + Emit("certificate-error", + WebContents::FromOrCreate(isolate(), web_contents), request_url, + net::ErrorToString(cert_error), ssl_info.cert, adapted_callback); // Deny the certificate by default. if (!prevent_default) - callback.Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); + adapted_callback.Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY); } base::OnceClosure App::SelectClientCertificate( @@ -814,7 +789,7 @@ void App::RenderProcessReady(content::RenderProcessHost* host) { // `WebContents` instances, but this was implicitly happening before in // `RenderProcessPreferences`, so this is at least more explicit... content::WebContents* web_contents = - AtomBrowserClient::Get()->GetWebContentsFromProcessID(host->GetID()); + ElectronBrowserClient::Get()->GetWebContentsFromProcessID(host->GetID()); if (web_contents) WebContents::FromOrCreate(v8::Isolate::GetCurrent(), web_contents); } @@ -901,9 +876,17 @@ void App::SetPath(gin_helper::ErrorThrower thrower, bool succeed = false; int key = GetPathConstant(name); - if (key >= 0) + if (key >= 0) { succeed = base::PathService::OverrideAndCreateIfNeeded(key, path, true, false); + if (key == DIR_USER_DATA) { + succeed |= base::PathService::OverrideAndCreateIfNeeded( + chrome::DIR_USER_DATA, path, true, false); + succeed |= base::PathService::Override( + chrome::DIR_APP_DICTIONARIES, + path.Append(base::FilePath::FromUTF8Unsafe("Dictionaries"))); + } + } if (!succeed) thrower.ThrowError("Failed to set path"); } @@ -1014,7 +997,8 @@ bool App::Relaunch(mate::Arguments* js_args) { } if (!override_argv) { - const relauncher::StringVector& argv = electron::AtomCommandLine::argv(); + const relauncher::StringVector& argv = + electron::ElectronCommandLine::argv(); return relauncher::RelaunchApp(argv); } @@ -1087,7 +1071,7 @@ Browser::LoginItemSettings App::GetLoginItemSettings(mate::Arguments* args) { #if defined(USE_NSS_CERTS) void App::ImportCertificate(const base::DictionaryValue& options, net::CompletionRepeatingCallback callback) { - auto browser_context = AtomBrowserContext::From("", false); + auto browser_context = ElectronBrowserContext::From("", false); if (!certificate_manager_model_) { auto copy = base::DictionaryValue::From( base::Value::ToUniquePtrValue(options.Clone())); @@ -1186,7 +1170,7 @@ v8::Local App::GetFileIcon(const base::FilePath& path, icon_size = GetIconSizeByString(icon_size_string); } - auto* icon_manager = AtomBrowserMainParts::Get()->GetIconManager(); + auto* icon_manager = ElectronBrowserMainParts::Get()->GetIconManager(); gfx::Image* icon = icon_manager->LookupIconFromFilepath(normalized_path, icon_size); if (icon) { @@ -1336,29 +1320,32 @@ void App::EnableSandbox(gin_helper::ErrorThrower thrower) { } void App::SetUserAgentFallback(const std::string& user_agent) { - AtomBrowserClient::Get()->SetUserAgent(user_agent); + ElectronBrowserClient::Get()->SetUserAgent(user_agent); } std::string App::GetUserAgentFallback() { - return AtomBrowserClient::Get()->GetUserAgent(); + return ElectronBrowserClient::Get()->GetUserAgent(); } void App::SetBrowserClientCanUseCustomSiteInstance(bool should_disable) { - AtomBrowserClient::Get()->SetCanUseCustomSiteInstance(should_disable); + ElectronBrowserClient::Get()->SetCanUseCustomSiteInstance(should_disable); } bool App::CanBrowserClientUseCustomSiteInstance() { - return AtomBrowserClient::Get()->CanUseCustomSiteInstance(); + return ElectronBrowserClient::Get()->CanUseCustomSiteInstance(); +} +bool App::CanBrowserClientUseCustomSiteInstanceIsDefaultValue() { + return ElectronBrowserClient::Get()->CanUseCustomSiteInstanceIsDefaultValue(); } #if defined(OS_MACOSX) bool App::MoveToApplicationsFolder(gin_helper::ErrorThrower thrower, mate::Arguments* args) { gin::Arguments gin_args(args->info()); - return AtomBundleMover::Move(thrower, &gin_args); + return ElectronBundleMover::Move(thrower, &gin_args); } bool App::IsInApplicationsFolder() { - return AtomBundleMover::IsCurrentAppInApplicationsFolder(); + return ElectronBundleMover::IsCurrentAppInApplicationsFolder(); } int DockBounce(mate::Arguments* args) { @@ -1447,6 +1434,9 @@ void App::BuildPrototype(v8::Isolate* isolate, .SetMethod( "removeAsDefaultProtocolClient", base::BindRepeating(&Browser::RemoveAsDefaultProtocolClient, browser)) + .SetMethod( + "getApplicationNameForProtocol", + base::BindRepeating(&Browser::GetApplicationNameForProtocol, browser)) .SetMethod("_setBadgeCount", base::BindRepeating(&Browser::SetBadgeCount, browser)) .SetMethod("_getBadgeCount", @@ -1538,7 +1528,9 @@ void App::BuildPrototype(v8::Isolate* isolate, .SetMethod("enableSandbox", &App::EnableSandbox) .SetProperty("allowRendererProcessReuse", &App::CanBrowserClientUseCustomSiteInstance, - &App::SetBrowserClientCanUseCustomSiteInstance); + &App::SetBrowserClientCanUseCustomSiteInstance) + .SetProperty("_allowRendererProcessReuseIsDefaultValue", + &App::CanBrowserClientUseCustomSiteInstanceIsDefaultValue); } } // namespace api diff --git a/shell/browser/api/atom_api_app.h b/shell/browser/api/electron_api_app.h similarity index 95% rename from shell/browser/api/atom_api_app.h rename to shell/browser/api/electron_api_app.h index dd4ea2a3be6f2..71aa1a21e1536 100644 --- a/shell/browser/api/atom_api_app.h +++ b/shell/browser/api/electron_api_app.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_APP_H_ -#define SHELL_BROWSER_API_ATOM_API_APP_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_APP_H_ +#define SHELL_BROWSER_API_ELECTRON_API_APP_H_ #include #include @@ -24,9 +24,9 @@ #include "net/ssl/client_cert_identity.h" #include "shell/browser/api/event_emitter_deprecated.h" #include "shell/browser/api/process_metric.h" -#include "shell/browser/atom_browser_client.h" #include "shell/browser/browser.h" #include "shell/browser/browser_observer.h" +#include "shell/browser/electron_browser_client.h" #include "shell/common/gin_helper/error_thrower.h" #include "shell/common/promise_util.h" @@ -50,7 +50,7 @@ enum class JumpListResult : int; namespace api { -class App : public AtomBrowserClient::Delegate, +class App : public ElectronBrowserClient::Delegate, public mate::EventEmitter, public BrowserObserver, public content::GpuDataManagerObserver, @@ -90,8 +90,6 @@ class App : public AtomBrowserClient::Delegate, void OnActivate(bool has_visible_windows) override; void OnWillFinishLaunching() override; void OnFinishLaunching(const base::DictionaryValue& launch_info) override; - void OnLogin(scoped_refptr login_handler, - const base::DictionaryValue& request_details) override; void OnAccessibilitySupportChanged() override; void OnPreMainMessageLoopRun() override; #if defined(OS_MACOSX) @@ -120,8 +118,8 @@ class App : public AtomBrowserClient::Delegate, const GURL& request_url, bool is_main_frame_request, bool strict_enforcement, - const base::RepeatingCallback< - void(content::CertificateRequestResultType)>& callback) override; + base::OnceCallback callback) + override; base::OnceClosure SelectClientCertificate( content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, @@ -205,6 +203,7 @@ class App : public AtomBrowserClient::Delegate, std::string GetUserAgentFallback(); void SetBrowserClientCanUseCustomSiteInstance(bool should_disable); bool CanBrowserClientUseCustomSiteInstance(); + bool CanBrowserClientUseCustomSiteInstanceIsDefaultValue(); #if defined(OS_MACOSX) bool MoveToApplicationsFolder(gin_helper::ErrorThrower, @@ -250,4 +249,4 @@ class App : public AtomBrowserClient::Delegate, } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_APP_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_APP_H_ diff --git a/shell/browser/api/atom_api_app_mac.mm b/shell/browser/api/electron_api_app_mac.mm similarity index 92% rename from shell/browser/api/atom_api_app_mac.mm rename to shell/browser/api/electron_api_app_mac.mm index 98415bc81ca9e..778ce03e57128 100644 --- a/shell/browser/api/atom_api_app_mac.mm +++ b/shell/browser/api/electron_api_app_mac.mm @@ -3,8 +3,8 @@ // found in the LICENSE file. #include "base/path_service.h" -#include "shell/browser/api/atom_api_app.h" -#include "shell/browser/atom_paths.h" +#include "shell/browser/api/electron_api_app.h" +#include "shell/browser/electron_paths.h" #include "shell/common/native_mate_converters/file_path_converter.h" #import diff --git a/shell/browser/api/atom_api_app_mas.mm b/shell/browser/api/electron_api_app_mas.mm similarity index 97% rename from shell/browser/api/atom_api_app_mas.mm rename to shell/browser/api/electron_api_app_mas.mm index 381074fc2415b..aaef369b90987 100644 --- a/shell/browser/api/atom_api_app_mas.mm +++ b/shell/browser/api/electron_api_app_mas.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_app.h" +#include "shell/browser/api/electron_api_app.h" #include diff --git a/shell/browser/api/atom_api_auto_updater.cc b/shell/browser/api/electron_api_auto_updater.cc similarity index 98% rename from shell/browser/api/atom_api_auto_updater.cc rename to shell/browser/api/electron_api_auto_updater.cc index 0d0f78759a37b..6b82edc2de990 100644 --- a/shell/browser/api/atom_api_auto_updater.cc +++ b/shell/browser/api/electron_api_auto_updater.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_auto_updater.h" +#include "shell/browser/api/electron_api_auto_updater.h" #include "base/time/time.h" #include "shell/browser/browser.h" diff --git a/shell/browser/api/atom_api_auto_updater.h b/shell/browser/api/electron_api_auto_updater.h similarity index 90% rename from shell/browser/api/atom_api_auto_updater.h rename to shell/browser/api/electron_api_auto_updater.h index 5664491031438..13221aa58bca9 100644 --- a/shell/browser/api/atom_api_auto_updater.h +++ b/shell/browser/api/electron_api_auto_updater.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_AUTO_UPDATER_H_ -#define SHELL_BROWSER_API_ATOM_API_AUTO_UPDATER_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_AUTO_UPDATER_H_ +#define SHELL_BROWSER_API_ELECTRON_API_AUTO_UPDATER_H_ #include @@ -59,4 +59,4 @@ class AutoUpdater } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_AUTO_UPDATER_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_AUTO_UPDATER_H_ diff --git a/shell/browser/api/atom_api_browser_view.cc b/shell/browser/api/electron_api_browser_view.cc similarity index 98% rename from shell/browser/api/atom_api_browser_view.cc rename to shell/browser/api/electron_api_browser_view.cc index a246092a35f38..0601fd6b007a6 100644 --- a/shell/browser/api/atom_api_browser_view.cc +++ b/shell/browser/api/electron_api_browser_view.cc @@ -2,10 +2,10 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_browser_view.h" +#include "shell/browser/api/electron_api_browser_view.h" #include "native_mate/dictionary.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/browser.h" #include "shell/browser/native_browser_view.h" #include "shell/common/color_util.h" diff --git a/shell/browser/api/atom_api_browser_view.h b/shell/browser/api/electron_api_browser_view.h similarity index 91% rename from shell/browser/api/atom_api_browser_view.h rename to shell/browser/api/electron_api_browser_view.h index 61fc836063403..4b185f6030ee7 100644 --- a/shell/browser/api/atom_api_browser_view.h +++ b/shell/browser/api/electron_api_browser_view.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_BROWSER_VIEW_H_ -#define SHELL_BROWSER_API_ATOM_API_BROWSER_VIEW_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_BROWSER_VIEW_H_ +#define SHELL_BROWSER_API_ELECTRON_API_BROWSER_VIEW_H_ #include #include @@ -71,4 +71,4 @@ class BrowserView : public mate::TrackableObject, } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_BROWSER_VIEW_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_BROWSER_VIEW_H_ diff --git a/shell/browser/api/atom_api_browser_window.cc b/shell/browser/api/electron_api_browser_window.cc similarity index 99% rename from shell/browser/api/atom_api_browser_window.cc rename to shell/browser/api/electron_api_browser_window.cc index 4e85062dc9716..738c097d74ee9 100644 --- a/shell/browser/api/atom_api_browser_window.cc +++ b/shell/browser/api/electron_api_browser_window.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_browser_window.h" +#include "shell/browser/api/electron_api_browser_window.h" #include @@ -242,7 +242,7 @@ void BrowserWindow::OnCloseButtonClicked(bool* prevent_default) { // Already closed by renderer return; - if (web_contents()->NeedToFireBeforeUnload()) + if (web_contents()->NeedToFireBeforeUnloadOrUnload()) web_contents()->DispatchBeforeUnload(false /* auto_cancel */); else web_contents()->Close(); @@ -439,7 +439,7 @@ void BrowserWindow::OnWindowShow() { } void BrowserWindow::OnWindowHide() { - web_contents()->WasHidden(); + web_contents()->WasOccluded(); TopLevelWindow::OnWindowHide(); } diff --git a/shell/browser/api/atom_api_browser_window.h b/shell/browser/api/electron_api_browser_window.h similarity index 93% rename from shell/browser/api/atom_api_browser_window.h rename to shell/browser/api/electron_api_browser_window.h index c0a9900b5973e..fe4299cf9b130 100644 --- a/shell/browser/api/atom_api_browser_window.h +++ b/shell/browser/api/electron_api_browser_window.h @@ -2,16 +2,16 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_BROWSER_WINDOW_H_ -#define SHELL_BROWSER_API_ATOM_API_BROWSER_WINDOW_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_BROWSER_WINDOW_H_ +#define SHELL_BROWSER_API_ELECTRON_API_BROWSER_WINDOW_H_ #include #include #include #include "base/cancelable_callback.h" -#include "shell/browser/api/atom_api_top_level_window.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_top_level_window.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/common/gin_helper/error_thrower.h" namespace electron { @@ -129,4 +129,4 @@ class BrowserWindow : public TopLevelWindow, } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_BROWSER_WINDOW_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_BROWSER_WINDOW_H_ diff --git a/shell/browser/api/atom_api_browser_window_mac.mm b/shell/browser/api/electron_api_browser_window_mac.mm similarity index 94% rename from shell/browser/api/atom_api_browser_window_mac.mm rename to shell/browser/api/electron_api_browser_window_mac.mm index 08dd00ed62f4e..f1c245dad8ed6 100644 --- a/shell/browser/api/atom_api_browser_window_mac.mm +++ b/shell/browser/api/electron_api_browser_window_mac.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_browser_window.h" +#include "shell/browser/api/electron_api_browser_window.h" #include #include @@ -79,6 +79,9 @@ - (NSView*)hitTest:(NSPoint)aPoint { if (window_->has_frame()) return; + if (!web_contents()) + return; + // All ControlRegionViews should be added as children of the WebContentsView, // because WebContentsView will be removed and re-added when entering and // leaving fullscreen mode. @@ -101,9 +104,11 @@ - (NSView*)hitTest:(NSPoint)aPoint { // Draggable regions is implemented by having the whole web view draggable // (mouseDownCanMoveWindow) and overlaying regions that are not draggable. - draggable_regions_.clear(); - for (const auto& r : regions) - draggable_regions_.push_back(r.Clone()); + if (&draggable_regions_ != ®ions) { + draggable_regions_.clear(); + for (const auto& r : regions) + draggable_regions_.push_back(r.Clone()); + } std::vector drag_exclude_rects; if (regions.empty()) { drag_exclude_rects.push_back(gfx::Rect(0, 0, webViewWidth, webViewHeight)); diff --git a/shell/browser/api/atom_api_browser_window_views.cc b/shell/browser/api/electron_api_browser_window_views.cc similarity index 90% rename from shell/browser/api/atom_api_browser_window_views.cc rename to shell/browser/api/electron_api_browser_window_views.cc index be5bbecf293bf..8da8a18fa3028 100644 --- a/shell/browser/api/atom_api_browser_window_views.cc +++ b/shell/browser/api/electron_api_browser_window_views.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_browser_window.h" +#include "shell/browser/api/electron_api_browser_window.h" #include "shell/browser/native_window_views.h" diff --git a/shell/browser/api/atom_api_content_tracing.cc b/shell/browser/api/electron_api_content_tracing.cc similarity index 100% rename from shell/browser/api/atom_api_content_tracing.cc rename to shell/browser/api/electron_api_content_tracing.cc diff --git a/shell/browser/api/atom_api_cookies.cc b/shell/browser/api/electron_api_cookies.cc similarity index 82% rename from shell/browser/api/atom_api_cookies.cc rename to shell/browser/api/electron_api_cookies.cc index fc392b5d230d9..c4ccb30232646 100644 --- a/shell/browser/api/atom_api_cookies.cc +++ b/shell/browser/api/electron_api_cookies.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_cookies.h" +#include "shell/browser/api/electron_api_cookies.h" #include #include @@ -19,8 +19,8 @@ #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_store.h" #include "net/cookies/cookie_util.h" -#include "shell/browser/atom_browser_context.h" #include "shell/browser/cookie_change_notifier.h" +#include "shell/browser/electron_browser_context.h" #include "shell/common/native_mate_converters/gurl_converter.h" #include "shell/common/native_mate_converters/value_converter.h" @@ -48,21 +48,20 @@ struct Converter { }; template <> -struct Converter { - static v8::Local ToV8( - v8::Isolate* isolate, - const network::mojom::CookieChangeCause& val) { +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const net::CookieChangeCause& val) { switch (val) { - case network::mojom::CookieChangeCause::INSERTED: - case network::mojom::CookieChangeCause::EXPLICIT: + case net::CookieChangeCause::INSERTED: + case net::CookieChangeCause::EXPLICIT: return gin::StringToV8(isolate, "explicit"); - case network::mojom::CookieChangeCause::OVERWRITE: + case net::CookieChangeCause::OVERWRITE: return gin::StringToV8(isolate, "overwrite"); - case network::mojom::CookieChangeCause::EXPIRED: + case net::CookieChangeCause::EXPIRED: return gin::StringToV8(isolate, "expired"); - case network::mojom::CookieChangeCause::EVICTED: + case net::CookieChangeCause::EVICTED: return gin::StringToV8(isolate, "evicted"); - case network::mojom::CookieChangeCause::EXPIRED_OVERWRITE: + case net::CookieChangeCause::EXPIRED_OVERWRITE: return gin::StringToV8(isolate, "expired-overwrite"); default: return gin::StringToV8(isolate, "unknown"); @@ -139,6 +138,15 @@ void FilterCookieWithStatuses(const base::Value& filter, net::cookie_util::StripStatuses(list)); } +// Parse dictionary property to CanonicalCookie time correctly. +base::Time ParseTimeProperty(const base::Optional& value) { + if (!value) // empty time means ignoring the parameter + return base::Time(); + if (*value == 0) // FromDoubleT would convert 0 to empty Time + return base::Time::UnixEpoch(); + return base::Time::FromDoubleT(*value); +} + std::string InclusionStatusToString( net::CanonicalCookie::CookieInclusionStatus status) { if (status.HasExclusionReason( @@ -164,7 +172,7 @@ std::string InclusionStatusToString( } // namespace -Cookies::Cookies(v8::Isolate* isolate, AtomBrowserContext* browser_context) +Cookies::Cookies(v8::Isolate* isolate, ElectronBrowserContext* browser_context) : browser_context_(browser_context) { Init(isolate); cookie_change_subscription_ = @@ -241,21 +249,6 @@ v8::Local Cookies::Set(const base::DictionaryValue& details) { const std::string* path = details.FindStringKey("path"); bool secure = details.FindBoolKey("secure").value_or(false); bool http_only = details.FindBoolKey("httpOnly").value_or(false); - base::Optional creation_date = details.FindDoubleKey("creationDate"); - base::Optional expiration_date = - details.FindDoubleKey("expirationDate"); - base::Optional last_access_date = - details.FindDoubleKey("lastAccessDate"); - - base::Time creation_time = creation_date - ? base::Time::FromDoubleT(*creation_date) - : base::Time::UnixEpoch(); - base::Time expiration_time = expiration_date - ? base::Time::FromDoubleT(*expiration_date) - : base::Time::UnixEpoch(); - base::Time last_access_time = last_access_date - ? base::Time::FromDoubleT(*last_access_date) - : base::Time::UnixEpoch(); GURL url(url_string ? *url_string : ""); if (!url.is_valid()) { @@ -266,18 +259,14 @@ v8::Local Cookies::Set(const base::DictionaryValue& details) { return handle; } - if (!name || name->empty()) { - promise.RejectWithErrorMessage( - InclusionStatusToString(net::CanonicalCookie::CookieInclusionStatus( - net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_FAILURE_TO_STORE))); - return handle; - } - auto canonical_cookie = net::CanonicalCookie::CreateSanitizedCookie( - url, *name, value ? *value : "", domain ? *domain : "", path ? *path : "", - creation_time, expiration_time, last_access_time, secure, http_only, - net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT); + url, name ? *name : "", value ? *value : "", domain ? *domain : "", + path ? *path : "", + ParseTimeProperty(details.FindDoubleKey("creationDate")), + ParseTimeProperty(details.FindDoubleKey("expirationDate")), + ParseTimeProperty(details.FindDoubleKey("lastAccessDate")), secure, + http_only, net::CookieSameSite::NO_RESTRICTION, + net::COOKIE_PRIORITY_DEFAULT); if (!canonical_cookie || !canonical_cookie->IsCanonical()) { promise.RejectWithErrorMessage( InclusionStatusToString(net::CanonicalCookie::CookieInclusionStatus( @@ -323,15 +312,16 @@ v8::Local Cookies::FlushStore() { return handle; } -void Cookies::OnCookieChanged(const CookieDetails* details) { - Emit("changed", gin::ConvertToV8(isolate(), *(details->cookie)), - gin::ConvertToV8(isolate(), details->cause), - gin::ConvertToV8(isolate(), details->removed)); +void Cookies::OnCookieChanged(const net::CookieChangeInfo& change) { + Emit("changed", gin::ConvertToV8(isolate(), change.cookie), + gin::ConvertToV8(isolate(), change.cause), + gin::ConvertToV8(isolate(), + change.cause != net::CookieChangeCause::INSERTED)); } // static gin::Handle Cookies::Create(v8::Isolate* isolate, - AtomBrowserContext* browser_context) { + ElectronBrowserContext* browser_context) { return gin::CreateHandle(isolate, new Cookies(isolate, browser_context)); } diff --git a/shell/browser/api/atom_api_cookies.h b/shell/browser/api/electron_api_cookies.h similarity index 67% rename from shell/browser/api/atom_api_cookies.h rename to shell/browser/api/electron_api_cookies.h index eba0fb6229069..67a8ed0772cf9 100644 --- a/shell/browser/api/atom_api_cookies.h +++ b/shell/browser/api/electron_api_cookies.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_COOKIES_H_ -#define SHELL_BROWSER_API_ATOM_API_COOKIES_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_COOKIES_H_ +#define SHELL_BROWSER_API_ELECTRON_API_COOKIES_H_ #include #include @@ -11,8 +11,8 @@ #include "base/callback_list.h" #include "gin/handle.h" #include "net/cookies/canonical_cookie.h" +#include "net/cookies/cookie_change_dispatcher.h" #include "shell/browser/api/trackable_object.h" -#include "shell/browser/net/cookie_details.h" #include "shell/common/promise_util.h" namespace base { @@ -29,21 +29,21 @@ class URLRequestContextGetter; namespace electron { -class AtomBrowserContext; +class ElectronBrowserContext; namespace api { class Cookies : public mate::TrackableObject { public: static gin::Handle Create(v8::Isolate* isolate, - AtomBrowserContext* browser_context); + ElectronBrowserContext* browser_context); // mate::TrackableObject: static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); protected: - Cookies(v8::Isolate* isolate, AtomBrowserContext* browser_context); + Cookies(v8::Isolate* isolate, ElectronBrowserContext* browser_context); ~Cookies() override; v8::Local Get(const mate::Dictionary& filter); @@ -52,12 +52,13 @@ class Cookies : public mate::TrackableObject { v8::Local FlushStore(); // CookieChangeNotifier subscription: - void OnCookieChanged(const CookieDetails*); + void OnCookieChanged(const net::CookieChangeInfo& change); private: - std::unique_ptr::Subscription> + std::unique_ptr::Subscription> cookie_change_subscription_; - scoped_refptr browser_context_; + scoped_refptr browser_context_; DISALLOW_COPY_AND_ASSIGN(Cookies); }; @@ -66,4 +67,4 @@ class Cookies : public mate::TrackableObject { } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_COOKIES_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_COOKIES_H_ diff --git a/shell/browser/api/atom_api_data_pipe_holder.cc b/shell/browser/api/electron_api_data_pipe_holder.cc similarity index 98% rename from shell/browser/api/atom_api_data_pipe_holder.cc rename to shell/browser/api/electron_api_data_pipe_holder.cc index b26783a7a0b3d..2bca7940402c6 100644 --- a/shell/browser/api/atom_api_data_pipe_holder.cc +++ b/shell/browser/api/electron_api_data_pipe_holder.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_data_pipe_holder.h" +#include "shell/browser/api/electron_api_data_pipe_holder.h" #include #include diff --git a/shell/browser/api/atom_api_data_pipe_holder.h b/shell/browser/api/electron_api_data_pipe_holder.h similarity index 88% rename from shell/browser/api/atom_api_data_pipe_holder.h rename to shell/browser/api/electron_api_data_pipe_holder.h index 877753ca84ac9..0991f659d4e9d 100644 --- a/shell/browser/api/atom_api_data_pipe_holder.h +++ b/shell/browser/api/electron_api_data_pipe_holder.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_DATA_PIPE_HOLDER_H_ -#define SHELL_BROWSER_API_ATOM_API_DATA_PIPE_HOLDER_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_DATA_PIPE_HOLDER_H_ +#define SHELL_BROWSER_API_ELECTRON_API_DATA_PIPE_HOLDER_H_ #include @@ -51,4 +51,4 @@ class DataPipeHolder : public gin::Wrappable { } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_DATA_PIPE_HOLDER_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_DATA_PIPE_HOLDER_H_ diff --git a/shell/browser/api/atom_api_debugger.cc b/shell/browser/api/electron_api_debugger.cc similarity index 99% rename from shell/browser/api/atom_api_debugger.cc rename to shell/browser/api/electron_api_debugger.cc index 083342802bc33..e51beae287470 100644 --- a/shell/browser/api/atom_api_debugger.cc +++ b/shell/browser/api/electron_api_debugger.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_debugger.h" +#include "shell/browser/api/electron_api_debugger.h" #include #include diff --git a/shell/browser/api/atom_api_debugger.h b/shell/browser/api/electron_api_debugger.h similarity index 93% rename from shell/browser/api/atom_api_debugger.h rename to shell/browser/api/electron_api_debugger.h index 4bf882ea2c0d5..86cc8e2c89d29 100644 --- a/shell/browser/api/atom_api_debugger.h +++ b/shell/browser/api/electron_api_debugger.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_DEBUGGER_H_ -#define SHELL_BROWSER_API_ATOM_API_DEBUGGER_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_DEBUGGER_H_ +#define SHELL_BROWSER_API_ELECTRON_API_DEBUGGER_H_ #include #include @@ -76,4 +76,4 @@ class Debugger : public mate::TrackableObject, } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_DEBUGGER_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_DEBUGGER_H_ diff --git a/shell/browser/api/atom_api_desktop_capturer.cc b/shell/browser/api/electron_api_desktop_capturer.cc similarity index 96% rename from shell/browser/api/atom_api_desktop_capturer.cc rename to shell/browser/api/electron_api_desktop_capturer.cc index d456071de9266..6e6021b7d9a37 100644 --- a/shell/browser/api/atom_api_desktop_capturer.cc +++ b/shell/browser/api/electron_api_desktop_capturer.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_desktop_capturer.h" +#include "shell/browser/api/electron_api_desktop_capturer.h" #include #include @@ -14,7 +14,7 @@ #include "chrome/browser/media/webrtc/desktop_media_list.h" #include "chrome/browser/media/webrtc/window_icon_util.h" #include "content/public/browser/desktop_capture.h" -#include "shell/common/api/atom_api_native_image.h" +#include "shell/common/api/electron_api_native_image.h" #include "shell/common/gin_converters/gfx_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/object_template_builder.h" @@ -95,6 +95,7 @@ void DesktopCapturer::StartHandling(bool capture_window, content::DesktopMediaID::TYPE_WINDOW, content::desktop_capture::CreateWindowCapturer()); window_capturer_->SetThumbnailSize(thumbnail_size); + window_capturer_->AddObserver(this); window_capturer_->Update(base::BindOnce( &DesktopCapturer::UpdateSourcesList, weak_ptr_factory_.GetWeakPtr(), window_capturer_.get())); @@ -105,6 +106,7 @@ void DesktopCapturer::StartHandling(bool capture_window, content::DesktopMediaID::TYPE_SCREEN, content::desktop_capture::CreateScreenCapturer()); screen_capturer_->SetThumbnailSize(thumbnail_size); + screen_capturer_->AddObserver(this); screen_capturer_->Update(base::BindOnce( &DesktopCapturer::UpdateSourcesList, weak_ptr_factory_.GetWeakPtr(), screen_capturer_.get())); @@ -112,6 +114,10 @@ void DesktopCapturer::StartHandling(bool capture_window, } } +void DesktopCapturer::OnSourceUnchanged(DesktopMediaList* list) { + UpdateSourcesList(list); +} + void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) { if (capture_window_ && list->GetMediaListType() == content::DesktopMediaID::TYPE_WINDOW) { diff --git a/shell/browser/api/atom_api_desktop_capturer.h b/shell/browser/api/electron_api_desktop_capturer.h similarity index 72% rename from shell/browser/api/atom_api_desktop_capturer.h rename to shell/browser/api/electron_api_desktop_capturer.h index 77360c4bc7545..720634fe791a6 100644 --- a/shell/browser/api/atom_api_desktop_capturer.h +++ b/shell/browser/api/electron_api_desktop_capturer.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_H_ -#define SHELL_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_DESKTOP_CAPTURER_H_ +#define SHELL_BROWSER_API_ELECTRON_API_DESKTOP_CAPTURER_H_ #include #include @@ -22,7 +22,8 @@ namespace api { class DesktopCapturer : public mate::TrackableObject< DesktopCapturer, - gin_helper::EventEmitter>> { + gin_helper::EventEmitter>>, + public DesktopMediaListObserver { public: struct Source { DesktopMediaList::Source media_list_source; @@ -47,6 +48,16 @@ class DesktopCapturer explicit DesktopCapturer(v8::Isolate* isolate); ~DesktopCapturer() override; + // DesktopMediaListObserver: + void OnSourceAdded(DesktopMediaList* list, int index) override {} + void OnSourceRemoved(DesktopMediaList* list, int index) override {} + void OnSourceMoved(DesktopMediaList* list, + int old_index, + int new_index) override {} + void OnSourceNameChanged(DesktopMediaList* list, int index) override {} + void OnSourceThumbnailChanged(DesktopMediaList* list, int index) override {} + void OnSourceUnchanged(DesktopMediaList* list) override; + private: void UpdateSourcesList(DesktopMediaList* list); @@ -69,4 +80,4 @@ class DesktopCapturer } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_DESKTOP_CAPTURER_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_DESKTOP_CAPTURER_H_ diff --git a/shell/browser/api/atom_api_dialog.cc b/shell/browser/api/electron_api_dialog.cc similarity index 100% rename from shell/browser/api/atom_api_dialog.cc rename to shell/browser/api/electron_api_dialog.cc diff --git a/shell/browser/api/atom_api_download_item.cc b/shell/browser/api/electron_api_download_item.cc similarity index 98% rename from shell/browser/api/atom_api_download_item.cc rename to shell/browser/api/electron_api_download_item.cc index 7a2649013b063..a8102bbb90ec6 100644 --- a/shell/browser/api/atom_api_download_item.cc +++ b/shell/browser/api/electron_api_download_item.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_download_item.h" +#include "shell/browser/api/electron_api_download_item.h" #include @@ -10,7 +10,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "native_mate/dictionary.h" #include "net/base/filename_util.h" -#include "shell/browser/atom_browser_main_parts.h" +#include "shell/browser/electron_browser_main_parts.h" #include "shell/common/native_mate_converters/file_dialog_converter.h" #include "shell/common/native_mate_converters/file_path_converter.h" #include "shell/common/native_mate_converters/gurl_converter.h" diff --git a/shell/browser/api/atom_api_download_item.h b/shell/browser/api/electron_api_download_item.h similarity index 92% rename from shell/browser/api/atom_api_download_item.h rename to shell/browser/api/electron_api_download_item.h index e2310e566645c..eae7a9ca453d7 100644 --- a/shell/browser/api/atom_api_download_item.h +++ b/shell/browser/api/electron_api_download_item.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_DOWNLOAD_ITEM_H_ -#define SHELL_BROWSER_API_ATOM_API_DOWNLOAD_ITEM_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_DOWNLOAD_ITEM_H_ +#define SHELL_BROWSER_API_ELECTRON_API_DOWNLOAD_ITEM_H_ #include #include @@ -71,4 +71,4 @@ class DownloadItem : public mate::TrackableObject, } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_DOWNLOAD_ITEM_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_DOWNLOAD_ITEM_H_ diff --git a/shell/browser/api/atom_api_event.cc b/shell/browser/api/electron_api_event.cc similarity index 100% rename from shell/browser/api/atom_api_event.cc rename to shell/browser/api/electron_api_event.cc diff --git a/shell/browser/api/atom_api_global_shortcut.cc b/shell/browser/api/electron_api_global_shortcut.cc similarity index 91% rename from shell/browser/api/atom_api_global_shortcut.cc rename to shell/browser/api/electron_api_global_shortcut.cc index 09e5f1c387eee..15e4453c7fd03 100644 --- a/shell/browser/api/atom_api_global_shortcut.cc +++ b/shell/browser/api/electron_api_global_shortcut.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_global_shortcut.h" +#include "shell/browser/api/electron_api_global_shortcut.h" #include #include @@ -10,7 +10,7 @@ #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "native_mate/dictionary.h" -#include "shell/browser/api/atom_api_system_preferences.h" +#include "shell/browser/api/electron_api_system_preferences.h" #include "shell/common/native_mate_converters/accelerator_converter.h" #include "shell/common/native_mate_converters/callback_converter_deprecated.h" #include "shell/common/node_includes.h" @@ -73,19 +73,13 @@ bool GlobalShortcut::RegisterAll( std::vector registered; for (auto& accelerator : accelerators) { -#if defined(OS_MACOSX) - if (RegisteringMediaKeyForUntrustedClient(accelerator)) - return false; - - GlobalShortcutListener* listener = GlobalShortcutListener::GetInstance(); - if (!listener->RegisterAccelerator(accelerator, this)) { + if (!Register(accelerator, callback)) { // unregister all shortcuts if any failed UnregisterSome(registered); return false; } -#endif + registered.push_back(accelerator); - accelerator_callback_map_[accelerator] = callback; } return true; } diff --git a/shell/browser/api/atom_api_global_shortcut.h b/shell/browser/api/electron_api_global_shortcut.h similarity index 90% rename from shell/browser/api/atom_api_global_shortcut.h rename to shell/browser/api/electron_api_global_shortcut.h index 456a554524c09..f417c8f2627d6 100644 --- a/shell/browser/api/atom_api_global_shortcut.h +++ b/shell/browser/api/electron_api_global_shortcut.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_GLOBAL_SHORTCUT_H_ -#define SHELL_BROWSER_API_ATOM_API_GLOBAL_SHORTCUT_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_GLOBAL_SHORTCUT_H_ +#define SHELL_BROWSER_API_ELECTRON_API_GLOBAL_SHORTCUT_H_ #include #include @@ -55,4 +55,4 @@ class GlobalShortcut : public extensions::GlobalShortcutListener::Observer, } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_GLOBAL_SHORTCUT_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_GLOBAL_SHORTCUT_H_ diff --git a/shell/browser/api/atom_api_in_app_purchase.cc b/shell/browser/api/electron_api_in_app_purchase.cc similarity index 98% rename from shell/browser/api/atom_api_in_app_purchase.cc rename to shell/browser/api/electron_api_in_app_purchase.cc index ccd5e5e397932..5934de74b0003 100644 --- a/shell/browser/api/atom_api_in_app_purchase.cc +++ b/shell/browser/api/electron_api_in_app_purchase.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_in_app_purchase.h" +#include "shell/browser/api/electron_api_in_app_purchase.h" #include #include diff --git a/shell/browser/api/atom_api_in_app_purchase.h b/shell/browser/api/electron_api_in_app_purchase.h similarity index 89% rename from shell/browser/api/atom_api_in_app_purchase.h rename to shell/browser/api/electron_api_in_app_purchase.h index 0226f0ae761f5..f37d73d83f69b 100644 --- a/shell/browser/api/atom_api_in_app_purchase.h +++ b/shell/browser/api/electron_api_in_app_purchase.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_IN_APP_PURCHASE_H_ -#define SHELL_BROWSER_API_ATOM_API_IN_APP_PURCHASE_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_IN_APP_PURCHASE_H_ +#define SHELL_BROWSER_API_ELECTRON_API_IN_APP_PURCHASE_H_ #include #include @@ -51,4 +51,4 @@ class InAppPurchase } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_IN_APP_PURCHASE_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_IN_APP_PURCHASE_H_ diff --git a/shell/browser/api/atom_api_menu.cc b/shell/browser/api/electron_api_menu.cc similarity index 94% rename from shell/browser/api/atom_api_menu.cc rename to shell/browser/api/electron_api_menu.cc index d28439eabcaa2..7b86e5499fbba 100644 --- a/shell/browser/api/atom_api_menu.cc +++ b/shell/browser/api/electron_api_menu.cc @@ -2,9 +2,10 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_menu.h" +#include "shell/browser/api/electron_api_menu.h" #include +#include #include "native_mate/constructor.h" #include "shell/browser/native_window.h" @@ -28,7 +29,7 @@ namespace electron { namespace api { -Menu::Menu(gin::Arguments* args) : model_(new AtomMenuModel(this)) { +Menu::Menu(gin::Arguments* args) : model_(new ElectronMenuModel(this)) { InitWithArgs(args); model_->AddObserver(this); } @@ -218,6 +219,16 @@ void Menu::OnMenuWillShow() { Emit("menu-will-show"); } +base::OnceClosure Menu::BindSelfToClosure(base::OnceClosure callback) { + // return ((callback, ref) => { callback() }).bind(null, callback, this) + v8::Global ref(isolate(), GetWrapper()); + return base::BindOnce( + [](base::OnceClosure callback, v8::Global ref) { + std::move(callback).Run(); + }, + std::move(callback), std::move(ref)); +} + // static void Menu::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { diff --git a/shell/browser/api/atom_api_menu.h b/shell/browser/api/electron_api_menu.h similarity index 84% rename from shell/browser/api/atom_api_menu.h rename to shell/browser/api/electron_api_menu.h index b67eb925f19bb..90e0cf529f453 100644 --- a/shell/browser/api/atom_api_menu.h +++ b/shell/browser/api/electron_api_menu.h @@ -2,25 +2,25 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_MENU_H_ -#define SHELL_BROWSER_API_ATOM_API_MENU_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_MENU_H_ +#define SHELL_BROWSER_API_ELECTRON_API_MENU_H_ #include #include #include "base/callback.h" #include "gin/arguments.h" -#include "shell/browser/api/atom_api_top_level_window.h" +#include "shell/browser/api/electron_api_top_level_window.h" #include "shell/browser/api/trackable_object.h" -#include "shell/browser/ui/atom_menu_model.h" +#include "shell/browser/ui/electron_menu_model.h" namespace electron { namespace api { class Menu : public mate::TrackableObject, - public AtomMenuModel::Delegate, - public AtomMenuModel::Observer { + public ElectronMenuModel::Delegate, + public ElectronMenuModel::Observer { public: static mate::WrappableBase* New(gin::Arguments* args); @@ -35,7 +35,7 @@ class Menu : public mate::TrackableObject, static void SendActionToFirstResponder(const std::string& action); #endif - AtomMenuModel* model() const { return model_.get(); } + ElectronMenuModel* model() const { return model_.get(); } protected: explicit Menu(gin::Arguments* args); @@ -61,16 +61,21 @@ class Menu : public mate::TrackableObject, int x, int y, int positioning_item, - const base::Closure& callback) = 0; + base::OnceClosure callback) = 0; virtual void ClosePopupAt(int32_t window_id) = 0; - std::unique_ptr model_; + std::unique_ptr model_; Menu* parent_ = nullptr; // Observable: void OnMenuWillClose() override; void OnMenuWillShow() override; + protected: + // Returns a new callback which keeps references of the JS wrapper until the + // passed |callback| is called. + base::OnceClosure BindSelfToClosure(base::OnceClosure callback); + private: void InsertItemAt(int index, int command_id, const base::string16& label); void InsertSeparatorAt(int index); @@ -125,10 +130,10 @@ class Menu : public mate::TrackableObject, namespace mate { template <> -struct Converter { +struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Local val, - electron::AtomMenuModel** out) { + electron::ElectronMenuModel** out) { // null would be tranfered to NULL. if (val->IsNull()) { *out = nullptr; @@ -145,4 +150,4 @@ struct Converter { } // namespace mate -#endif // SHELL_BROWSER_API_ATOM_API_MENU_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_MENU_H_ diff --git a/shell/browser/api/atom_api_menu_mac.h b/shell/browser/api/electron_api_menu_mac.h similarity index 62% rename from shell/browser/api/atom_api_menu_mac.h rename to shell/browser/api/electron_api_menu_mac.h index db7d9f4d27843..3b3c066c5621e 100644 --- a/shell/browser/api/atom_api_menu_mac.h +++ b/shell/browser/api/electron_api_menu_mac.h @@ -2,15 +2,15 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_MENU_MAC_H_ -#define SHELL_BROWSER_API_ATOM_API_MENU_MAC_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_MENU_MAC_H_ +#define SHELL_BROWSER_API_ELECTRON_API_MENU_MAC_H_ -#include "shell/browser/api/atom_api_menu.h" +#include "shell/browser/api/electron_api_menu.h" #include #include -#import "shell/browser/ui/cocoa/atom_menu_controller.h" +#import "shell/browser/ui/cocoa/electron_menu_controller.h" using base::scoped_nsobject; @@ -27,25 +27,25 @@ class MenuMac : public Menu { int x, int y, int positioning_item, - const base::Closure& callback) override; + base::OnceClosure callback) override; void PopupOnUI(const base::WeakPtr& native_window, int32_t window_id, int x, int y, int positioning_item, - base::Closure callback); + base::OnceClosure callback); void ClosePopupAt(int32_t window_id) override; void ClosePopupOnUI(int32_t window_id); private: friend class Menu; - void OnClosed(int32_t window_id, base::Closure callback); + void OnClosed(int32_t window_id, base::OnceClosure callback); - scoped_nsobject menu_controller_; + scoped_nsobject menu_controller_; // window ID -> open context menu - std::map> popup_controllers_; + std::map> popup_controllers_; base::WeakPtrFactory weak_factory_; @@ -56,4 +56,4 @@ class MenuMac : public Menu { } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_MENU_MAC_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_MENU_MAC_H_ diff --git a/shell/browser/api/atom_api_menu_mac.mm b/shell/browser/api/electron_api_menu_mac.mm similarity index 83% rename from shell/browser/api/atom_api_menu_mac.mm rename to shell/browser/api/electron_api_menu_mac.mm index 246918ab22dc6..84c8415e5444a 100644 --- a/shell/browser/api/atom_api_menu_mac.mm +++ b/shell/browser/api/electron_api_menu_mac.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#import "shell/browser/api/atom_api_menu_mac.h" +#import "shell/browser/api/electron_api_menu_mac.h" #include #include @@ -38,15 +38,19 @@ int x, int y, int positioning_item, - const base::Closure& callback) { + base::OnceClosure callback) { NativeWindow* native_window = window->window(); if (!native_window) return; + // Make sure the Menu object would not be garbage-collected until the callback + // has run. + base::OnceClosure callback_with_ref = BindSelfToClosure(std::move(callback)); + auto popup = base::BindOnce(&MenuMac::PopupOnUI, weak_factory_.GetWeakPtr(), native_window->GetWeakPtr(), window->weak_map_id(), x, y, - positioning_item, callback); + positioning_item, std::move(callback_with_ref)); base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(popup)); } @@ -55,16 +59,17 @@ int x, int y, int positioning_item, - base::Closure callback) { + base::OnceClosure callback) { if (!native_window) return; NSWindow* nswindow = native_window->GetNativeWindow().GetNativeNSWindow(); - auto close_callback = base::BindRepeating( - &MenuMac::OnClosed, weak_factory_.GetWeakPtr(), window_id, callback); - popup_controllers_[window_id] = base::scoped_nsobject( - [[AtomMenuController alloc] initWithModel:model() - useDefaultAccelerator:NO]); + base::OnceClosure close_callback = + base::BindOnce(&MenuMac::OnClosed, weak_factory_.GetWeakPtr(), window_id, + std::move(callback)); + popup_controllers_[window_id] = base::scoped_nsobject( + [[ElectronMenuController alloc] initWithModel:model() + useDefaultAccelerator:NO]); NSMenu* menu = [popup_controllers_[window_id] menu]; NSView* view = [nswindow contentView]; @@ -99,7 +104,7 @@ if (rightmostMenuPoint > screenRight) position.x = position.x - [menu size].width; - [popup_controllers_[window_id] setCloseCallback:close_callback]; + [popup_controllers_[window_id] setCloseCallback:std::move(close_callback)]; // Make sure events can be pumped while the menu is up. base::MessageLoopCurrent::ScopedNestableTaskAllower allow; @@ -137,17 +142,17 @@ } } -void MenuMac::OnClosed(int32_t window_id, base::Closure callback) { +void MenuMac::OnClosed(int32_t window_id, base::OnceClosure callback) { popup_controllers_.erase(window_id); - callback.Run(); + std::move(callback).Run(); } // static void Menu::SetApplicationMenu(Menu* base_menu) { MenuMac* menu = static_cast(base_menu); - base::scoped_nsobject menu_controller( - [[AtomMenuController alloc] initWithModel:menu->model_.get() - useDefaultAccelerator:YES]); + base::scoped_nsobject menu_controller( + [[ElectronMenuController alloc] initWithModel:menu->model_.get() + useDefaultAccelerator:YES]); NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop]; [currentRunLoop cancelPerformSelector:@selector(setMainMenu:) diff --git a/shell/browser/api/atom_api_menu_views.cc b/shell/browser/api/electron_api_menu_views.cc similarity index 70% rename from shell/browser/api/atom_api_menu_views.cc rename to shell/browser/api/electron_api_menu_views.cc index fa51699dbd986..ba0661ae974aa 100644 --- a/shell/browser/api/atom_api_menu_views.cc +++ b/shell/browser/api/electron_api_menu_views.cc @@ -2,9 +2,10 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_menu_views.h" +#include "shell/browser/api/electron_api_menu_views.h" #include +#include #include "shell/browser/native_window_views.h" #include "shell/browser/unresponsive_suppressor.h" @@ -24,7 +25,7 @@ void MenuViews::PopupAt(TopLevelWindow* window, int x, int y, int positioning_item, - const base::Closure& callback) { + base::OnceClosure callback) { auto* native_window = static_cast(window->window()); if (!native_window) return; @@ -43,12 +44,21 @@ void MenuViews::PopupAt(TopLevelWindow* window, // Don't emit unresponsive event when showing menu. electron::UnresponsiveSuppressor suppressor; + // Make sure the Menu object would not be garbage-collected until the callback + // has run. + base::OnceClosure callback_with_ref = BindSelfToClosure(std::move(callback)); + // Show the menu. + // + // Note that while views::MenuRunner accepts RepeatingCallback as close + // callback, it is fine passing OnceCallback to it because we reset the + // menu runner immediately when the menu is closed. int32_t window_id = window->weak_map_id(); - auto close_callback = base::BindRepeating( - &MenuViews::OnClosed, weak_factory_.GetWeakPtr(), window_id, callback); + auto close_callback = base::AdaptCallbackForRepeating( + base::BindOnce(&MenuViews::OnClosed, weak_factory_.GetWeakPtr(), + window_id, std::move(callback_with_ref))); menu_runners_[window_id] = - std::make_unique(model(), flags, close_callback); + std::make_unique(model(), flags, std::move(close_callback)); menu_runners_[window_id]->RunMenuAt( native_window->widget(), nullptr, gfx::Rect(location, gfx::Size()), views::MenuAnchorPosition::kTopLeft, ui::MENU_SOURCE_MOUSE); @@ -68,9 +78,9 @@ void MenuViews::ClosePopupAt(int32_t window_id) { } } -void MenuViews::OnClosed(int32_t window_id, base::Closure callback) { +void MenuViews::OnClosed(int32_t window_id, base::OnceClosure callback) { menu_runners_.erase(window_id); - callback.Run(); + std::move(callback).Run(); } // static diff --git a/shell/browser/api/atom_api_menu_views.h b/shell/browser/api/electron_api_menu_views.h similarity index 72% rename from shell/browser/api/atom_api_menu_views.h rename to shell/browser/api/electron_api_menu_views.h index f2e4c28103f52..d7aeb3c9c466a 100644 --- a/shell/browser/api/atom_api_menu_views.h +++ b/shell/browser/api/electron_api_menu_views.h @@ -2,14 +2,14 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_MENU_VIEWS_H_ -#define SHELL_BROWSER_API_ATOM_API_MENU_VIEWS_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_MENU_VIEWS_H_ +#define SHELL_BROWSER_API_ELECTRON_API_MENU_VIEWS_H_ #include #include #include "base/memory/weak_ptr.h" -#include "shell/browser/api/atom_api_menu.h" +#include "shell/browser/api/electron_api_menu.h" #include "ui/display/screen.h" #include "ui/views/controls/menu/menu_runner.h" @@ -27,11 +27,11 @@ class MenuViews : public Menu { int x, int y, int positioning_item, - const base::Closure& callback) override; + base::OnceClosure callback) override; void ClosePopupAt(int32_t window_id) override; private: - void OnClosed(int32_t window_id, base::Closure callback); + void OnClosed(int32_t window_id, base::OnceClosure callback); // window ID -> open context menu std::map> menu_runners_; @@ -45,4 +45,4 @@ class MenuViews : public Menu { } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_MENU_VIEWS_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_MENU_VIEWS_H_ diff --git a/shell/browser/api/atom_api_native_theme.cc b/shell/browser/api/electron_api_native_theme.cc similarity index 85% rename from shell/browser/api/atom_api_native_theme.cc rename to shell/browser/api/electron_api_native_theme.cc index 114edcf25bb2b..1f2c60a584294 100644 --- a/shell/browser/api/atom_api_native_theme.cc +++ b/shell/browser/api/electron_api_native_theme.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_native_theme.h" +#include "shell/browser/api/electron_api_native_theme.h" #include @@ -21,14 +21,16 @@ namespace electron { namespace api { -NativeTheme::NativeTheme(v8::Isolate* isolate, ui::NativeTheme* theme) - : theme_(theme) { - theme_->AddObserver(this); +NativeTheme::NativeTheme(v8::Isolate* isolate, + ui::NativeTheme* ui_theme, + ui::NativeTheme* web_theme) + : ui_theme_(ui_theme), web_theme_(web_theme) { + ui_theme_->AddObserver(this); Init(isolate); } NativeTheme::~NativeTheme() { - theme_->RemoveObserver(this); + ui_theme_->RemoveObserver(this); } void NativeTheme::OnNativeThemeUpdatedOnUI() { @@ -42,7 +44,8 @@ void NativeTheme::OnNativeThemeUpdated(ui::NativeTheme* theme) { } void NativeTheme::SetThemeSource(ui::NativeTheme::ThemeSource override) { - theme_->set_theme_source(override); + ui_theme_->set_theme_source(override); + web_theme_->set_theme_source(override); #if defined(OS_MACOSX) // Update the macOS appearance setting for this new override value UpdateMacOSAppearanceForOverrideValue(override); @@ -52,15 +55,15 @@ void NativeTheme::SetThemeSource(ui::NativeTheme::ThemeSource override) { } ui::NativeTheme::ThemeSource NativeTheme::GetThemeSource() const { - return theme_->theme_source(); + return ui_theme_->theme_source(); } bool NativeTheme::ShouldUseDarkColors() { - return theme_->ShouldUseDarkColors(); + return ui_theme_->ShouldUseDarkColors(); } bool NativeTheme::ShouldUseHighContrastColors() { - return theme_->UsesHighContrastColors(); + return ui_theme_->UsesHighContrastColors(); } #if defined(OS_MACOSX) @@ -85,8 +88,11 @@ bool NativeTheme::ShouldUseInvertedColorScheme() { // static v8::Local NativeTheme::Create(v8::Isolate* isolate) { - ui::NativeTheme* theme = ui::NativeTheme::GetInstanceForNativeUi(); - return gin::CreateHandle(isolate, new NativeTheme(isolate, theme)).ToV8(); + ui::NativeTheme* ui_theme = ui::NativeTheme::GetInstanceForNativeUi(); + ui::NativeTheme* web_theme = ui::NativeTheme::GetInstanceForWeb(); + return gin::CreateHandle(isolate, + new NativeTheme(isolate, ui_theme, web_theme)) + .ToV8(); } // static diff --git a/shell/browser/api/atom_api_native_theme.h b/shell/browser/api/electron_api_native_theme.h similarity index 82% rename from shell/browser/api/atom_api_native_theme.h rename to shell/browser/api/electron_api_native_theme.h index 7be9604ac1175..a38fa98a9c28f 100644 --- a/shell/browser/api/atom_api_native_theme.h +++ b/shell/browser/api/electron_api_native_theme.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_NATIVE_THEME_H_ -#define SHELL_BROWSER_API_ATOM_API_NATIVE_THEME_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_NATIVE_THEME_H_ +#define SHELL_BROWSER_API_ELECTRON_API_NATIVE_THEME_H_ #include "shell/browser/api/event_emitter_deprecated.h" #include "ui/native_theme/native_theme.h" @@ -22,7 +22,9 @@ class NativeTheme : public mate::EventEmitter, v8::Local prototype); protected: - NativeTheme(v8::Isolate* isolate, ui::NativeTheme* theme); + NativeTheme(v8::Isolate* isolate, + ui::NativeTheme* ui_theme, + ui::NativeTheme* web_theme); ~NativeTheme() override; void SetThemeSource(ui::NativeTheme::ThemeSource override); @@ -40,7 +42,8 @@ class NativeTheme : public mate::EventEmitter, void OnNativeThemeUpdatedOnUI(); private: - ui::NativeTheme* theme_; + ui::NativeTheme* ui_theme_; + ui::NativeTheme* web_theme_; DISALLOW_COPY_AND_ASSIGN(NativeTheme); }; @@ -62,4 +65,4 @@ struct Converter { } // namespace gin -#endif // SHELL_BROWSER_API_ATOM_API_NATIVE_THEME_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_NATIVE_THEME_H_ diff --git a/shell/browser/api/atom_api_native_theme_mac.mm b/shell/browser/api/electron_api_native_theme_mac.mm similarity index 90% rename from shell/browser/api/atom_api_native_theme_mac.mm rename to shell/browser/api/electron_api_native_theme_mac.mm index 04e25a2ef4ad6..2dab1ea61ae0b 100644 --- a/shell/browser/api/atom_api_native_theme_mac.mm +++ b/shell/browser/api/electron_api_native_theme_mac.mm @@ -2,10 +2,10 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_native_theme.h" +#include "shell/browser/api/electron_api_native_theme.h" #include "base/mac/sdk_forward_declarations.h" -#include "shell/browser/mac/atom_application.h" +#include "shell/browser/mac/electron_application.h" namespace electron { diff --git a/shell/browser/api/atom_api_net.cc b/shell/browser/api/electron_api_net.cc similarity index 65% rename from shell/browser/api/atom_api_net.cc rename to shell/browser/api/electron_api_net.cc index fbaebc9a641b6..07789fd90bd84 100644 --- a/shell/browser/api/atom_api_net.cc +++ b/shell/browser/api/electron_api_net.cc @@ -2,12 +2,14 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_net.h" +#include "shell/browser/api/electron_api_net.h" + +#include #include "native_mate/dictionary.h" #include "native_mate/handle.h" #include "services/network/public/cpp/features.h" -#include "shell/browser/api/atom_api_url_request_ns.h" +#include "shell/browser/api/electron_api_url_loader.h" #include "shell/common/node_includes.h" @@ -31,12 +33,12 @@ void Net::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { prototype->SetClassName(mate::StringToV8(isolate, "Net")); mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) - .SetProperty("URLRequest", &Net::URLRequest); + .SetProperty("URLLoader", &Net::URLLoader); } -v8::Local Net::URLRequest(v8::Isolate* isolate) { +v8::Local Net::URLLoader(v8::Isolate* isolate) { v8::Local constructor; - constructor = URLRequestNS::GetConstructor(isolate); + constructor = SimpleURLLoaderWrapper::GetConstructor(isolate); return constructor->GetFunction(isolate->GetCurrentContext()) .ToLocalChecked(); } @@ -47,8 +49,16 @@ v8::Local Net::URLRequest(v8::Isolate* isolate) { namespace { +bool IsValidHeaderName(std::string header_name) { + return net::HttpUtil::IsValidHeaderName(header_name); +} + +bool IsValidHeaderValue(std::string header_value) { + return net::HttpUtil::IsValidHeaderValue(header_value); +} + using electron::api::Net; -using electron::api::URLRequestNS; +using electron::api::SimpleURLLoaderWrapper; void Initialize(v8::Local exports, v8::Local unused, @@ -56,12 +66,15 @@ void Initialize(v8::Local exports, void* priv) { v8::Isolate* isolate = context->GetIsolate(); - URLRequestNS::SetConstructor(isolate, base::BindRepeating(URLRequestNS::New)); + SimpleURLLoaderWrapper::SetConstructor( + isolate, base::BindRepeating(SimpleURLLoaderWrapper::New)); mate::Dictionary dict(isolate, exports); dict.Set("net", Net::Create(isolate)); dict.Set("Net", Net::GetConstructor(isolate)->GetFunction(context).ToLocalChecked()); + dict.SetMethod("_isValidHeaderName", &IsValidHeaderName); + dict.SetMethod("_isValidHeaderValue", &IsValidHeaderValue); } } // namespace diff --git a/shell/browser/api/atom_api_net.h b/shell/browser/api/electron_api_net.h similarity index 76% rename from shell/browser/api/atom_api_net.h rename to shell/browser/api/electron_api_net.h index d9193aabff7d3..b2b071375c8b5 100644 --- a/shell/browser/api/atom_api_net.h +++ b/shell/browser/api/electron_api_net.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_NET_H_ -#define SHELL_BROWSER_API_ATOM_API_NET_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_NET_H_ +#define SHELL_BROWSER_API_ELECTRON_API_NET_H_ #include "shell/browser/api/event_emitter_deprecated.h" @@ -18,7 +18,7 @@ class Net : public mate::EventEmitter { static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); - v8::Local URLRequest(v8::Isolate* isolate); + v8::Local URLLoader(v8::Isolate* isolate); protected: explicit Net(v8::Isolate* isolate); @@ -32,4 +32,4 @@ class Net : public mate::EventEmitter { } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_NET_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_NET_H_ diff --git a/shell/browser/api/atom_api_net_log.cc b/shell/browser/api/electron_api_net_log.cc similarity index 96% rename from shell/browser/api/atom_api_net_log.cc rename to shell/browser/api/electron_api_net_log.cc index 5400089f4f852..ff07f9760492e 100644 --- a/shell/browser/api/atom_api_net_log.cc +++ b/shell/browser/api/electron_api_net_log.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_net_log.h" +#include "shell/browser/api/electron_api_net_log.h" #include @@ -14,7 +14,7 @@ #include "native_mate/converter.h" #include "native_mate/dictionary.h" #include "native_mate/handle.h" -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/electron_browser_context.h" #include "shell/browser/net/system_network_context_manager.h" #include "shell/common/native_mate_converters/file_path_converter.h" #include "shell/common/node_includes.h" @@ -75,7 +75,7 @@ void ResolvePromiseWithNetError(util::Promise promise, int32_t error) { namespace api { -NetLog::NetLog(v8::Isolate* isolate, AtomBrowserContext* browser_context) +NetLog::NetLog(v8::Isolate* isolate, ElectronBrowserContext* browser_context) : browser_context_(browser_context), weak_ptr_factory_(this) { Init(isolate); file_task_runner_ = CreateFileTaskRunner(); @@ -212,7 +212,7 @@ v8::Local NetLog::StopLogging(mate::Arguments* args) { // static mate::Handle NetLog::Create(v8::Isolate* isolate, - AtomBrowserContext* browser_context) { + ElectronBrowserContext* browser_context) { return mate::CreateHandle(isolate, new NetLog(isolate, browser_context)); } diff --git a/shell/browser/api/atom_api_net_log.h b/shell/browser/api/electron_api_net_log.h similarity index 80% rename from shell/browser/api/atom_api_net_log.h rename to shell/browser/api/electron_api_net_log.h index 3618a3a6ddc79..7e7eaec415380 100644 --- a/shell/browser/api/atom_api_net_log.h +++ b/shell/browser/api/electron_api_net_log.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_NET_LOG_H_ -#define SHELL_BROWSER_API_ATOM_API_NET_LOG_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_NET_LOG_H_ +#define SHELL_BROWSER_API_ELECTRON_API_NET_LOG_H_ #include #include @@ -19,14 +19,14 @@ namespace electron { -class AtomBrowserContext; +class ElectronBrowserContext; namespace api { class NetLog : public mate::TrackableObject { public: static mate::Handle Create(v8::Isolate* isolate, - AtomBrowserContext* browser_context); + ElectronBrowserContext* browser_context); static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); @@ -37,7 +37,8 @@ class NetLog : public mate::TrackableObject { bool IsCurrentlyLogging() const; protected: - explicit NetLog(v8::Isolate* isolate, AtomBrowserContext* browser_context); + explicit NetLog(v8::Isolate* isolate, + ElectronBrowserContext* browser_context); ~NetLog() override; void OnConnectionError(); @@ -49,7 +50,7 @@ class NetLog : public mate::TrackableObject { void NetLogStarted(int32_t error); private: - AtomBrowserContext* browser_context_; + ElectronBrowserContext* browser_context_; network::mojom::NetLogExporterPtr net_log_exporter_; @@ -66,4 +67,4 @@ class NetLog : public mate::TrackableObject { } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_NET_LOG_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_NET_LOG_H_ diff --git a/shell/browser/api/atom_api_notification.cc b/shell/browser/api/electron_api_notification.cc similarity index 95% rename from shell/browser/api/atom_api_notification.cc rename to shell/browser/api/electron_api_notification.cc index 769f6f95cb9e0..26894f1014f84 100644 --- a/shell/browser/api/atom_api_notification.cc +++ b/shell/browser/api/electron_api_notification.cc @@ -2,14 +2,14 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_notification.h" +#include "shell/browser/api/electron_api_notification.h" #include "base/guid.h" #include "base/strings/utf_string_conversions.h" #include "native_mate/constructor.h" -#include "shell/browser/api/atom_api_menu.h" -#include "shell/browser/atom_browser_client.h" +#include "shell/browser/api/electron_api_menu.h" #include "shell/browser/browser.h" +#include "shell/browser/electron_browser_client.h" #include "shell/common/gin_converters/image_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/object_template_builder.h" @@ -52,7 +52,7 @@ namespace api { Notification::Notification(gin::Arguments* args) { InitWithArgs(args); - presenter_ = static_cast(AtomBrowserClient::Get()) + presenter_ = static_cast(ElectronBrowserClient::Get()) ->GetNotificationPresenter(); gin::Dictionary opts(nullptr); @@ -206,6 +206,7 @@ void Notification::NotificationClosed() { void Notification::Close() { if (notification_) { notification_->Dismiss(); + notification_->set_delegate(nullptr); notification_.reset(); } } @@ -236,7 +237,7 @@ void Notification::Show() { } bool Notification::IsSupported() { - return !!static_cast(AtomBrowserClient::Get()) + return !!static_cast(ElectronBrowserClient::Get()) ->GetNotificationPresenter(); } diff --git a/shell/browser/api/atom_api_notification.h b/shell/browser/api/electron_api_notification.h similarity index 94% rename from shell/browser/api/atom_api_notification.h rename to shell/browser/api/electron_api_notification.h index 23f5a7b45875c..a3630d05f9c40 100644 --- a/shell/browser/api/atom_api_notification.h +++ b/shell/browser/api/electron_api_notification.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_NOTIFICATION_H_ -#define SHELL_BROWSER_API_ATOM_API_NOTIFICATION_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_NOTIFICATION_H_ +#define SHELL_BROWSER_API_ELECTRON_API_NOTIFICATION_H_ #include #include @@ -103,4 +103,4 @@ class Notification : public mate::TrackableObject, } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_NOTIFICATION_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_NOTIFICATION_H_ diff --git a/shell/browser/api/atom_api_power_monitor.cc b/shell/browser/api/electron_api_power_monitor.cc similarity index 98% rename from shell/browser/api/atom_api_power_monitor.cc rename to shell/browser/api/electron_api_power_monitor.cc index 4240795eef61c..242881433b489 100644 --- a/shell/browser/api/atom_api_power_monitor.cc +++ b/shell/browser/api/electron_api_power_monitor.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_power_monitor.h" +#include "shell/browser/api/electron_api_power_monitor.h" #include "base/power_monitor/power_monitor.h" #include "base/power_monitor/power_monitor_device_source.h" diff --git a/shell/browser/api/atom_api_power_monitor.h b/shell/browser/api/electron_api_power_monitor.h similarity index 92% rename from shell/browser/api/atom_api_power_monitor.h rename to shell/browser/api/electron_api_power_monitor.h index 075e7d6943b02..2af9d0fe5c906 100644 --- a/shell/browser/api/atom_api_power_monitor.h +++ b/shell/browser/api/electron_api_power_monitor.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_POWER_MONITOR_H_ -#define SHELL_BROWSER_API_ATOM_API_POWER_MONITOR_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_POWER_MONITOR_H_ +#define SHELL_BROWSER_API_ELECTRON_API_POWER_MONITOR_H_ #include "base/compiler_specific.h" #include "native_mate/handle.h" @@ -78,4 +78,4 @@ class PowerMonitor : public mate::TrackableObject, } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_POWER_MONITOR_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_POWER_MONITOR_H_ diff --git a/shell/browser/api/atom_api_power_monitor_mac.mm b/shell/browser/api/electron_api_power_monitor_mac.mm similarity index 96% rename from shell/browser/api/atom_api_power_monitor_mac.mm rename to shell/browser/api/electron_api_power_monitor_mac.mm index e34f68efad9aa..7bad49285642b 100644 --- a/shell/browser/api/atom_api_power_monitor_mac.mm +++ b/shell/browser/api/electron_api_power_monitor_mac.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_power_monitor.h" +#include "shell/browser/api/electron_api_power_monitor.h" #include diff --git a/shell/browser/api/atom_api_power_monitor_win.cc b/shell/browser/api/electron_api_power_monitor_win.cc similarity index 97% rename from shell/browser/api/atom_api_power_monitor_win.cc rename to shell/browser/api/electron_api_power_monitor_win.cc index ad85edddc6617..ca3a825f38411 100644 --- a/shell/browser/api/atom_api_power_monitor_win.cc +++ b/shell/browser/api/electron_api_power_monitor_win.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_power_monitor.h" +#include "shell/browser/api/electron_api_power_monitor.h" #include #include diff --git a/shell/browser/api/atom_api_power_save_blocker.cc b/shell/browser/api/electron_api_power_save_blocker.cc similarity index 89% rename from shell/browser/api/atom_api_power_save_blocker.cc rename to shell/browser/api/electron_api_power_save_blocker.cc index 4423f4254194d..8782c339d8eea 100644 --- a/shell/browser/api/atom_api_power_save_blocker.cc +++ b/shell/browser/api/electron_api_power_save_blocker.cc @@ -2,14 +2,14 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_power_save_blocker.h" +#include "shell/browser/api/electron_api_power_save_blocker.h" #include #include "base/bind_helpers.h" #include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" -#include "content/public/common/service_manager_connection.h" +#include "content/public/browser/system_connector.h" #include "gin/dictionary.h" #include "gin/function_template.h" #include "services/device/public/mojom/constants.mojom.h" @@ -87,17 +87,16 @@ void PowerSaveBlocker::UpdatePowerSaveBlocker() { device::mojom::WakeLock* PowerSaveBlocker::GetWakeLock() { if (!wake_lock_) { - device::mojom::WakeLockProviderPtr wake_lock_provider; - DCHECK(content::ServiceManagerConnection::GetForProcess()); - auto* connector = - content::ServiceManagerConnection::GetForProcess()->GetConnector(); - connector->BindInterface(device::mojom::kServiceName, - mojo::MakeRequest(&wake_lock_provider)); + mojo::Remote wake_lock_provider; + DCHECK(content::GetSystemConnector()); + content::GetSystemConnector()->Connect( + device::mojom::kServiceName, + wake_lock_provider.BindNewPipeAndPassReceiver()); wake_lock_provider->GetWakeLockWithoutContext( device::mojom::WakeLockType::kPreventAppSuspension, device::mojom::WakeLockReason::kOther, ELECTRON_PRODUCT_NAME, - mojo::MakeRequest(&wake_lock_)); + wake_lock_.BindNewPipeAndPassReceiver()); } return wake_lock_.get(); } diff --git a/shell/browser/api/atom_api_power_save_blocker.h b/shell/browser/api/electron_api_power_save_blocker.h similarity index 82% rename from shell/browser/api/atom_api_power_save_blocker.h rename to shell/browser/api/electron_api_power_save_blocker.h index 42ad84aab24fd..2a5ad80afe94a 100644 --- a/shell/browser/api/atom_api_power_save_blocker.h +++ b/shell/browser/api/electron_api_power_save_blocker.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_POWER_SAVE_BLOCKER_H_ -#define SHELL_BROWSER_API_ATOM_API_POWER_SAVE_BLOCKER_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_POWER_SAVE_BLOCKER_H_ +#define SHELL_BROWSER_API_ELECTRON_API_POWER_SAVE_BLOCKER_H_ #include #include @@ -11,6 +11,7 @@ #include "gin/handle.h" #include "gin/object_template_builder.h" #include "gin/wrappable.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/mojom/wake_lock.mojom.h" namespace electron { @@ -49,7 +50,7 @@ class PowerSaveBlocker : public gin::Wrappable { using WakeLockTypeMap = std::map; WakeLockTypeMap wake_lock_types_; - device::mojom::WakeLockPtr wake_lock_; + mojo::Remote wake_lock_; DISALLOW_COPY_AND_ASSIGN(PowerSaveBlocker); }; @@ -58,4 +59,4 @@ class PowerSaveBlocker : public gin::Wrappable { } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_POWER_SAVE_BLOCKER_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_POWER_SAVE_BLOCKER_H_ diff --git a/shell/browser/api/atom_api_protocol_ns.cc b/shell/browser/api/electron_api_protocol_ns.cc similarity index 97% rename from shell/browser/api/atom_api_protocol_ns.cc rename to shell/browser/api/electron_api_protocol_ns.cc index 83963c24d870f..6b1c4e01667a3 100644 --- a/shell/browser/api/atom_api_protocol_ns.cc +++ b/shell/browser/api/electron_api_protocol_ns.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_protocol_ns.h" +#include "shell/browser/api/electron_api_protocol_ns.h" #include #include @@ -10,8 +10,8 @@ #include "base/stl_util.h" #include "content/public/browser/child_process_security_policy.h" -#include "shell/browser/atom_browser_context.h" #include "shell/browser/browser.h" +#include "shell/browser/electron_browser_context.h" #include "shell/common/deprecate_util.h" #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/gin_converters/net_converter.h" @@ -19,6 +19,7 @@ #include "shell/common/gin_helper/object_template_builder.h" #include "shell/common/options_switches.h" #include "shell/common/promise_util.h" +#include "url/url_util.h" namespace { @@ -160,7 +161,7 @@ std::string ErrorCodeToString(ProtocolError error) { } // namespace ProtocolNS::ProtocolNS(v8::Isolate* isolate, - AtomBrowserContext* browser_context) { + ElectronBrowserContext* browser_context) { Init(isolate); AttachAsUserData(browser_context); } @@ -170,7 +171,7 @@ ProtocolNS::~ProtocolNS() = default; void ProtocolNS::RegisterURLLoaderFactories( content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories) { for (const auto& it : handlers_) { - factories->emplace(it.first, std::make_unique( + factories->emplace(it.first, std::make_unique( it.second.first, it.second.second)); } } @@ -256,7 +257,7 @@ void ProtocolNS::HandleOptionalCallback(gin::Arguments* args, // static gin::Handle ProtocolNS::Create( v8::Isolate* isolate, - AtomBrowserContext* browser_context) { + ElectronBrowserContext* browser_context) { return gin::CreateHandle(isolate, new ProtocolNS(isolate, browser_context)); } diff --git a/shell/browser/api/atom_api_protocol_ns.h b/shell/browser/api/electron_api_protocol_ns.h similarity index 85% rename from shell/browser/api/atom_api_protocol_ns.h rename to shell/browser/api/electron_api_protocol_ns.h index b8cd623c57394..febbd34eb605a 100644 --- a/shell/browser/api/atom_api_protocol_ns.h +++ b/shell/browser/api/electron_api_protocol_ns.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_PROTOCOL_NS_H_ -#define SHELL_BROWSER_API_ATOM_API_PROTOCOL_NS_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_PROTOCOL_NS_H_ +#define SHELL_BROWSER_API_ELECTRON_API_PROTOCOL_NS_H_ #include #include @@ -11,12 +11,12 @@ #include "content/public/browser/content_browser_client.h" #include "gin/handle.h" #include "shell/browser/api/trackable_object.h" -#include "shell/browser/net/atom_url_loader_factory.h" +#include "shell/browser/net/electron_url_loader_factory.h" #include "shell/common/gin_helper/dictionary.h" namespace electron { -class AtomBrowserContext; +class ElectronBrowserContext; namespace api { @@ -37,20 +37,21 @@ enum class ProtocolError { // Protocol implementation based on network services. class ProtocolNS : public mate::TrackableObject { public: - static gin::Handle Create(v8::Isolate* isolate, - AtomBrowserContext* browser_context); + static gin::Handle Create( + v8::Isolate* isolate, + ElectronBrowserContext* browser_context); static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); - // Used by AtomBrowserClient for creating URLLoaderFactory. + // Used by ElectronBrowserClient for creating URLLoaderFactory. void RegisterURLLoaderFactories( content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories); const HandlersMap& intercept_handlers() const { return intercept_handlers_; } private: - ProtocolNS(v8::Isolate* isolate, AtomBrowserContext* browser_context); + ProtocolNS(v8::Isolate* isolate, ElectronBrowserContext* browser_context); ~ProtocolNS() override; // Callback types. @@ -99,4 +100,4 @@ class ProtocolNS : public mate::TrackableObject { } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_PROTOCOL_NS_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_PROTOCOL_NS_H_ diff --git a/shell/browser/api/atom_api_screen.cc b/shell/browser/api/electron_api_screen.cc similarity index 96% rename from shell/browser/api/atom_api_screen.cc rename to shell/browser/api/electron_api_screen.cc index 8ce966d74e5e4..e3e6b8cbc17af 100644 --- a/shell/browser/api/atom_api_screen.cc +++ b/shell/browser/api/electron_api_screen.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_screen.h" +#include "shell/browser/api/electron_api_screen.h" #include #include @@ -14,6 +14,7 @@ #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/gin_converters/gfx_converter.h" #include "shell/common/gin_converters/native_window_converter.h" +#include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/object_template_builder.h" #include "shell/common/node_includes.h" #include "ui/display/display.h" @@ -184,8 +185,8 @@ void Initialize(v8::Local exports, v8::Local context, void* priv) { v8::Isolate* isolate = context->GetIsolate(); - gin::Dictionary dict(isolate, exports); - dict.Set("createScreen", base::BindRepeating(&Screen::Create)); + gin_helper::Dictionary dict(isolate, exports); + dict.SetMethod("createScreen", base::BindRepeating(&Screen::Create)); dict.Set( "Screen", Screen::GetConstructor(isolate)->GetFunction(context).ToLocalChecked()); diff --git a/shell/browser/api/atom_api_screen.h b/shell/browser/api/electron_api_screen.h similarity index 91% rename from shell/browser/api/atom_api_screen.h rename to shell/browser/api/electron_api_screen.h index 1326e798f2fc1..48443cad11bec 100644 --- a/shell/browser/api/atom_api_screen.h +++ b/shell/browser/api/electron_api_screen.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_SCREEN_H_ -#define SHELL_BROWSER_API_ATOM_API_SCREEN_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_SCREEN_H_ +#define SHELL_BROWSER_API_ELECTRON_API_SCREEN_H_ #include @@ -57,4 +57,4 @@ class Screen : public gin_helper::EventEmitter>, } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_SCREEN_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_SCREEN_H_ diff --git a/shell/browser/api/atom_api_session.cc b/shell/browser/api/electron_api_session.cc similarity index 81% rename from shell/browser/api/atom_api_session.cc rename to shell/browser/api/electron_api_session.cc index b471121c236d3..ac633b59eb072 100644 --- a/shell/browser/api/atom_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_session.h" +#include "shell/browser/api/electron_api_session.h" #include #include @@ -11,6 +11,7 @@ #include #include +#include "base/command_line.h" #include "base/files/file_path.h" #include "base/guid.h" #include "base/strings/string_number_conversions.h" @@ -41,16 +42,16 @@ #include "net/http/http_cache.h" #include "services/network/network_service.h" #include "services/network/public/cpp/features.h" -#include "shell/browser/api/atom_api_cookies.h" -#include "shell/browser/api/atom_api_data_pipe_holder.h" -#include "shell/browser/api/atom_api_download_item.h" -#include "shell/browser/api/atom_api_net_log.h" -#include "shell/browser/api/atom_api_protocol_ns.h" -#include "shell/browser/api/atom_api_web_request_ns.h" -#include "shell/browser/atom_browser_context.h" -#include "shell/browser/atom_browser_main_parts.h" -#include "shell/browser/atom_permission_manager.h" +#include "shell/browser/api/electron_api_cookies.h" +#include "shell/browser/api/electron_api_data_pipe_holder.h" +#include "shell/browser/api/electron_api_download_item.h" +#include "shell/browser/api/electron_api_net_log.h" +#include "shell/browser/api/electron_api_protocol_ns.h" +#include "shell/browser/api/electron_api_web_request_ns.h" #include "shell/browser/browser.h" +#include "shell/browser/electron_browser_context.h" +#include "shell/browser/electron_browser_main_parts.h" +#include "shell/browser/electron_permission_manager.h" #include "shell/browser/media/media_device_id_salt.h" #include "shell/browser/net/cert_verifier_client.h" #include "shell/browser/session_preferences.h" @@ -66,12 +67,41 @@ #include "ui/base/l10n/l10n_util.h" #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) -#include "shell/browser/extensions/atom_extension_system.h" +#include "shell/browser/extensions/electron_extension_system.h" +#endif + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "chrome/browser/spellchecker/spellcheck_factory.h" // nogncheck +#include "chrome/browser/spellchecker/spellcheck_service.h" // nogncheck +#include "components/spellcheck/browser/pref_names.h" +#include "components/spellcheck/common/spellcheck_common.h" + +#if BUILDFLAG(USE_BROWSER_SPELLCHECKER) +#include "components/spellcheck/browser/spellcheck_platform.h" +#include "components/spellcheck/common/spellcheck_features.h" +#endif #endif using content::BrowserThread; using content::StoragePartition; +namespace predictors { +// NOTE(nornagon): this is copied from +// //chrome/browser/predictors/resource_prefetch_predictor.cc we don't need +// anything in that file other than this constructor. Without it we get a link +// error. Probably upstream the constructor should be moved to +// preconnect_manager.cc. +PreconnectRequest::PreconnectRequest( + const url::Origin& origin, + int num_sockets, + const net::NetworkIsolationKey& network_isolation_key) + : origin(origin), + num_sockets(num_sockets), + network_isolation_key(network_isolation_key) { + DCHECK_GE(num_sockets, 0); +} +} // namespace predictors + namespace { struct ClearStorageDataOptions { @@ -195,7 +225,7 @@ void DestroyGlobalHandle(v8::Isolate* isolate, } // namespace -Session::Session(v8::Isolate* isolate, AtomBrowserContext* browser_context) +Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context) : network_emulation_token_(base::UnguessableToken::Create()), browser_context_(browser_context) { // Observe DownloadManager to get download notifications. @@ -206,11 +236,28 @@ Session::Session(v8::Isolate* isolate, AtomBrowserContext* browser_context) Init(isolate); AttachAsUserData(browser_context); + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + SpellcheckService* service = + SpellcheckServiceFactory::GetForContext(browser_context_.get()); + if (service) { + service->SetHunspellObserver(this); + } +#endif } Session::~Session() { content::BrowserContext::GetDownloadManager(browser_context()) ->RemoveObserver(this); + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + SpellcheckService* service = + SpellcheckServiceFactory::GetForContext(browser_context_.get()); + if (service) { + service->SetHunspellObserver(nullptr); + } +#endif + // TODO(zcbenz): Now since URLRequestContextGetter is gone, is this still // needed? // Refs https://github.com/electron/electron/pull/12305. @@ -239,6 +286,21 @@ void Session::OnDownloadCreated(content::DownloadManager* manager, } } +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +void Session::OnHunspellDictionaryInitialized(const std::string& language) { + Emit("spellcheck-dictionary-initialized", language); +} +void Session::OnHunspellDictionaryDownloadBegin(const std::string& language) { + Emit("spellcheck-dictionary-download-begin", language); +} +void Session::OnHunspellDictionaryDownloadSuccess(const std::string& language) { + Emit("spellcheck-dictionary-download-success", language); +} +void Session::OnHunspellDictionaryDownloadFailure(const std::string& language) { + Emit("spellcheck-dictionary-download-failure", language); +} +#endif + v8::Local Session::ResolveProxy(mate::Arguments* args) { v8::Isolate* isolate = args->isolate(); util::Promise promise(isolate); @@ -419,23 +481,23 @@ void Session::SetCertVerifyProc(v8::Local val, void Session::SetPermissionRequestHandler(v8::Local val, mate::Arguments* args) { - auto* permission_manager = static_cast( + auto* permission_manager = static_cast( browser_context()->GetPermissionControllerDelegate()); if (val->IsNull()) { permission_manager->SetPermissionRequestHandler( - AtomPermissionManager::RequestHandler()); + ElectronPermissionManager::RequestHandler()); return; } - auto handler = std::make_unique(); + auto handler = std::make_unique(); if (!mate::ConvertFromV8(args->isolate(), val, handler.get())) { args->ThrowError("Must pass null or function"); return; } permission_manager->SetPermissionRequestHandler(base::BindRepeating( - [](AtomPermissionManager::RequestHandler* handler, + [](ElectronPermissionManager::RequestHandler* handler, content::WebContents* web_contents, content::PermissionType permission_type, - AtomPermissionManager::StatusCallback callback, + ElectronPermissionManager::StatusCallback callback, const base::Value& details) { handler->Run(web_contents, permission_type, base::AdaptCallbackForRepeating(std::move(callback)), @@ -446,12 +508,12 @@ void Session::SetPermissionRequestHandler(v8::Local val, void Session::SetPermissionCheckHandler(v8::Local val, mate::Arguments* args) { - AtomPermissionManager::CheckHandler handler; + ElectronPermissionManager::CheckHandler handler; if (!(val->IsNull() || mate::ConvertFromV8(args->isolate(), val, &handler))) { args->ThrowError("Must pass null or function"); return; } - auto* permission_manager = static_cast( + auto* permission_manager = static_cast( browser_context()->GetPermissionControllerDelegate()); permission_manager->SetPermissionCheckHandler(handler); } @@ -489,6 +551,9 @@ void Session::AllowNTLMCredentialsForDomains(const std::string& domains) { network::mojom::HttpAuthDynamicParamsPtr auth_dynamic_params = network::mojom::HttpAuthDynamicParams::New(); auth_dynamic_params->server_allowlist = domains; + auth_dynamic_params->enable_negotiate_port = + base::CommandLine::ForCurrentProcess()->HasSwitch( + electron::switches::kEnableAuthNegotiatePort); content::GetNetworkService()->ConfigureHttpAuthPrefs( std::move(auth_dynamic_params)); } @@ -521,7 +586,7 @@ void Session::DownloadURL(const GURL& url) { auto* download_manager = content::BrowserContext::GetDownloadManager(browser_context()); auto download_params = std::make_unique( - url, MISSING_TRAFFIC_ANNOTATION); + url, MISSING_TRAFFIC_ANNOTATION, net::NetworkIsolationKey()); download_manager->DownloadUrl(std::move(download_params)); } @@ -571,7 +636,7 @@ std::vector Session::GetPreloads() const { #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) void Session::LoadChromeExtension(const base::FilePath extension_path) { - auto* extension_system = static_cast( + auto* extension_system = static_cast( extensions::ExtensionSystem::Get(browser_context())); extension_system->LoadExtension(extension_path); } @@ -611,11 +676,12 @@ v8::Local Session::NetLog(v8::Isolate* isolate) { } static void StartPreconnectOnUI( - scoped_refptr browser_context, + scoped_refptr browser_context, const GURL& url, int num_sockets_to_preconnect) { std::vector requests = { - {url.GetOrigin(), num_sockets_to_preconnect, net::NetworkIsolationKey()}}; + {url::Origin::Create(url), num_sockets_to_preconnect, + net::NetworkIsolationKey()}}; browser_context->GetPreconnectManager()->Start(url, requests); } @@ -646,9 +712,60 @@ void Session::Preconnect(const mate::Dictionary& options, url, num_sockets_to_preconnect)); } +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +base::Value Session::GetSpellCheckerLanguages() { + return browser_context_->prefs() + ->Get(spellcheck::prefs::kSpellCheckDictionaries) + ->Clone(); +} + +void Session::SetSpellCheckerLanguages( + gin_helper::ErrorThrower thrower, + const std::vector& languages) { + base::ListValue language_codes; + for (const std::string& lang : languages) { + std::string code = spellcheck::GetCorrespondingSpellCheckLanguage(lang); + if (code.empty()) { + thrower.ThrowError("Invalid language code provided: \"" + lang + + "\" is not a valid language code"); + return; + } + language_codes.AppendString(code); + } + browser_context_->prefs()->Set(spellcheck::prefs::kSpellCheckDictionaries, + language_codes); +} + +void SetSpellCheckerDictionaryDownloadURL(gin_helper::ErrorThrower thrower, + const GURL& url) { + if (!url.is_valid()) { + thrower.ThrowError( + "The URL you provided to setSpellCheckerDictionaryDownloadURL is not a " + "valid URL"); + return; + } + SpellcheckHunspellDictionary::SetBaseDownloadURL(url); +} + +bool Session::AddWordToSpellCheckerDictionary(const std::string& word) { +#if BUILDFLAG(USE_BROWSER_SPELLCHECKER) + if (spellcheck::UseBrowserSpellChecker()) { + spellcheck_platform::AddWord(base::UTF8ToUTF16(word)); + } +#endif + SpellcheckService* spellcheck = + SpellcheckServiceFactory::GetForContext(browser_context_.get()); + if (!spellcheck) + return false; + + return spellcheck->GetCustomDictionary()->AddWord(word); +} +#endif // BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + // static -mate::Handle Session::CreateFrom(v8::Isolate* isolate, - AtomBrowserContext* browser_context) { +mate::Handle Session::CreateFrom( + v8::Isolate* isolate, + ElectronBrowserContext* browser_context) { auto* existing = TrackableObject::FromWrappedClass(isolate, browser_context); if (existing) return mate::CreateHandle(isolate, static_cast(existing)); @@ -669,15 +786,15 @@ mate::Handle Session::FromPartition( v8::Isolate* isolate, const std::string& partition, const base::DictionaryValue& options) { - scoped_refptr browser_context; + scoped_refptr browser_context; if (partition.empty()) { - browser_context = AtomBrowserContext::From("", false, options); + browser_context = ElectronBrowserContext::From("", false, options); } else if (base::StartsWith(partition, kPersistPrefix, base::CompareCase::SENSITIVE)) { std::string name = partition.substr(8); - browser_context = AtomBrowserContext::From(name, false, options); + browser_context = ElectronBrowserContext::From(name, false, options); } else { - browser_context = AtomBrowserContext::From(partition, true, options); + browser_context = ElectronBrowserContext::From(partition, true, options); } return CreateFrom(isolate, browser_context.get()); } @@ -716,6 +833,16 @@ void Session::BuildPrototype(v8::Isolate* isolate, .SetMethod("getPreloads", &Session::GetPreloads) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) .SetMethod("loadChromeExtension", &Session::LoadChromeExtension) +#endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + .SetMethod("getSpellCheckerLanguages", &Session::GetSpellCheckerLanguages) + .SetMethod("setSpellCheckerLanguages", &Session::SetSpellCheckerLanguages) + .SetProperty("availableSpellCheckerLanguages", + &spellcheck::SpellCheckLanguages) + .SetMethod("setSpellCheckerDictionaryDownloadURL", + &SetSpellCheckerDictionaryDownloadURL) + .SetMethod("addWordToSpellCheckerDictionary", + &Session::AddWordToSpellCheckerDictionary) #endif .SetMethod("preconnect", &Session::Preconnect) .SetProperty("cookies", &Session::Cookies) diff --git a/shell/browser/api/atom_api_session.h b/shell/browser/api/electron_api_session.h similarity index 70% rename from shell/browser/api/atom_api_session.h rename to shell/browser/api/electron_api_session.h index 1e18ba2733a9d..894ab54f7db7c 100644 --- a/shell/browser/api/atom_api_session.h +++ b/shell/browser/api/electron_api_session.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_SESSION_H_ -#define SHELL_BROWSER_API_ATOM_API_SESSION_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_SESSION_H_ +#define SHELL_BROWSER_API_ELECTRON_API_SESSION_H_ #include #include @@ -16,6 +16,10 @@ #include "shell/browser/net/resolve_proxy_helper.h" #include "shell/common/promise_util.h" +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "chrome/browser/spellchecker/spellcheck_hunspell_dictionary.h" // nogncheck +#endif + class GURL; namespace base { @@ -33,16 +37,20 @@ class ProxyConfig; namespace electron { -class AtomBrowserContext; +class ElectronBrowserContext; namespace api { class Session : public mate::TrackableObject, +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + public SpellcheckHunspellDictionary::Observer, +#endif public content::DownloadManager::Observer { public: // Gets or creates Session from the |browser_context|. - static mate::Handle CreateFrom(v8::Isolate* isolate, - AtomBrowserContext* browser_context); + static mate::Handle CreateFrom( + v8::Isolate* isolate, + ElectronBrowserContext* browser_context); // Gets the Session of |partition|. static mate::Handle FromPartition( @@ -50,7 +58,9 @@ class Session : public mate::TrackableObject, const std::string& partition, const base::DictionaryValue& options = base::DictionaryValue()); - AtomBrowserContext* browser_context() const { return browser_context_.get(); } + ElectronBrowserContext* browser_context() const { + return browser_context_.get(); + } // mate::TrackableObject: static void BuildPrototype(v8::Isolate* isolate, @@ -87,19 +97,35 @@ class Session : public mate::TrackableObject, v8::Local WebRequest(v8::Isolate* isolate); v8::Local NetLog(v8::Isolate* isolate); void Preconnect(const mate::Dictionary& options, mate::Arguments* args); +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + base::Value GetSpellCheckerLanguages(); + void SetSpellCheckerLanguages(gin_helper::ErrorThrower thrower, + const std::vector& languages); + bool AddWordToSpellCheckerDictionary(const std::string& word); +#endif #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) void LoadChromeExtension(const base::FilePath extension_path); #endif protected: - Session(v8::Isolate* isolate, AtomBrowserContext* browser_context); + Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context); ~Session() override; // content::DownloadManager::Observer: void OnDownloadCreated(content::DownloadManager* manager, download::DownloadItem* item) override; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + // SpellcheckHunspellDictionary::Observer + void OnHunspellDictionaryInitialized(const std::string& language) override; + void OnHunspellDictionaryDownloadBegin(const std::string& language) override; + void OnHunspellDictionaryDownloadSuccess( + const std::string& language) override; + void OnHunspellDictionaryDownloadFailure( + const std::string& language) override; +#endif + private: // Cached mate::Wrappable objects. v8::Global cookies_; @@ -112,7 +138,7 @@ class Session : public mate::TrackableObject, // The client id to enable the network throttler. base::UnguessableToken network_emulation_token_; - scoped_refptr browser_context_; + scoped_refptr browser_context_; DISALLOW_COPY_AND_ASSIGN(Session); }; @@ -121,4 +147,4 @@ class Session : public mate::TrackableObject, } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_SESSION_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_SESSION_H_ diff --git a/shell/browser/api/atom_api_system_preferences.cc b/shell/browser/api/electron_api_system_preferences.cc similarity index 98% rename from shell/browser/api/atom_api_system_preferences.cc rename to shell/browser/api/electron_api_system_preferences.cc index 1f98956c35ac0..ec8c0b296a5ff 100644 --- a/shell/browser/api/atom_api_system_preferences.cc +++ b/shell/browser/api/electron_api_system_preferences.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_system_preferences.h" +#include "shell/browser/api/electron_api_system_preferences.h" #include "native_mate/dictionary.h" #include "shell/common/gin_helper/dictionary.h" diff --git a/shell/browser/api/atom_api_system_preferences.h b/shell/browser/api/electron_api_system_preferences.h similarity index 96% rename from shell/browser/api/atom_api_system_preferences.h rename to shell/browser/api/electron_api_system_preferences.h index 6a00ad82869e2..4e71ea7dd0daf 100644 --- a/shell/browser/api/atom_api_system_preferences.h +++ b/shell/browser/api/electron_api_system_preferences.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_SYSTEM_PREFERENCES_H_ -#define SHELL_BROWSER_API_ATOM_API_SYSTEM_PREFERENCES_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_SYSTEM_PREFERENCES_H_ +#define SHELL_BROWSER_API_ELECTRON_API_SYSTEM_PREFERENCES_H_ #include #include @@ -22,10 +22,6 @@ #include "ui/gfx/sys_color_change_listener.h" #endif -namespace base { -class DictionaryValue; -} - namespace electron { namespace api { @@ -173,4 +169,4 @@ class SystemPreferences : public mate::EventEmitter } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_SYSTEM_PREFERENCES_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_SYSTEM_PREFERENCES_H_ diff --git a/shell/browser/api/atom_api_system_preferences_mac.mm b/shell/browser/api/electron_api_system_preferences_mac.mm similarity index 95% rename from shell/browser/api/atom_api_system_preferences_mac.mm rename to shell/browser/api/electron_api_system_preferences_mac.mm index b203587c327e7..f1c738942176a 100644 --- a/shell/browser/api/atom_api_system_preferences_mac.mm +++ b/shell/browser/api/electron_api_system_preferences_mac.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_system_preferences.h" +#include "shell/browser/api/electron_api_system_preferences.h" #include #include @@ -21,10 +21,11 @@ #include "base/strings/sys_string_conversions.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/values.h" +#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h" #include "native_mate/object_template_builder_deprecated.h" #include "net/base/mac/url_conversions.h" -#include "shell/browser/mac/atom_application.h" #include "shell/browser/mac/dict_util.h" +#include "shell/browser/mac/electron_application.h" #include "shell/browser/ui/cocoa/NSColor+Hex.h" #include "shell/common/deprecate_util.h" #include "shell/common/native_mate_converters/gurl_converter.h" @@ -102,15 +103,17 @@ AVMediaType ParseMediaType(const std::string& media_type) { } } -std::string ConvertAuthorizationStatus(AVAuthorizationStatusMac status) { - switch (status) { - case AVAuthorizationStatusNotDeterminedMac: +std::string ConvertSystemPermission( + system_media_permissions::SystemPermission value) { + using SystemPermission = system_media_permissions::SystemPermission; + switch (value) { + case SystemPermission::kNotDetermined: return "not-determined"; - case AVAuthorizationStatusRestrictedMac: + case SystemPermission::kRestricted: return "restricted"; - case AVAuthorizationStatusDeniedMac: + case SystemPermission::kDenied: return "denied"; - case AVAuthorizationStatusAuthorizedMac: + case SystemPermission::kAllowed: return "granted"; default: return "unknown"; @@ -597,14 +600,15 @@ AVMediaType ParseMediaType(const std::string& media_type) { std::string SystemPreferences::GetMediaAccessStatus( const std::string& media_type, mate::Arguments* args) { - if (auto type = ParseMediaType(media_type)) { - if (@available(macOS 10.14, *)) { - return ConvertAuthorizationStatus( - [AVCaptureDevice authorizationStatusForMediaType:type]); - } else { - // access always allowed pre-10.14 Mojave - return ConvertAuthorizationStatus(AVAuthorizationStatusAuthorizedMac); - } + if (media_type == "camera") { + return ConvertSystemPermission( + system_media_permissions::CheckSystemVideoCapturePermission()); + } else if (media_type == "microphone") { + return ConvertSystemPermission( + system_media_permissions::CheckSystemAudioCapturePermission()); + } else if (media_type == "screen") { + return ConvertSystemPermission( + system_media_permissions::CheckSystemScreenCapturePermission()); } else { args->ThrowError("Invalid media type"); return std::string(); diff --git a/shell/browser/api/atom_api_system_preferences_win.cc b/shell/browser/api/electron_api_system_preferences_win.cc similarity index 99% rename from shell/browser/api/atom_api_system_preferences_win.cc rename to shell/browser/api/electron_api_system_preferences_win.cc index 5a991a701c879..4ab67b9e46070 100644 --- a/shell/browser/api/atom_api_system_preferences_win.cc +++ b/shell/browser/api/electron_api_system_preferences_win.cc @@ -5,7 +5,7 @@ #include #include -#include "shell/browser/api/atom_api_system_preferences.h" +#include "shell/browser/api/electron_api_system_preferences.h" #include "base/win/wrapped_window_proc.h" #include "shell/common/color_util.h" diff --git a/shell/browser/api/atom_api_top_level_window.cc b/shell/browser/api/electron_api_top_level_window.cc similarity index 96% rename from shell/browser/api/atom_api_top_level_window.cc rename to shell/browser/api/electron_api_top_level_window.cc index 62d488607428d..312eb7091d07d 100644 --- a/shell/browser/api/atom_api_top_level_window.cc +++ b/shell/browser/api/electron_api_top_level_window.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_top_level_window.h" +#include "shell/browser/api/electron_api_top_level_window.h" #include #include @@ -10,11 +10,12 @@ #include "electron/buildflags/buildflags.h" #include "gin/dictionary.h" #include "native_mate/persistent_dictionary.h" -#include "shell/browser/api/atom_api_browser_view.h" -#include "shell/browser/api/atom_api_menu.h" -#include "shell/browser/api/atom_api_view.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_browser_view.h" +#include "shell/browser/api/electron_api_menu.h" +#include "shell/browser/api/electron_api_view.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/common/color_util.h" +#include "shell/common/deprecate_util.h" #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_converters/gfx_converter.h" @@ -83,11 +84,11 @@ TopLevelWindow::TopLevelWindow(v8::Isolate* isolate, #if BUILDFLAG(ENABLE_OSR) // Offscreen windows are always created frameless. - gin_helper::Dictionary web_preferences; + mate::Dictionary web_preferences; bool offscreen; if (options.Get(options::kWebPreferences, &web_preferences) && web_preferences.Get(options::kOffscreen, &offscreen) && offscreen) { - const_cast(options).Set(options::kFrame, false); + const_cast(options).Set(options::kFrame, false); } #endif @@ -682,10 +683,16 @@ void TopLevelWindow::SetMenu(v8::Isolate* isolate, v8::Local value) { gin::V8ToString(isolate, object->GetConstructorName()) == "Menu" && gin::ConvertFromV8(isolate, value, &menu) && !menu.IsEmpty()) { menu_.Reset(isolate, menu.ToV8()); - window_->SetMenu(menu->model()); + + // We only want to update the menu if the menu has a non-zero item count, + // or we risk crashes. + if (menu->model()->GetItemCount() == 0) { + RemoveMenu(); + } else { + window_->SetMenu(menu->model()); + } } else if (value->IsNull()) { - menu_.Reset(); - window_->SetMenu(nullptr); + RemoveMenu(); } else { isolate->ThrowException( v8::Exception::TypeError(gin::StringToV8(isolate, "Invalid Menu"))); @@ -794,6 +801,13 @@ void TopLevelWindow::SetVisibleOnAllWorkspaces(bool visible, bool visibleOnFullScreen = false; args->GetNext(&options) && options.Get("visibleOnFullScreen", &visibleOnFullScreen); + if (visibleOnFullScreen) { + node::Environment* env = node::Environment::GetCurrent(args->isolate()); + EmitDeprecationWarning( + env, "visibleOnFullScreen is deprecated and will be removed", + "electron"); + } + return window_->SetVisibleOnAllWorkspaces(visible, visibleOnFullScreen); } @@ -811,6 +825,16 @@ void TopLevelWindow::SetVibrancy(v8::Isolate* isolate, window_->SetVibrancy(type); } +#if defined(OS_MACOSX) +void TopLevelWindow::SetTrafficLightPosition(const gfx::Point& position) { + window_->SetTrafficLightPosition(position); +} + +gfx::Point TopLevelWindow::GetTrafficLightPosition() const { + return window_->GetTrafficLightPosition(); +} +#endif + void TopLevelWindow::SetTouchBar( const std::vector& items) { window_->SetTouchBar(items); @@ -1179,6 +1203,12 @@ void TopLevelWindow::BuildPrototype(v8::Isolate* isolate, .SetMethod("setAutoHideCursor", &TopLevelWindow::SetAutoHideCursor) #endif .SetMethod("setVibrancy", &TopLevelWindow::SetVibrancy) +#if defined(OS_MACOSX) + .SetMethod("setTrafficLightPosition", + &TopLevelWindow::SetTrafficLightPosition) + .SetMethod("getTrafficLightPosition", + &TopLevelWindow::GetTrafficLightPosition) +#endif .SetMethod("_setTouchBarItems", &TopLevelWindow::SetTouchBar) .SetMethod("_refreshTouchBarItem", &TopLevelWindow::RefreshTouchBarItem) .SetMethod("_setEscapeTouchBarItem", diff --git a/shell/browser/api/atom_api_top_level_window.h b/shell/browser/api/electron_api_top_level_window.h similarity index 96% rename from shell/browser/api/atom_api_top_level_window.h rename to shell/browser/api/electron_api_top_level_window.h index e60d25eb2e34e..c8fb031dce1a8 100644 --- a/shell/browser/api/atom_api_top_level_window.h +++ b/shell/browser/api/electron_api_top_level_window.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_TOP_LEVEL_WINDOW_H_ -#define SHELL_BROWSER_API_ATOM_API_TOP_LEVEL_WINDOW_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_TOP_LEVEL_WINDOW_H_ +#define SHELL_BROWSER_API_ELECTRON_API_TOP_LEVEL_WINDOW_H_ #include #include @@ -16,7 +16,7 @@ #include "shell/browser/api/trackable_object.h" #include "shell/browser/native_window.h" #include "shell/browser/native_window_observer.h" -#include "shell/common/api/atom_api_native_image.h" +#include "shell/common/api/electron_api_native_image.h" #include "shell/common/gin_helper/event_emitter.h" namespace electron { @@ -186,6 +186,12 @@ class TopLevelWindow bool IsVisibleOnAllWorkspaces(); void SetAutoHideCursor(bool auto_hide); virtual void SetVibrancy(v8::Isolate* isolate, v8::Local value); + +#if defined(OS_MACOSX) + void SetTrafficLightPosition(const gfx::Point& position); + gfx::Point GetTrafficLightPosition() const; +#endif + void SetTouchBar(const std::vector& items); void RefreshTouchBarItem(const std::string& item_id); void SetEscapeTouchBarItem(const mate::PersistentDictionary& item); @@ -267,4 +273,4 @@ class TopLevelWindow } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_TOP_LEVEL_WINDOW_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_TOP_LEVEL_WINDOW_H_ diff --git a/shell/browser/api/atom_api_tray.cc b/shell/browser/api/electron_api_tray.cc similarity index 98% rename from shell/browser/api/atom_api_tray.cc rename to shell/browser/api/electron_api_tray.cc index f0999d3c52dd5..776346e4650ad 100644 --- a/shell/browser/api/atom_api_tray.cc +++ b/shell/browser/api/electron_api_tray.cc @@ -2,14 +2,14 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_tray.h" +#include "shell/browser/api/electron_api_tray.h" #include #include "base/threading/thread_task_runner_handle.h" -#include "shell/browser/api/atom_api_menu.h" +#include "shell/browser/api/electron_api_menu.h" #include "shell/browser/browser.h" -#include "shell/common/api/atom_api_native_image.h" +#include "shell/common/api/electron_api_native_image.h" #include "shell/common/gin_converters/gfx_converter.h" #include "shell/common/gin_converters/image_converter.h" #include "shell/common/gin_helper/dictionary.h" diff --git a/shell/browser/api/atom_api_tray.h b/shell/browser/api/electron_api_tray.h similarity index 95% rename from shell/browser/api/atom_api_tray.h rename to shell/browser/api/electron_api_tray.h index 748d1916a51fc..3ba514b2002e7 100644 --- a/shell/browser/api/atom_api_tray.h +++ b/shell/browser/api/electron_api_tray.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_TRAY_H_ -#define SHELL_BROWSER_API_ATOM_API_TRAY_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_TRAY_H_ +#define SHELL_BROWSER_API_ELECTRON_API_TRAY_H_ #include #include @@ -95,4 +95,4 @@ class Tray : public mate::TrackableObject< } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_TRAY_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_TRAY_H_ diff --git a/shell/browser/api/electron_api_url_loader.cc b/shell/browser/api/electron_api_url_loader.cc new file mode 100644 index 0000000000000..bb188a24c3e38 --- /dev/null +++ b/shell/browser/api/electron_api_url_loader.cc @@ -0,0 +1,483 @@ +// Copyright (c) 2019 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/api/electron_api_url_loader.h" + +#include +#include +#include +#include +#include +#include + +#include "base/containers/id_map.h" +#include "gin/handle.h" +#include "gin/object_template_builder.h" +#include "gin/wrappable.h" +#include "mojo/public/cpp/system/data_pipe_producer.h" +#include "net/base/load_flags.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "services/network/public/mojom/chunked_data_pipe_getter.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "shell/browser/api/electron_api_session.h" +#include "shell/browser/electron_browser_context.h" +#include "shell/common/gin_converters/callback_converter.h" +#include "shell/common/gin_converters/gurl_converter.h" +#include "shell/common/gin_converters/net_converter.h" +#include "shell/common/gin_helper/dictionary.h" +#include "shell/common/gin_helper/object_template_builder.h" +#include "shell/common/node_includes.h" + +namespace gin { + +template <> +struct Converter { + static v8::Local ToV8( + v8::Isolate* isolate, + const network::mojom::HttpRawHeaderPairPtr& pair) { + gin_helper::Dictionary dict = gin::Dictionary::CreateEmpty(isolate); + dict.Set("key", base::ToLowerASCII(pair->key)); + dict.Set("value", pair->value); + return dict.GetHandle(); + } +}; + +} // namespace gin + +namespace electron { + +namespace api { + +namespace { + +class BufferDataSource : public mojo::DataPipeProducer::DataSource { + public: + explicit BufferDataSource(base::span buffer) { + buffer_.resize(buffer.size()); + memcpy(buffer_.data(), buffer.data(), buffer_.size()); + } + ~BufferDataSource() override = default; + + private: + // mojo::DataPipeProducer::DataSource: + uint64_t GetLength() const override { return buffer_.size(); } + ReadResult Read(uint64_t offset, base::span buffer) override { + ReadResult result; + if (offset <= buffer_.size()) { + size_t readable_size = buffer_.size() - offset; + size_t writable_size = buffer.size(); + size_t copyable_size = std::min(readable_size, writable_size); + if (copyable_size > 0) { + memcpy(buffer.data(), &buffer_[offset], copyable_size); + } + result.bytes_read = copyable_size; + } else { + NOTREACHED(); + result.result = MOJO_RESULT_OUT_OF_RANGE; + } + return result; + } + + std::vector buffer_; +}; + +class JSChunkedDataPipeGetter : public gin::Wrappable, + public network::mojom::ChunkedDataPipeGetter { + public: + static gin::Handle Create( + v8::Isolate* isolate, + v8::Local body_func, + mojo::PendingReceiver + chunked_data_pipe_getter) { + return gin::CreateHandle( + isolate, new JSChunkedDataPipeGetter( + isolate, body_func, std::move(chunked_data_pipe_getter))); + } + + // gin::Wrappable + gin::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override { + return gin::Wrappable::GetObjectTemplateBuilder( + isolate) + .SetMethod("write", &JSChunkedDataPipeGetter::WriteChunk) + .SetMethod("done", &JSChunkedDataPipeGetter::Done); + } + + static gin::WrapperInfo kWrapperInfo; + ~JSChunkedDataPipeGetter() override = default; + + private: + JSChunkedDataPipeGetter( + v8::Isolate* isolate, + v8::Local body_func, + mojo::PendingReceiver + chunked_data_pipe_getter) + : isolate_(isolate), body_func_(isolate, body_func) { + receiver_.Bind(std::move(chunked_data_pipe_getter)); + } + + // network::mojom::ChunkedDataPipeGetter: + void GetSize(GetSizeCallback callback) override { + size_callback_ = std::move(callback); + } + + void StartReading(mojo::ScopedDataPipeProducerHandle pipe) override { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + if (body_func_.IsEmpty()) { + LOG(ERROR) << "Tried to read twice from a JSChunkedDataPipeGetter"; + // Drop the handle on the floor. + return; + } + data_producer_ = std::make_unique(std::move(pipe)); + + v8::HandleScope handle_scope(isolate_); + v8::MicrotasksScope script_scope(isolate_, + v8::MicrotasksScope::kRunMicrotasks); + auto maybe_wrapper = GetWrapper(isolate_); + v8::Local wrapper; + if (!maybe_wrapper.ToLocal(&wrapper)) { + return; + } + v8::Local argv[] = {wrapper}; + node::Environment* env = node::Environment::GetCurrent(isolate_); + auto global = env->context()->Global(); + node::MakeCallback(isolate_, global, body_func_.Get(isolate_), + node::arraysize(argv), argv, {0, 0}); + } + + v8::Local WriteChunk(v8::Local buffer_val) { + electron::util::Promise promise(isolate_); + v8::Local handle = promise.GetHandle(); + if (!buffer_val->IsArrayBufferView()) { + promise.RejectWithErrorMessage("Expected an ArrayBufferView"); + return handle; + } + if (is_writing_) { + promise.RejectWithErrorMessage("Only one write can be pending at a time"); + return handle; + } + if (!size_callback_) { + promise.RejectWithErrorMessage("Can't write after calling done()"); + return handle; + } + auto buffer = buffer_val.As(); + is_writing_ = true; + bytes_written_ += buffer->ByteLength(); + auto backing_store = buffer->Buffer()->GetBackingStore(); + auto buffer_span = base::make_span( + static_cast(backing_store->Data()) + buffer->ByteOffset(), + buffer->ByteLength()); + auto buffer_source = std::make_unique(buffer_span); + data_producer_->Write( + std::move(buffer_source), + base::BindOnce(&JSChunkedDataPipeGetter::OnWriteChunkComplete, + // We're OK to use Unretained here because we own + // |data_producer_|. + base::Unretained(this), std::move(promise))); + return handle; + } + + void OnWriteChunkComplete(electron::util::Promise promise, + MojoResult result) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + is_writing_ = false; + if (result == MOJO_RESULT_OK) { + electron::util::Promise::ResolveEmptyPromise(std::move(promise)); + } else { + promise.RejectWithErrorMessage("mojo result not ok"); + Finished(); + } + } + + // TODO(nornagon): accept a net error here to allow the data provider to + // cancel the request with an error. + void Done() { + if (size_callback_) { + std::move(size_callback_).Run(net::OK, bytes_written_); + Finished(); + } + } + + void Finished() { + size_callback_.Reset(); + body_func_.Reset(); + receiver_.reset(); + data_producer_.reset(); + } + + GetSizeCallback size_callback_; + mojo::Receiver receiver_{this}; + std::unique_ptr data_producer_; + bool is_writing_ = false; + uint64_t bytes_written_ = 0; + + v8::Isolate* isolate_; + v8::Global body_func_; +}; + +gin::WrapperInfo JSChunkedDataPipeGetter::kWrapperInfo = { + gin::kEmbedderNativeGin}; + +const net::NetworkTrafficAnnotationTag kTrafficAnnotation = + net::DefineNetworkTrafficAnnotation("electron_net_module", R"( + semantics { + sender: "Electron Net module" + description: + "Issue HTTP/HTTPS requests using Chromium's native networking " + "library." + trigger: "Using the Net module" + data: "Anything the user wants to send." + destination: OTHER + } + policy { + cookies_allowed: YES + cookies_store: "user" + setting: "This feature cannot be disabled." + })"); + +base::IDMap& GetAllRequests() { + static base::NoDestructor> + s_all_requests; + return *s_all_requests; +} + +} // namespace + +SimpleURLLoaderWrapper::SimpleURLLoaderWrapper( + std::unique_ptr request, + network::mojom::URLLoaderFactory* url_loader_factory) + : id_(GetAllRequests().Add(this)) { + // We slightly abuse the |render_frame_id| field in ResourceRequest so that + // we can correlate any authentication events that arrive with this request. + request->render_frame_id = id_; + + // SimpleURLLoader wants to control the request body itself. We have other + // ideas. + auto request_body = std::move(request->request_body); + auto* request_ref = request.get(); + loader_ = + network::SimpleURLLoader::Create(std::move(request), kTrafficAnnotation); + if (request_body) { + request_ref->request_body = std::move(request_body); + } + + loader_->SetAllowHttpErrorResults(true); + loader_->SetOnResponseStartedCallback(base::BindOnce( + &SimpleURLLoaderWrapper::OnResponseStarted, base::Unretained(this))); + loader_->SetOnRedirectCallback(base::BindRepeating( + &SimpleURLLoaderWrapper::OnRedirect, base::Unretained(this))); + loader_->SetOnUploadProgressCallback(base::BindRepeating( + &SimpleURLLoaderWrapper::OnUploadProgress, base::Unretained(this))); + loader_->SetOnDownloadProgressCallback(base::BindRepeating( + &SimpleURLLoaderWrapper::OnDownloadProgress, base::Unretained(this))); + + loader_->DownloadAsStream(url_loader_factory, this); +} + +void SimpleURLLoaderWrapper::Pin() { + // Prevent ourselves from being GC'd until the request is complete. + // Must be called after InitWithArgs(), otherwise GetWrapper() isn't + // initialized. + pinned_wrapper_.Reset(isolate(), GetWrapper()); +} + +void SimpleURLLoaderWrapper::PinBodyGetter(v8::Local body_getter) { + pinned_chunk_pipe_getter_.Reset(isolate(), body_getter); +} + +SimpleURLLoaderWrapper::~SimpleURLLoaderWrapper() { + GetAllRequests().Remove(id_); +} + +// static +SimpleURLLoaderWrapper* SimpleURLLoaderWrapper::FromID(uint32_t id) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + return GetAllRequests().Lookup(id); +} + +void SimpleURLLoaderWrapper::OnAuthRequired( + const GURL& url, + bool first_auth_attempt, + net::AuthChallengeInfo auth_info, + network::mojom::URLResponseHeadPtr head, + mojo::PendingRemote + auth_challenge_responder) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + mojo::Remote auth_responder( + std::move(auth_challenge_responder)); + // WeakPtr because if we're Cancel()ed while waiting for auth, and the + // network service also decides to cancel at the same time and kill this + // pipe, we might end up trying to call Cancel again on dead memory. + auth_responder.set_disconnect_handler(base::BindOnce( + &SimpleURLLoaderWrapper::Cancel, weak_factory_.GetWeakPtr())); + auto cb = base::BindOnce( + [](mojo::Remote auth_responder, + gin::Arguments* args) { + base::string16 username_str, password_str; + if (!args->GetNext(&username_str) || !args->GetNext(&password_str)) { + auth_responder->OnAuthCredentials(base::nullopt); + return; + } + auth_responder->OnAuthCredentials( + net::AuthCredentials(username_str, password_str)); + }, + std::move(auth_responder)); + Emit("login", auth_info, base::AdaptCallbackForRepeating(std::move(cb))); +} + +void SimpleURLLoaderWrapper::Cancel() { + loader_.reset(); + pinned_wrapper_.Reset(); + pinned_chunk_pipe_getter_.Reset(); + // This ensures that no further callbacks will be called, so there's no need + // for additional guards. +} + +// static +mate::WrappableBase* SimpleURLLoaderWrapper::New(gin::Arguments* args) { + gin_helper::Dictionary opts; + if (!args->GetNext(&opts)) { + args->ThrowTypeError("Expected a dictionary"); + return nullptr; + } + auto request = std::make_unique(); + request->attach_same_site_cookies = true; + opts.Get("method", &request->method); + opts.Get("url", &request->url); + opts.Get("referrer", &request->referrer); + std::map extra_headers; + if (opts.Get("extraHeaders", &extra_headers)) { + for (const auto& it : extra_headers) { + if (!net::HttpUtil::IsValidHeaderName(it.first) || + !net::HttpUtil::IsValidHeaderValue(it.second)) { + args->ThrowTypeError("Invalid header name or value"); + return nullptr; + } + request->headers.SetHeader(it.first, it.second); + } + } + + bool use_session_cookies = false; + opts.Get("useSessionCookies", &use_session_cookies); + if (!use_session_cookies) { + request->load_flags |= net::LOAD_DO_NOT_SEND_COOKIES; + } + + // Chromium filters headers using browser rules, while for net module we have + // every header passed. + request->report_raw_headers = true; + + v8::Local body; + v8::Local chunk_pipe_getter; + if (opts.Get("body", &body)) { + if (body->IsArrayBufferView()) { + auto buffer_body = body.As(); + auto backing_store = buffer_body->Buffer()->GetBackingStore(); + request->request_body = network::ResourceRequestBody::CreateFromBytes( + static_cast(backing_store->Data()) + buffer_body->ByteOffset(), + buffer_body->ByteLength()); + } else if (body->IsFunction()) { + auto body_func = body.As(); + + mojo::PendingRemote + data_pipe_getter; + chunk_pipe_getter = JSChunkedDataPipeGetter::Create( + args->isolate(), body_func, + data_pipe_getter.InitWithNewPipeAndPassReceiver()) + .ToV8(); + request->request_body = new network::ResourceRequestBody(); + request->request_body->SetToChunkedDataPipe(std::move(data_pipe_getter)); + } + } + + std::string partition; + mate::Handle session; + if (!opts.Get("session", &session)) { + if (opts.Get("partition", &partition)) + session = Session::FromPartition(args->isolate(), partition); + else // default session + session = Session::FromPartition(args->isolate(), ""); + } + + auto url_loader_factory = session->browser_context()->GetURLLoaderFactory(); + + auto* ret = + new SimpleURLLoaderWrapper(std::move(request), url_loader_factory.get()); + ret->InitWithArgs(args); + ret->Pin(); + if (!chunk_pipe_getter.IsEmpty()) { + ret->PinBodyGetter(chunk_pipe_getter); + } + return ret; +} + +void SimpleURLLoaderWrapper::OnDataReceived(base::StringPiece string_piece, + base::OnceClosure resume) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + v8::HandleScope handle_scope(isolate()); + auto array_buffer = v8::ArrayBuffer::New(isolate(), string_piece.size()); + auto backing_store = array_buffer->GetBackingStore(); + memcpy(backing_store->Data(), string_piece.data(), string_piece.size()); + Emit("data", array_buffer); + std::move(resume).Run(); +} + +void SimpleURLLoaderWrapper::OnComplete(bool success) { + if (success) { + Emit("complete"); + } else { + Emit("error", net::ErrorToString(loader_->NetError())); + } + loader_.reset(); + pinned_wrapper_.Reset(); + pinned_chunk_pipe_getter_.Reset(); +} + +void SimpleURLLoaderWrapper::OnRetry(base::OnceClosure start_retry) {} + +void SimpleURLLoaderWrapper::OnResponseStarted( + const GURL& final_url, + const network::mojom::URLResponseHead& response_head) { + gin::Dictionary dict = gin::Dictionary::CreateEmpty(isolate()); + dict.Set("statusCode", response_head.headers->response_code()); + dict.Set("statusMessage", response_head.headers->GetStatusText()); + dict.Set("httpVersion", response_head.headers->GetHttpVersion()); + // Note that |response_head.headers| are filtered by Chromium and should not + // be used here. + DCHECK(response_head.raw_request_response_info); + dict.Set("rawHeaders", + response_head.raw_request_response_info->response_headers); + Emit("response-started", final_url, dict); +} + +void SimpleURLLoaderWrapper::OnRedirect( + const net::RedirectInfo& redirect_info, + const network::mojom::URLResponseHead& response_head, + std::vector* removed_headers) { + Emit("redirect", redirect_info, response_head.headers.get()); +} + +void SimpleURLLoaderWrapper::OnUploadProgress(uint64_t position, + uint64_t total) { + Emit("upload-progress", position, total); +} + +void SimpleURLLoaderWrapper::OnDownloadProgress(uint64_t current) { + Emit("download-progress", current); +} + +// static +void SimpleURLLoaderWrapper::BuildPrototype( + v8::Isolate* isolate, + v8::Local prototype) { + prototype->SetClassName(gin::StringToV8(isolate, "SimpleURLLoaderWrapper")); + gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) + .SetMethod("cancel", &SimpleURLLoaderWrapper::Cancel); +} + +} // namespace api + +} // namespace electron diff --git a/shell/browser/api/electron_api_url_loader.h b/shell/browser/api/electron_api_url_loader.h new file mode 100644 index 0000000000000..6d9769d75cfff --- /dev/null +++ b/shell/browser/api/electron_api_url_loader.h @@ -0,0 +1,94 @@ +// Copyright (c) 2019 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_BROWSER_API_ELECTRON_API_URL_LOADER_H_ +#define SHELL_BROWSER_API_ELECTRON_API_URL_LOADER_H_ + +#include +#include +#include + +#include "base/memory/weak_ptr.h" +#include "native_mate/wrappable.h" +#include "net/base/auth.h" +#include "services/network/public/cpp/simple_url_loader_stream_consumer.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom-forward.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "shell/common/gin_helper/event_emitter.h" +#include "url/gurl.h" +#include "v8/include/v8.h" + +namespace gin { +class Arguments; +} + +namespace network { +class SimpleURLLoader; +struct ResourceRequest; +} // namespace network + +namespace electron { + +namespace api { + +/** Wraps a SimpleURLLoader to make it usable from JavaScript */ +class SimpleURLLoaderWrapper + : public gin_helper::EventEmitter>, + public network::SimpleURLLoaderStreamConsumer { + public: + ~SimpleURLLoaderWrapper() override; + static mate::WrappableBase* New(gin::Arguments* args); + + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + + static SimpleURLLoaderWrapper* FromID(uint32_t id); + + void OnAuthRequired( + const GURL& url, + bool first_auth_attempt, + net::AuthChallengeInfo auth_info, + network::mojom::URLResponseHeadPtr head, + mojo::PendingRemote + auth_challenge_responder); + + void Cancel(); + + private: + SimpleURLLoaderWrapper(std::unique_ptr loader, + network::mojom::URLLoaderFactory* url_loader_factory); + + // SimpleURLLoaderStreamConsumer: + void OnDataReceived(base::StringPiece string_piece, + base::OnceClosure resume) override; + void OnComplete(bool success) override; + void OnRetry(base::OnceClosure start_retry) override; + + // SimpleURLLoader callbacks + void OnResponseStarted(const GURL& final_url, + const network::mojom::URLResponseHead& response_head); + void OnRedirect(const net::RedirectInfo& redirect_info, + const network::mojom::URLResponseHead& response_head, + std::vector* removed_headers); + void OnUploadProgress(uint64_t position, uint64_t total); + void OnDownloadProgress(uint64_t current); + + void Start(); + void Pin(); + void PinBodyGetter(v8::Local); + + uint32_t id_; + std::unique_ptr loader_; + v8::Global pinned_wrapper_; + v8::Global pinned_chunk_pipe_getter_; + + base::WeakPtrFactory weak_factory_{this}; +}; + +} // namespace api + +} // namespace electron + +#endif // SHELL_BROWSER_API_ELECTRON_API_URL_LOADER_H_ diff --git a/shell/browser/api/atom_api_view.cc b/shell/browser/api/electron_api_view.cc similarity index 97% rename from shell/browser/api/atom_api_view.cc rename to shell/browser/api/electron_api_view.cc index 78d5938f6f84a..27260d04c701b 100644 --- a/shell/browser/api/atom_api_view.cc +++ b/shell/browser/api/electron_api_view.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_view.h" +#include "shell/browser/api/electron_api_view.h" #include "native_mate/dictionary.h" #include "shell/common/node_includes.h" diff --git a/shell/browser/api/atom_api_view.h b/shell/browser/api/electron_api_view.h similarity index 88% rename from shell/browser/api/atom_api_view.h rename to shell/browser/api/electron_api_view.h index b0f4cd4f77eb6..a19c0691ccd0b 100644 --- a/shell/browser/api/atom_api_view.h +++ b/shell/browser/api/electron_api_view.h @@ -2,15 +2,15 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_VIEW_H_ -#define SHELL_BROWSER_API_ATOM_API_VIEW_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_VIEW_H_ +#define SHELL_BROWSER_API_ELECTRON_API_VIEW_H_ #include #include #include "electron/buildflags/buildflags.h" #include "native_mate/handle.h" -#include "shell/browser/api/views/atom_api_layout_manager.h" +#include "shell/browser/api/views/electron_api_layout_manager.h" #include "ui/views/view.h" namespace electron { @@ -71,4 +71,4 @@ struct Converter { } // namespace mate -#endif // SHELL_BROWSER_API_ATOM_API_VIEW_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_VIEW_H_ diff --git a/shell/browser/api/atom_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc similarity index 88% rename from shell/browser/api/atom_api_web_contents.cc rename to shell/browser/api/electron_api_web_contents.cc index f7e16ffafb2d5..3731ffe4b4d36 100644 --- a/shell/browser/api/atom_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -2,8 +2,9 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" +#include #include #include #include @@ -44,22 +45,23 @@ #include "content/public/common/context_menu_params.h" #include "electron/buildflags/buildflags.h" #include "electron/shell/common/api/api.mojom.h" +#include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/system/platform_handle.h" #include "native_mate/converter.h" #include "native_mate/dictionary.h" #include "native_mate/object_template_builder_deprecated.h" #include "ppapi/buildflags/buildflags.h" -#include "shell/browser/api/atom_api_browser_window.h" -#include "shell/browser/api/atom_api_debugger.h" -#include "shell/browser/api/atom_api_session.h" -#include "shell/browser/atom_autofill_driver_factory.h" -#include "shell/browser/atom_browser_client.h" -#include "shell/browser/atom_browser_context.h" -#include "shell/browser/atom_browser_main_parts.h" -#include "shell/browser/atom_javascript_dialog_manager.h" -#include "shell/browser/atom_navigation_throttle.h" +#include "shell/browser/api/electron_api_browser_window.h" +#include "shell/browser/api/electron_api_debugger.h" +#include "shell/browser/api/electron_api_session.h" #include "shell/browser/browser.h" #include "shell/browser/child_web_contents_tracker.h" +#include "shell/browser/electron_autofill_driver_factory.h" +#include "shell/browser/electron_browser_client.h" +#include "shell/browser/electron_browser_context.h" +#include "shell/browser/electron_browser_main_parts.h" +#include "shell/browser/electron_javascript_dialog_manager.h" +#include "shell/browser/electron_navigation_throttle.h" #include "shell/browser/lib/bluetooth_chooser.h" #include "shell/browser/native_window.h" #include "shell/browser/session_preferences.h" @@ -70,11 +72,12 @@ #include "shell/browser/web_contents_preferences.h" #include "shell/browser/web_contents_zoom_controller.h" #include "shell/browser/web_view_guest_delegate.h" -#include "shell/common/api/atom_api_native_image.h" +#include "shell/common/api/electron_api_native_image.h" #include "shell/common/color_util.h" #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/gin_converters/gfx_converter.h" #include "shell/common/gin_helper/dictionary.h" +#include "shell/common/language_util.h" #include "shell/common/mouse_util.h" #include "shell/common/native_mate_converters/blink_converter.h" #include "shell/common/native_mate_converters/content_converter.h" @@ -91,6 +94,7 @@ #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" #include "third_party/blink/public/common/page/page_zoom.h" #include "third_party/blink/public/mojom/frame/find_in_page.mojom.h" +#include "third_party/blink/public/mojom/frame/fullscreen.mojom.h" #include "third_party/blink/public/platform/web_cursor_info.h" #include "third_party/blink/public/platform/web_input_event.h" #include "ui/display/screen.h" @@ -116,13 +120,8 @@ #include "ui/gfx/font_render_params.h" #endif -#if BUILDFLAG(ENABLE_PRINTING) -#include "chrome/browser/printing/print_view_manager_basic.h" -#include "components/printing/common/print_messages.h" -#endif - #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) -#include "shell/browser/extensions/atom_extension_web_contents_observer.h" +#include "shell/browser/extensions/electron_extension_web_contents_observer.h" #endif namespace mate { @@ -348,6 +347,37 @@ base::Optional GetCursorBlinkInterval() { return base::nullopt; } +#if BUILDFLAG(ENABLE_PRINTING) +// This will return false if no printer with the provided device_name can be +// found on the network. We need to check this because Chromium does not do +// sanity checking of device_name validity and so will crash on invalid names. +bool IsDeviceNameValid(const base::string16& device_name) { +#if defined(OS_MACOSX) + base::ScopedCFTypeRef new_printer_id( + base::SysUTF16ToCFStringRef(device_name)); + PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get()); + bool printer_exists = new_printer != nullptr; + PMRelease(new_printer); + return printer_exists; +#elif defined(OS_WIN) + printing::ScopedPrinterHandle printer; + return printer.OpenPrinterWithName(device_name.c_str()); +#endif + return true; +} + +base::string16 GetDefaultPrinterAsync() { + base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, + base::BlockingType::MAY_BLOCK); + + scoped_refptr backend = + printing::PrintBackend::CreateInstance( + nullptr, g_browser_process->GetApplicationLocale()); + std::string printer_name = backend->GetDefaultPrinterName(); + return base::UTF8ToUTF16(printer_name); +} +#endif + } // namespace WebContents::WebContents(v8::Isolate* isolate, @@ -400,7 +430,10 @@ WebContents::WebContents(v8::Isolate* isolate, const mate::Dictionary& options) // Whether to enable DevTools. options.Get("devTools", &enable_devtools_); - bool initially_shown = true; + // BrowserViews are not attached to a window initially so they should start + // off as hidden. This is also important for compositor recycling. See: + // https://github.com/electron/electron/pull/21372 + bool initially_shown = type_ != Type::BROWSER_VIEW; options.Get(options::kShow, &initially_shown); // Obtain the session. @@ -488,7 +521,24 @@ void WebContents::InitWithSessionAndOptions( managed_web_contents()->GetView()->SetDelegate(this); auto* prefs = web_contents()->GetMutableRendererPrefs(); - prefs->accept_languages = g_browser_process->GetApplicationLocale(); + + // Collect preferred languages from OS and browser process. + // accept_languages effects HTTP header, navigator.languages, + // and CJK fallback font selection. + // + // Note that an application locale set to the browser + // process might be different with the one set to the preference + // list. (e.g. overridden with --lang) + std::string accept_languages = + g_browser_process->GetApplicationLocale() + ","; + for (auto const& language : electron::GetPreferredLanguages()) { + if (language == g_browser_process->GetApplicationLocale()) { + continue; + } + accept_languages += language + ","; + } + accept_languages.pop_back(); + prefs->accept_languages = accept_languages; #if defined(OS_LINUX) || defined(OS_WIN) // Update font settings. @@ -513,7 +563,7 @@ void WebContents::InitWithSessionAndOptions( SecurityStateTabHelper::CreateForWebContents(web_contents()); InitZoomController(web_contents(), options); #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - extensions::AtomExtensionWebContentsObserver::CreateForWebContents( + extensions::ElectronExtensionWebContentsObserver::CreateForWebContents( web_contents()); #endif @@ -616,6 +666,30 @@ void WebContents::WebContentsCreated(content::WebContents* source_contents, tracker->frame_name = frame_name; } +bool WebContents::IsWebContentsCreationOverridden( + content::SiteInstance* source_site_instance, + content::mojom::WindowContainerType window_container_type, + const GURL& opener_url, + const std::string& frame_name, + const GURL& target_url) { + if (Emit("-will-add-new-contents", target_url, frame_name)) { + return true; + } + return false; +} + +content::WebContents* WebContents::CreateCustomWebContents( + content::RenderFrameHost* opener, + content::SiteInstance* source_site_instance, + bool is_new_browsing_instance, + const GURL& opener_url, + const std::string& frame_name, + const GURL& target_url, + const std::string& partition_id, + content::SessionStorageNamespace* session_storage_namespace) { + return nullptr; +} + void WebContents::AddNewContents( content::WebContents* source, std::unique_ptr new_contents, @@ -733,7 +807,7 @@ void WebContents::ContentsZoomChange(bool zoom_in) { void WebContents::EnterFullscreenModeForTab( content::WebContents* source, const GURL& origin, - const blink::WebFullscreenOptions& options) { + const blink::mojom::FullscreenOptions& options) { auto* permission_helper = WebContentsPermissionHelper::FromWebContents(source); auto callback = @@ -745,7 +819,7 @@ void WebContents::EnterFullscreenModeForTab( void WebContents::OnEnterFullscreenModeForTab( content::WebContents* source, const GURL& origin, - const blink::WebFullscreenOptions& options, + const blink::mojom::FullscreenOptions& options, bool allowed) { if (!allowed) return; @@ -849,7 +923,7 @@ std::unique_ptr WebContents::RunBluetoothChooser( content::JavaScriptDialogManager* WebContents::GetJavaScriptDialogManager( content::WebContents* source) { if (!dialog_manager_) - dialog_manager_ = std::make_unique(this); + dialog_manager_ = std::make_unique(this); return dialog_manager_.get(); } @@ -865,10 +939,20 @@ void WebContents::BeforeUnloadFired(bool proceed, } void WebContents::RenderViewCreated(content::RenderViewHost* render_view_host) { - auto* impl = static_cast( - render_view_host->GetWidget()); - if (impl) - impl->disable_hidden_ = !background_throttling_; + if (!background_throttling_) + render_view_host->SetSchedulerThrottling(false); +} + +void WebContents::RenderFrameCreated( + content::RenderFrameHost* render_frame_host) { + auto* rwhv = render_frame_host->GetView(); + if (!rwhv) + return; + + auto* rwh_impl = + static_cast(rwhv->GetRenderWidgetHost()); + if (rwh_impl) + rwh_impl->disable_hidden_ = !background_throttling_; } void WebContents::RenderViewHostChanged(content::RenderViewHost* old_host, @@ -1288,31 +1372,24 @@ void WebContents::NavigationEntryCommitted( void WebContents::SetBackgroundThrottling(bool allowed) { background_throttling_ = allowed; - auto* contents = web_contents(); - if (!contents) { + auto* rfh = web_contents()->GetMainFrame(); + if (!rfh) return; - } - auto* render_view_host = contents->GetRenderViewHost(); - if (!render_view_host) { + auto* rwhv = rfh->GetView(); + if (!rwhv) return; - } - auto* render_process_host = render_view_host->GetProcess(); - if (!render_process_host) { + auto* rwh_impl = + static_cast(rwhv->GetRenderWidgetHost()); + if (!rwh_impl) return; - } - - auto* render_widget_host_impl = content::RenderWidgetHostImpl::FromID( - render_process_host->GetID(), render_view_host->GetRoutingID()); - if (!render_widget_host_impl) { - return; - } - render_widget_host_impl->disable_hidden_ = !background_throttling_; + rwh_impl->disable_hidden_ = !background_throttling_; + web_contents()->GetRenderViewHost()->SetSchedulerThrottling(allowed); - if (render_widget_host_impl->is_hidden()) { - render_widget_host_impl->WasShown(base::nullopt); + if (rwh_impl->is_hidden()) { + rwh_impl->WasShown(base::nullopt); } } @@ -1384,6 +1461,12 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) { params.load_type = content::NavigationController::LOAD_TYPE_DATA; } + bool reload_ignoring_cache = false; + if (options.Get("reloadIgnoringCache", &reload_ignoring_cache) && + reload_ignoring_cache) { + params.reload_type = content::ReloadType::BYPASSING_CACHE; + } + // Calling LoadURLWithParams() can trigger JS which destroys |this|. auto weak_this = GetWeakPtr(); @@ -1452,17 +1535,17 @@ void WebContents::Stop() { } void WebContents::GoBack() { - electron::AtomBrowserClient::SuppressRendererProcessRestartForOnce(); + electron::ElectronBrowserClient::SuppressRendererProcessRestartForOnce(); web_contents()->GetController().GoBack(); } void WebContents::GoForward() { - electron::AtomBrowserClient::SuppressRendererProcessRestartForOnce(); + electron::ElectronBrowserClient::SuppressRendererProcessRestartForOnce(); web_contents()->GetController().GoForward(); } void WebContents::GoToOffset(int offset) { - electron::AtomBrowserClient::SuppressRendererProcessRestartForOnce(); + electron::ElectronBrowserClient::SuppressRendererProcessRestartForOnce(); web_contents()->GetController().GoToOffset(offset); } @@ -1477,9 +1560,7 @@ void WebContents::SetWebRTCIPHandlingPolicy( web_contents()->GetMutableRendererPrefs()->webrtc_ip_handling_policy = webrtc_ip_handling_policy; - content::RenderViewHost* host = web_contents()->GetRenderViewHost(); - if (host) - host->SyncRendererPrefs(); + web_contents()->SyncRendererPrefs(); } bool WebContents::IsCrashed() const { @@ -1693,9 +1774,46 @@ bool WebContents::IsCurrentlyAudible() { } #if BUILDFLAG(ENABLE_PRINTING) +void WebContents::OnGetDefaultPrinter( + base::Value print_settings, + printing::CompletionCallback print_callback, + base::string16 device_name, + bool silent, + base::string16 default_printer) { + // The content::WebContents might be already deleted at this point, and the + // PrintViewManagerBasic class does not do null check. + if (!web_contents()) { + if (print_callback) + std::move(print_callback).Run(false, "failed"); + return; + } + + base::string16 printer_name = + device_name.empty() ? default_printer : device_name; + + // If there are no valid printers available on the network, we bail. + if (printer_name.empty() || !IsDeviceNameValid(printer_name)) { + if (print_callback) + std::move(print_callback).Run(false, "no valid printers available"); + return; + } + + print_settings.SetStringKey(printing::kSettingDeviceName, printer_name); + + auto* print_view_manager = + printing::PrintViewManagerBasic::FromWebContents(web_contents()); + auto* focused_frame = web_contents()->GetFocusedFrame(); + auto* rfh = focused_frame && focused_frame->HasSelection() + ? focused_frame + : web_contents()->GetMainFrame(); + + print_view_manager->PrintNow(rfh, silent, std::move(print_settings), + std::move(print_callback)); +} + void WebContents::Print(mate::Arguments* args) { mate::Dictionary options = mate::Dictionary::CreateEmpty(args->isolate()); - base::DictionaryValue settings; + base::Value settings(base::Value::Type::DICTIONARY); if (args->Length() >= 1 && !args->GetNext(&options)) { args->ThrowError("webContents.print(): Invalid print settings specified."); @@ -1715,7 +1833,7 @@ void WebContents::Print(mate::Arguments* args) { bool print_background = false; options.Get("printBackground", &print_background); - settings.SetBoolean(printing::kSettingShouldPrintBackgrounds, + settings.SetBoolKey(printing::kSettingShouldPrintBackgrounds, print_background); // Set custom margin settings @@ -1723,62 +1841,68 @@ void WebContents::Print(mate::Arguments* args) { if (options.Get("margins", &margins)) { printing::MarginType margin_type = printing::DEFAULT_MARGINS; margins.Get("marginType", &margin_type); - settings.SetInteger(printing::kSettingMarginsType, margin_type); + settings.SetIntKey(printing::kSettingMarginsType, margin_type); if (margin_type == printing::CUSTOM_MARGINS) { + base::Value custom_margins(base::Value::Type::DICTIONARY); int top = 0; margins.Get("top", &top); - settings.SetInteger(printing::kSettingMarginTop, top); + custom_margins.SetIntKey(printing::kSettingMarginTop, top); int bottom = 0; margins.Get("bottom", &bottom); - settings.SetInteger(printing::kSettingMarginBottom, bottom); + custom_margins.SetIntKey(printing::kSettingMarginBottom, bottom); int left = 0; margins.Get("left", &left); - settings.SetInteger(printing::kSettingMarginLeft, left); + custom_margins.SetIntKey(printing::kSettingMarginLeft, left); int right = 0; margins.Get("right", &right); - settings.SetInteger(printing::kSettingMarginRight, right); + custom_margins.SetIntKey(printing::kSettingMarginRight, right); + settings.SetPath(printing::kSettingMarginsCustom, + std::move(custom_margins)); } } else { - settings.SetInteger(printing::kSettingMarginsType, - printing::DEFAULT_MARGINS); + settings.SetIntKey(printing::kSettingMarginsType, + printing::DEFAULT_MARGINS); } // Set whether to print color or greyscale bool print_color = true; options.Get("color", &print_color); int color_setting = print_color ? printing::COLOR : printing::GRAY; - settings.SetInteger(printing::kSettingColor, color_setting); + settings.SetIntKey(printing::kSettingColor, color_setting); // Is the orientation landscape or portrait. bool landscape = false; options.Get("landscape", &landscape); - settings.SetBoolean(printing::kSettingLandscape, landscape); + settings.SetBoolKey(printing::kSettingLandscape, landscape); - // We set the default to empty string here and only update - // if at the Chromium level if it's non-empty + // We set the default to the system's default printer and only update + // if at the Chromium level if the user overrides. // Printer device name as opened by the OS. base::string16 device_name; options.Get("deviceName", &device_name); - settings.SetString(printing::kSettingDeviceName, device_name); + if (!device_name.empty() && !IsDeviceNameValid(device_name)) { + args->ThrowError("webContents.print(): Invalid deviceName provided."); + return; + } int scale_factor = 100; options.Get("scaleFactor", &scale_factor); - settings.SetInteger(printing::kSettingScaleFactor, scale_factor); + settings.SetIntKey(printing::kSettingScaleFactor, scale_factor); int pages_per_sheet = 1; options.Get("pagesPerSheet", &pages_per_sheet); - settings.SetInteger(printing::kSettingPagesPerSheet, pages_per_sheet); + settings.SetIntKey(printing::kSettingPagesPerSheet, pages_per_sheet); // True if the user wants to print with collate. bool collate = true; options.Get("collate", &collate); - settings.SetBoolean(printing::kSettingCollate, collate); + settings.SetBoolKey(printing::kSettingCollate, collate); // The number of individual copies to print int copies = 1; options.Get("copies", &copies); - settings.SetInteger(printing::kSettingCopies, copies); + settings.SetIntKey(printing::kSettingCopies, copies); // Strings to be printed as headers and footers if requested by the user. std::string header; @@ -1787,47 +1911,43 @@ void WebContents::Print(mate::Arguments* args) { options.Get("footer", &footer); if (!(header.empty() && footer.empty())) { - settings.SetBoolean(printing::kSettingHeaderFooterEnabled, true); + settings.SetBoolKey(printing::kSettingHeaderFooterEnabled, true); - settings.SetString(printing::kSettingHeaderFooterTitle, header); - settings.SetString(printing::kSettingHeaderFooterURL, footer); + settings.SetStringKey(printing::kSettingHeaderFooterTitle, header); + settings.SetStringKey(printing::kSettingHeaderFooterURL, footer); } else { - settings.SetBoolean(printing::kSettingHeaderFooterEnabled, false); + settings.SetBoolKey(printing::kSettingHeaderFooterEnabled, false); } // We don't want to allow the user to enable these settings // but we need to set them or a CHECK is hit. - settings.SetBoolean(printing::kSettingPrintToPDF, false); - settings.SetBoolean(printing::kSettingCloudPrintDialog, false); - settings.SetBoolean(printing::kSettingPrintWithPrivet, false); - settings.SetBoolean(printing::kSettingShouldPrintSelectionOnly, false); - settings.SetBoolean(printing::kSettingPrintWithExtension, false); - settings.SetBoolean(printing::kSettingRasterizePdf, false); + settings.SetIntKey(printing::kSettingPrinterType, printing::kLocalPrinter); + settings.SetBoolKey(printing::kSettingShouldPrintSelectionOnly, false); + settings.SetBoolKey(printing::kSettingRasterizePdf, false); // Set custom page ranges to print std::vector page_ranges; if (options.Get("pageRanges", &page_ranges)) { - std::unique_ptr page_range_list(new base::ListValue()); + base::Value page_range_list(base::Value::Type::LIST); for (auto& range : page_ranges) { int from, to; if (range.Get("from", &from) && range.Get("to", &to)) { - std::unique_ptr range( - new base::DictionaryValue()); - range->SetInteger(printing::kSettingPageRangeFrom, from); - range->SetInteger(printing::kSettingPageRangeTo, to); - page_range_list->Append(std::move(range)); + base::Value range(base::Value::Type::DICTIONARY); + range.SetIntKey(printing::kSettingPageRangeFrom, from); + range.SetIntKey(printing::kSettingPageRangeTo, to); + page_range_list.Append(std::move(range)); } else { continue; } } - if (page_range_list->GetSize() > 0) - settings.SetList(printing::kSettingPageRange, std::move(page_range_list)); + if (page_range_list.GetList().size() > 0) + settings.SetPath(printing::kSettingPageRange, std::move(page_range_list)); } // Duplex type user wants to use. printing::DuplexMode duplex_mode; options.Get("duplexMode", &duplex_mode); - settings.SetInteger(printing::kSettingDuplexMode, duplex_mode); + settings.SetIntKey(printing::kSettingDuplexMode, duplex_mode); // Set custom dots per inch (dpi) mate::Dictionary dpi_settings; @@ -1835,30 +1955,28 @@ void WebContents::Print(mate::Arguments* args) { if (options.Get("dpi", &dpi_settings)) { int horizontal = 72; dpi_settings.Get("horizontal", &horizontal); - settings.SetInteger(printing::kSettingDpiHorizontal, horizontal); + settings.SetIntKey(printing::kSettingDpiHorizontal, horizontal); int vertical = 72; dpi_settings.Get("vertical", &vertical); - settings.SetInteger(printing::kSettingDpiVertical, vertical); + settings.SetIntKey(printing::kSettingDpiVertical, vertical); } else { - settings.SetInteger(printing::kSettingDpiHorizontal, dpi); - settings.SetInteger(printing::kSettingDpiVertical, dpi); + settings.SetIntKey(printing::kSettingDpiHorizontal, dpi); + settings.SetIntKey(printing::kSettingDpiVertical, dpi); } - auto* print_view_manager = - printing::PrintViewManagerBasic::FromWebContents(web_contents()); - auto* focused_frame = web_contents()->GetFocusedFrame(); - auto* rfh = focused_frame && focused_frame->HasSelection() - ? focused_frame - : web_contents()->GetMainFrame(); - print_view_manager->PrintNow(rfh, - std::make_unique( - rfh->GetRoutingID(), silent, settings), - std::move(callback)); + base::PostTaskAndReplyWithResult( + FROM_HERE, + {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_BLOCKING}, + base::BindOnce(&GetDefaultPrinterAsync), + base::BindOnce(&WebContents::OnGetDefaultPrinter, + weak_factory_.GetWeakPtr(), std::move(settings), + std::move(callback), device_name, silent)); } std::vector WebContents::GetPrinterList() { std::vector printers; - auto print_backend = printing::PrintBackend::CreateInstance(nullptr); + auto print_backend = printing::PrintBackend::CreateInstance( + nullptr, g_browser_process->GetApplicationLocale()); { // TODO(deepak1556): Deprecate this api in favor of an // async version and post a non blocing task call. @@ -2032,10 +2150,11 @@ bool WebContents::SendIPCMessageWithSender(bool internal, } for (auto* frame_host : target_hosts) { - mojom::ElectronRendererAssociatedPtr electron_ptr; + mojo::AssociatedRemote electron_renderer; frame_host->GetRemoteAssociatedInterfaces()->GetInterface( - mojo::MakeRequest(&electron_ptr)); - electron_ptr->Message(internal, false, channel, std::move(args), sender_id); + &electron_renderer); + electron_renderer->Message(internal, false, channel, args.ShallowClone(), + sender_id); } return true; } @@ -2060,11 +2179,10 @@ bool WebContents::SendIPCMessageToFrame(bool internal, if (!(*iter)->IsRenderFrameLive()) return false; - mojom::ElectronRendererAssociatedPtr electron_ptr; - (*iter)->GetRemoteAssociatedInterfaces()->GetInterface( - mojo::MakeRequest(&electron_ptr)); - electron_ptr->Message(internal, send_to_all, channel, std::move(message), - 0 /* sender_id */); + mojo::AssociatedRemote electron_renderer; + (*iter)->GetRemoteAssociatedInterfaces()->GetInterface(&electron_renderer); + electron_renderer->Message(internal, send_to_all, channel, std::move(message), + 0 /* sender_id */); return true; } @@ -2161,23 +2279,10 @@ void WebContents::StartDrag(const mate::Dictionary& item, } mate::Handle icon; - if (!item.Get("icon", &icon) && !file.empty()) { - // TODO(zcbenz): Set default icon from file. - } - - // Error checking. - if (icon.IsEmpty()) { - args->ThrowError("Must specify 'icon' option"); - return; - } - -#if defined(OS_MACOSX) - // NSWindow.dragImage requires a non-empty NSImage - if (icon->image().IsEmpty()) { + if (!item.Get("icon", &icon)) { args->ThrowError("Must specify non-empty 'icon' option"); return; } -#endif // Start dragging. if (!files.empty()) { @@ -2226,6 +2331,34 @@ v8::Local WebContents::CapturePage(mate::Arguments* mate_args) { return handle; } +void WebContents::IncrementCapturerCount(mate::Arguments* mate_args) { + gin::Arguments gin_args(mate_args->info()); + gin_helper::Arguments* args = static_cast(&gin_args); + + gfx::Size size; + bool stay_hidden = false; + + // get size arguments if they exist + args->GetNext(&size); + // get stayHidden arguments if they exist + args->GetNext(&stay_hidden); + + web_contents()->IncrementCapturerCount(size, stay_hidden); +} + +void WebContents::DecrementCapturerCount(mate::Arguments* args) { + bool stay_hidden = false; + + // get stayHidden arguments if they exist + args->GetNext(&stay_hidden); + + web_contents()->DecrementCapturerCount(stay_hidden); +} + +bool WebContents::IsBeingCaptured() { + return web_contents()->IsBeingCaptured(); +} + void WebContents::OnCursorChange(const content::WebCursor& cursor) { const content::CursorInfo& info = cursor.info(); @@ -2263,7 +2396,8 @@ bool WebContents::IsOffScreen() const { #if BUILDFLAG(ENABLE_OSR) void WebContents::OnPaint(const gfx::Rect& dirty_rect, const SkBitmap& bitmap) { - Emit("paint", dirty_rect, gfx::Image::CreateFrom1xBitmap(bitmap)); + Emit("paint", gin::ConvertToV8(isolate(), dirty_rect), + gfx::Image::CreateFrom1xBitmap(bitmap)); } void WebContents::StartPainting() { @@ -2329,7 +2463,13 @@ double WebContents::GetZoomLevel() const { return zoom_controller_->GetZoomLevel(); } -void WebContents::SetZoomFactor(double factor) { +void WebContents::SetZoomFactor(gin_helper::ErrorThrower thrower, + double factor) { + if (factor < std::numeric_limits::epsilon()) { + thrower.ThrowError("'zoomFactor' must be a double greater than 0.0"); + return; + } + auto level = blink::PageZoomFactorToZoomLevel(factor); SetZoomLevel(level); } @@ -2339,15 +2479,6 @@ double WebContents::GetZoomFactor() const { return blink::PageZoomLevelToZoomFactor(level); } -void WebContents::SetZoomLimits(double min_zoom, double max_zoom) { - // Round the double to avoid returning incorrect minimum/maximum zoom - // percentages. - int minimum_percent = round(blink::PageZoomLevelToZoomFactor(min_zoom) * 100); - int maximum_percent = round(blink::PageZoomLevelToZoomFactor(max_zoom) * 100); - web_contents()->SetMinimumZoomPercent(minimum_percent); - web_contents()->SetMaximumZoomPercent(maximum_percent); -} - void WebContents::SetTemporaryZoomLevel(double level) { zoom_controller_->SetTemporaryZoomLevel(level); } @@ -2483,14 +2614,15 @@ v8::Local WebContents::TakeHeapSnapshot( // This dance with `base::Owned` is to ensure that the interface stays alive // until the callback is called. Otherwise it would be closed at the end of // this function. - auto electron_ptr = std::make_unique(); + auto electron_renderer = + std::make_unique>(); frame_host->GetRemoteAssociatedInterfaces()->GetInterface( - mojo::MakeRequest(electron_ptr.get())); - auto* raw_ptr = electron_ptr.get(); + electron_renderer.get()); + auto* raw_ptr = electron_renderer.get(); (*raw_ptr)->TakeHeapSnapshot( mojo::WrapPlatformFile(file.TakePlatformFile()), base::BindOnce( - [](mojom::ElectronRendererAssociatedPtr* ep, + [](mojo::AssociatedRemote* ep, util::Promise promise, bool success) { if (success) { promise.Resolve(); @@ -2498,7 +2630,7 @@ v8::Local WebContents::TakeHeapSnapshot( promise.RejectWithErrorMessage("takeHeapSnapshot failed"); } }, - base::Owned(std::move(electron_ptr)), std::move(promise))); + base::Owned(std::move(electron_renderer)), std::move(promise))); return handle; } @@ -2613,6 +2745,9 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("setEmbedder", &WebContents::SetEmbedder) .SetMethod("setDevToolsWebContents", &WebContents::SetDevToolsWebContents) .SetMethod("getNativeView", &WebContents::GetNativeView) + .SetMethod("incrementCapturerCount", &WebContents::IncrementCapturerCount) + .SetMethod("decrementCapturerCount", &WebContents::DecrementCapturerCount) + .SetMethod("isBeingCaptured", &WebContents::IsBeingCaptured) .SetMethod("setWebRTCIPHandlingPolicy", &WebContents::SetWebRTCIPHandlingPolicy) .SetMethod("getWebRTCIPHandlingPolicy", @@ -2626,8 +2761,9 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetProperty("debugger", &WebContents::Debugger); } -AtomBrowserContext* WebContents::GetBrowserContext() const { - return static_cast(web_contents()->GetBrowserContext()); +ElectronBrowserContext* WebContents::GetBrowserContext() const { + return static_cast( + web_contents()->GetBrowserContext()); } // static diff --git a/shell/browser/api/atom_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h similarity index 91% rename from shell/browser/api/atom_api_web_contents.h rename to shell/browser/api/electron_api_web_contents.h index a3e2644d14186..17cf91196e503 100644 --- a/shell/browser/api/atom_api_web_contents.h +++ b/shell/browser/api/electron_api_web_contents.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_WEB_CONTENTS_H_ -#define SHELL_BROWSER_API_ATOM_API_WEB_CONTENTS_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_WEB_CONTENTS_H_ +#define SHELL_BROWSER_API_ELECTRON_API_WEB_CONTENTS_H_ #include #include @@ -17,11 +17,11 @@ #include "content/public/browser/keyboard_event_processing_result.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_binding_set.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/common/favicon_url.h" #include "electron/buildflags/buildflags.h" #include "electron/shell/common/api/api.mojom.h" +#include "mojo/public/cpp/bindings/binding_set.h" #include "native_mate/handle.h" #include "printing/buildflags/buildflags.h" #include "services/service_manager/public/cpp/binder_registry.h" @@ -32,8 +32,14 @@ #include "ui/gfx/image/image.h" #if BUILDFLAG(ENABLE_PRINTING) +#include "chrome/browser/printing/print_view_manager_basic.h" +#include "components/printing/common/print_messages.h" #include "printing/backend/print_backend.h" #include "shell/browser/printing/print_preview_message_handler.h" + +#if defined(OS_WIN) +#include "printing/backend/win_helper.h" +#endif #endif namespace blink { @@ -51,8 +57,8 @@ class ResourceRequestBody; namespace electron { -class AtomBrowserContext; -class AtomJavaScriptDialogManager; +class ElectronBrowserContext; +class ElectronJavaScriptDialogManager; class InspectableWebContents; class WebContentsZoomController; class WebViewGuestDelegate; @@ -182,8 +188,16 @@ class WebContents : public mate::TrackableObject, void SetEmbedder(const WebContents* embedder); void SetDevToolsWebContents(const WebContents* devtools); v8::Local GetNativeView() const; + void IncrementCapturerCount(mate::Arguments* args); + void DecrementCapturerCount(mate::Arguments* args); + bool IsBeingCaptured(); #if BUILDFLAG(ENABLE_PRINTING) + void OnGetDefaultPrinter(base::Value print_settings, + printing::CompletionCallback print_callback, + base::string16 device_name, + bool silent, + base::string16 default_printer); void Print(mate::Arguments* args); std::vector GetPrinterList(); // Print current page as PDF. @@ -270,15 +284,15 @@ class WebContents : public mate::TrackableObject, // Methods for zoom handling. void SetZoomLevel(double level); double GetZoomLevel() const; - void SetZoomFactor(double factor); + void SetZoomFactor(gin_helper::ErrorThrower thrower, double factor); double GetZoomFactor() const; - void SetZoomLimits(double min_zoom, double max_zoom) override; // Callback triggered on permission response. - void OnEnterFullscreenModeForTab(content::WebContents* source, - const GURL& origin, - const blink::WebFullscreenOptions& options, - bool allowed); + void OnEnterFullscreenModeForTab( + content::WebContents* source, + const GURL& origin, + const blink::mojom::FullscreenOptions& options, + bool allowed); // Create window with the given disposition. void OnCreateWindow(const GURL& target_url, @@ -350,6 +364,21 @@ class WebContents : public mate::TrackableObject, const base::string16& message, int32_t line_no, const base::string16& source_id) override; + bool IsWebContentsCreationOverridden( + content::SiteInstance* source_site_instance, + content::mojom::WindowContainerType window_container_type, + const GURL& opener_url, + const std::string& frame_name, + const GURL& target_url) override; + content::WebContents* CreateCustomWebContents( + content::RenderFrameHost* opener, + content::SiteInstance* source_site_instance, + bool is_new_browsing_instance, + const GURL& opener_url, + const std::string& frame_name, + const GURL& target_url, + const std::string& partition_id, + content::SessionStorageNamespace* session_storage_namespace) override; void WebContentsCreated(content::WebContents* source_contents, int opener_render_process_id, int opener_render_frame_id, @@ -383,7 +412,7 @@ class WebContents : public mate::TrackableObject, void EnterFullscreenModeForTab( content::WebContents* source, const GURL& origin, - const blink::WebFullscreenOptions& options) override; + const blink::mojom::FullscreenOptions& options) override; void ExitFullscreenModeForTab(content::WebContents* source) override; void RendererUnresponsive( content::WebContents* source, @@ -421,7 +450,8 @@ class WebContents : public mate::TrackableObject, // content::WebContentsObserver: void BeforeUnloadFired(bool proceed, const base::TimeTicks& proceed_time) override; - void RenderViewCreated(content::RenderViewHost*) override; + void RenderViewCreated(content::RenderViewHost* render_view_host) override; + void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; void RenderViewHostChanged(content::RenderViewHost* old_host, content::RenderViewHost* new_host) override; void RenderViewDeleted(content::RenderViewHost*) override; @@ -473,7 +503,7 @@ class WebContents : public mate::TrackableObject, void DevToolsClosed() override; private: - AtomBrowserContext* GetBrowserContext() const; + ElectronBrowserContext* GetBrowserContext() const; // Binds the given request for the ElectronBrowser API. When the // RenderFrameHost is destroyed, all related bindings will be removed. @@ -532,7 +562,7 @@ class WebContents : public mate::TrackableObject, v8::Global devtools_web_contents_; v8::Global debugger_; - std::unique_ptr dialog_manager_; + std::unique_ptr dialog_manager_; std::unique_ptr guest_delegate_; std::unique_ptr frame_subscriber_; @@ -575,4 +605,4 @@ class WebContents : public mate::TrackableObject, } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_WEB_CONTENTS_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_WEB_CONTENTS_H_ diff --git a/shell/browser/api/atom_api_web_contents_impl.cc b/shell/browser/api/electron_api_web_contents_impl.cc similarity index 97% rename from shell/browser/api/atom_api_web_contents_impl.cc rename to shell/browser/api/electron_api_web_contents_impl.cc index f29a63b997b3c..1855d0325b27d 100644 --- a/shell/browser/api/atom_api_web_contents_impl.cc +++ b/shell/browser/api/electron_api_web_contents_impl.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "content/browser/frame_host/frame_tree.h" // nogncheck #include "content/browser/frame_host/frame_tree_node.h" // nogncheck diff --git a/shell/browser/api/atom_api_web_contents_mac.mm b/shell/browser/api/electron_api_web_contents_mac.mm similarity index 93% rename from shell/browser/api/atom_api_web_contents_mac.mm rename to shell/browser/api/electron_api_web_contents_mac.mm index a0fd14a000e93..ea70eed08d522 100644 --- a/shell/browser/api/atom_api_web_contents_mac.mm +++ b/shell/browser/api/electron_api_web_contents_mac.mm @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "content/public/browser/render_widget_host_view.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" #import diff --git a/shell/browser/api/atom_api_web_contents_view.cc b/shell/browser/api/electron_api_web_contents_view.cc similarity index 97% rename from shell/browser/api/atom_api_web_contents_view.cc rename to shell/browser/api/electron_api_web_contents_view.cc index 28e84b5795813..9ac5f734b5920 100644 --- a/shell/browser/api/atom_api_web_contents_view.cc +++ b/shell/browser/api/electron_api_web_contents_view.cc @@ -2,11 +2,11 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_web_contents_view.h" +#include "shell/browser/api/electron_api_web_contents_view.h" #include "content/public/browser/web_contents_user_data.h" #include "native_mate/dictionary.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/browser.h" #include "shell/browser/ui/inspectable_web_contents_view.h" #include "shell/common/api/constructor.h" diff --git a/shell/browser/api/atom_api_web_contents_view.h b/shell/browser/api/electron_api_web_contents_view.h similarity index 83% rename from shell/browser/api/atom_api_web_contents_view.h rename to shell/browser/api/electron_api_web_contents_view.h index 6502cb7dd6019..a90e90528d180 100644 --- a/shell/browser/api/atom_api_web_contents_view.h +++ b/shell/browser/api/electron_api_web_contents_view.h @@ -2,12 +2,12 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_WEB_CONTENTS_VIEW_H_ -#define SHELL_BROWSER_API_ATOM_API_WEB_CONTENTS_VIEW_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_WEB_CONTENTS_VIEW_H_ +#define SHELL_BROWSER_API_ELECTRON_API_WEB_CONTENTS_VIEW_H_ #include "content/public/browser/web_contents_observer.h" #include "native_mate/handle.h" -#include "shell/browser/api/atom_api_view.h" +#include "shell/browser/api/electron_api_view.h" namespace electron { @@ -46,4 +46,4 @@ class WebContentsView : public View, public content::WebContentsObserver { } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_WEB_CONTENTS_VIEW_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_WEB_CONTENTS_VIEW_H_ diff --git a/shell/browser/api/atom_api_web_request_ns.cc b/shell/browser/api/electron_api_web_request_ns.cc similarity index 97% rename from shell/browser/api/atom_api_web_request_ns.cc rename to shell/browser/api/electron_api_web_request_ns.cc index a2a06aa557bfb..6f42284519365 100644 --- a/shell/browser/api/atom_api_web_request_ns.cc +++ b/shell/browser/api/electron_api_web_request_ns.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/atom_api_web_request_ns.h" +#include "shell/browser/api/electron_api_web_request_ns.h" #include #include @@ -13,9 +13,9 @@ #include "gin/converter.h" #include "gin/dictionary.h" #include "gin/object_template_builder.h" -#include "shell/browser/api/atom_api_session.h" -#include "shell/browser/api/atom_api_web_contents.h" -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/api/electron_api_session.h" +#include "shell/browser/api/electron_api_web_contents.h" +#include "shell/browser/electron_browser_context.h" #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/gin_converters/gurl_converter.h" #include "shell/common/gin_converters/net_converter.h" @@ -332,6 +332,10 @@ void WebRequestNS::OnCompleted(extensions::WebRequestInfo* info, HandleSimpleEvent(kOnCompleted, info, request, net_error); } +void WebRequestNS::OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) { + callbacks_.erase(info->id); +} + template void WebRequestNS::SetSimpleListener(gin::Arguments* args) { SetListener(event, &simple_listeners_, args); @@ -475,8 +479,8 @@ gin::Handle WebRequestNS::FromOrCreate( if (handle.IsEmpty()) { // Make sure the |Session| object has the |webRequest| property created. v8::Local web_request = - Session::CreateFrom(isolate, - static_cast(browser_context)) + Session::CreateFrom( + isolate, static_cast(browser_context)) ->WebRequest(isolate); gin::ConvertFromV8(isolate, web_request, &handle); } diff --git a/shell/browser/api/atom_api_web_request_ns.h b/shell/browser/api/electron_api_web_request_ns.h similarity index 94% rename from shell/browser/api/atom_api_web_request_ns.h rename to shell/browser/api/electron_api_web_request_ns.h index d4d3be8a3af5b..2b138424d2e84 100644 --- a/shell/browser/api/atom_api_web_request_ns.h +++ b/shell/browser/api/electron_api_web_request_ns.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_ATOM_API_WEB_REQUEST_NS_H_ -#define SHELL_BROWSER_API_ATOM_API_WEB_REQUEST_NS_H_ +#ifndef SHELL_BROWSER_API_ELECTRON_API_WEB_REQUEST_NS_H_ +#define SHELL_BROWSER_API_ELECTRON_API_WEB_REQUEST_NS_H_ #include #include @@ -13,7 +13,7 @@ #include "gin/arguments.h" #include "gin/handle.h" #include "gin/wrappable.h" -#include "shell/browser/net/proxying_url_loader_factory.h" +#include "shell/browser/net/web_request_api_interface.h" namespace content { class BrowserContext; @@ -50,10 +50,6 @@ class WebRequestNS : public gin::Wrappable, public WebRequestAPI { v8::Isolate* isolate) override; const char* GetTypeName() override; - private: - WebRequestNS(v8::Isolate* isolate, content::BrowserContext* browser_context); - ~WebRequestNS() override; - // WebRequestAPI: bool HasListener() const override; int OnBeforeRequest(extensions::WebRequestInfo* info, @@ -85,6 +81,11 @@ class WebRequestNS : public gin::Wrappable, public WebRequestAPI { void OnCompleted(extensions::WebRequestInfo* info, const network::ResourceRequest& request, int net_error) override; + void OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) override; + + private: + WebRequestNS(v8::Isolate* isolate, content::BrowserContext* browser_context); + ~WebRequestNS() override; enum SimpleEvent { kOnSendHeaders, @@ -155,4 +156,4 @@ class WebRequestNS : public gin::Wrappable, public WebRequestAPI { } // namespace electron -#endif // SHELL_BROWSER_API_ATOM_API_WEB_REQUEST_NS_H_ +#endif // SHELL_BROWSER_API_ELECTRON_API_WEB_REQUEST_NS_H_ diff --git a/shell/browser/api/atom_api_web_view_manager.cc b/shell/browser/api/electron_api_web_view_manager.cc similarity index 100% rename from shell/browser/api/atom_api_web_view_manager.cc rename to shell/browser/api/electron_api_web_view_manager.cc diff --git a/shell/browser/api/frame_subscriber.cc b/shell/browser/api/frame_subscriber.cc index cf53219dcdc81..2d3d1a24701c5 100644 --- a/shell/browser/api/frame_subscriber.cc +++ b/shell/browser/api/frame_subscriber.cc @@ -10,6 +10,7 @@ #include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host_view.h" #include "media/capture/mojom/video_capture_types.mojom.h" +#include "mojo/public/cpp/bindings/remote.h" #include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/image/image.h" #include "ui/gfx/skbitmap_operations.h" @@ -85,7 +86,8 @@ void FrameSubscriber::OnFrameCaptured( base::ReadOnlySharedMemoryRegion data, ::media::mojom::VideoFrameInfoPtr info, const gfx::Rect& content_rect, - viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) { + mojo::PendingRemote + callbacks) { gfx::Size size = GetRenderViewSize(); if (size != content_rect.size()) { video_capturer_->SetResolutionConstraints(size, size, true); @@ -93,8 +95,10 @@ void FrameSubscriber::OnFrameCaptured( return; } + mojo::Remote + callbacks_remote(std::move(callbacks)); if (!data.IsValid()) { - callbacks->Done(); + callbacks_remote->Done(); return; } base::ReadOnlySharedMemoryMapping mapping = data.Map(); @@ -120,7 +124,7 @@ void FrameSubscriber::OnFrameCaptured( base::ReadOnlySharedMemoryMapping mapping; // Prevents FrameSinkVideoCapturer from recycling the shared memory that // backs |frame_|. - viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr releaser; + mojo::Remote releaser; }; SkBitmap bitmap; @@ -133,7 +137,7 @@ void FrameSubscriber::OnFrameCaptured( [](void* addr, void* context) { delete static_cast(context); }, - new FramePinner{std::move(mapping), std::move(callbacks)}); + new FramePinner{std::move(mapping), std::move(callbacks_remote)}); bitmap.setImmutable(); Done(content_rect, bitmap); diff --git a/shell/browser/api/frame_subscriber.h b/shell/browser/api/frame_subscriber.h index 59dcb0c45c003..db91937792420 100644 --- a/shell/browser/api/frame_subscriber.h +++ b/shell/browser/api/frame_subscriber.h @@ -12,6 +12,7 @@ #include "components/viz/host/client_frame_sink_video_capturer.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "v8/include/v8.h" namespace gfx { @@ -49,7 +50,8 @@ class FrameSubscriber : public content::WebContentsObserver, base::ReadOnlySharedMemoryRegion data, ::media::mojom::VideoFrameInfoPtr info, const gfx::Rect& content_rect, - viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) override; + mojo::PendingRemote + callbacks) override; void OnStopped() override; void Done(const gfx::Rect& damage, const SkBitmap& frame); diff --git a/shell/browser/api/gpu_info_enumerator.cc b/shell/browser/api/gpu_info_enumerator.cc index 978c3248a0194..47810ded35909 100644 --- a/shell/browser/api/gpu_info_enumerator.cc +++ b/shell/browser/api/gpu_info_enumerator.cc @@ -35,6 +35,11 @@ void GPUInfoEnumerator::AddTimeDeltaInSecondsF(const char* name, current->SetInteger(name, value.InMilliseconds()); } +void GPUInfoEnumerator::AddBinary(const char* name, + const base::span& value) { + current->Set(name, std::make_unique(value)); +} + void GPUInfoEnumerator::BeginGPUDevice() { value_stack.push(std::move(current)); current = std::make_unique(); diff --git a/shell/browser/api/gpu_info_enumerator.h b/shell/browser/api/gpu_info_enumerator.h index e343f0d886d86..474c20b556fee 100644 --- a/shell/browser/api/gpu_info_enumerator.h +++ b/shell/browser/api/gpu_info_enumerator.h @@ -36,6 +36,8 @@ class GPUInfoEnumerator final : public gpu::GPUInfo::Enumerator { void AddBool(const char* name, bool value) override; void AddTimeDeltaInSecondsF(const char* name, const base::TimeDelta& value) override; + void AddBinary(const char* name, + const base::span& value) override; void BeginGPUDevice() override; void EndGPUDevice() override; void BeginVideoDecodeAcceleratorSupportedProfile() override; diff --git a/shell/browser/api/save_page_handler.cc b/shell/browser/api/save_page_handler.cc index 72239f5fee6f2..ff055141ff2ad 100644 --- a/shell/browser/api/save_page_handler.cc +++ b/shell/browser/api/save_page_handler.cc @@ -10,7 +10,7 @@ #include "base/callback.h" #include "base/files/file_path.h" #include "content/public/browser/web_contents.h" -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/electron_browser_context.h" namespace electron { diff --git a/shell/browser/api/trackable_object.cc b/shell/browser/api/trackable_object.cc index 62a9af2c6644e..cc22e593aa656 100644 --- a/shell/browser/api/trackable_object.cc +++ b/shell/browser/api/trackable_object.cc @@ -8,7 +8,7 @@ #include "base/bind.h" #include "base/supports_user_data.h" -#include "shell/browser/atom_browser_main_parts.h" +#include "shell/browser/electron_browser_main_parts.h" namespace mate { @@ -31,7 +31,7 @@ class IDUserData : public base::SupportsUserData::Data { } // namespace TrackableObjectBase::TrackableObjectBase() : weak_factory_(this) { - electron::AtomBrowserMainParts::Get()->RegisterDestructionCallback( + electron::ElectronBrowserMainParts::Get()->RegisterDestructionCallback( GetDestroyClosure()); } diff --git a/shell/browser/api/views/atom_api_box_layout.cc b/shell/browser/api/views/electron_api_box_layout.cc similarity index 95% rename from shell/browser/api/views/atom_api_box_layout.cc rename to shell/browser/api/views/electron_api_box_layout.cc index 23b4474bf910b..4dbf7641170b6 100644 --- a/shell/browser/api/views/atom_api_box_layout.cc +++ b/shell/browser/api/views/electron_api_box_layout.cc @@ -2,12 +2,12 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/views/atom_api_box_layout.h" +#include "shell/browser/api/views/electron_api_box_layout.h" #include #include "native_mate/dictionary.h" -#include "shell/browser/api/atom_api_view.h" +#include "shell/browser/api/electron_api_view.h" #include "shell/common/api/constructor.h" #include "shell/common/node_includes.h" diff --git a/shell/browser/api/views/atom_api_box_layout.h b/shell/browser/api/views/electron_api_box_layout.h similarity index 77% rename from shell/browser/api/views/atom_api_box_layout.h rename to shell/browser/api/views/electron_api_box_layout.h index 4cffa4a7b5ee3..ee3f41f91ff5a 100644 --- a/shell/browser/api/views/atom_api_box_layout.h +++ b/shell/browser/api/views/electron_api_box_layout.h @@ -2,11 +2,11 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_VIEWS_ATOM_API_BOX_LAYOUT_H_ -#define SHELL_BROWSER_API_VIEWS_ATOM_API_BOX_LAYOUT_H_ +#ifndef SHELL_BROWSER_API_VIEWS_ELECTRON_API_BOX_LAYOUT_H_ +#define SHELL_BROWSER_API_VIEWS_ELECTRON_API_BOX_LAYOUT_H_ #include "native_mate/handle.h" -#include "shell/browser/api/views/atom_api_layout_manager.h" +#include "shell/browser/api/views/electron_api_layout_manager.h" #include "ui/views/layout/box_layout.h" namespace electron { @@ -37,4 +37,4 @@ class BoxLayout : public LayoutManager { } // namespace electron -#endif // SHELL_BROWSER_API_VIEWS_ATOM_API_BOX_LAYOUT_H_ +#endif // SHELL_BROWSER_API_VIEWS_ELECTRON_API_BOX_LAYOUT_H_ diff --git a/shell/browser/api/views/atom_api_button.cc b/shell/browser/api/views/electron_api_button.cc similarity index 96% rename from shell/browser/api/views/atom_api_button.cc rename to shell/browser/api/views/electron_api_button.cc index a93a2b6abe03d..18c690099913d 100644 --- a/shell/browser/api/views/atom_api_button.cc +++ b/shell/browser/api/views/electron_api_button.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/views/atom_api_button.h" +#include "shell/browser/api/views/electron_api_button.h" #include "native_mate/dictionary.h" #include "shell/common/api/constructor.h" diff --git a/shell/browser/api/views/atom_api_button.h b/shell/browser/api/views/electron_api_button.h similarity index 79% rename from shell/browser/api/views/atom_api_button.h rename to shell/browser/api/views/electron_api_button.h index 20827c3affe53..5f68e67a4830c 100644 --- a/shell/browser/api/views/atom_api_button.h +++ b/shell/browser/api/views/electron_api_button.h @@ -2,11 +2,11 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_VIEWS_ATOM_API_BUTTON_H_ -#define SHELL_BROWSER_API_VIEWS_ATOM_API_BUTTON_H_ +#ifndef SHELL_BROWSER_API_VIEWS_ELECTRON_API_BUTTON_H_ +#define SHELL_BROWSER_API_VIEWS_ELECTRON_API_BUTTON_H_ #include "native_mate/handle.h" -#include "shell/browser/api/atom_api_view.h" +#include "shell/browser/api/electron_api_view.h" #include "ui/views/controls/button/button.h" namespace electron { @@ -37,4 +37,4 @@ class Button : public View, public views::ButtonListener { } // namespace electron -#endif // SHELL_BROWSER_API_VIEWS_ATOM_API_BUTTON_H_ +#endif // SHELL_BROWSER_API_VIEWS_ELECTRON_API_BUTTON_H_ diff --git a/shell/browser/api/views/atom_api_label_button.cc b/shell/browser/api/views/electron_api_label_button.cc similarity index 97% rename from shell/browser/api/views/atom_api_label_button.cc rename to shell/browser/api/views/electron_api_label_button.cc index 8ec1be8154332..371f990eaac7e 100644 --- a/shell/browser/api/views/atom_api_label_button.cc +++ b/shell/browser/api/views/electron_api_label_button.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/views/atom_api_label_button.h" +#include "shell/browser/api/views/electron_api_label_button.h" #include "base/strings/utf_string_conversions.h" #include "native_mate/dictionary.h" diff --git a/shell/browser/api/views/atom_api_label_button.h b/shell/browser/api/views/electron_api_label_button.h similarity index 80% rename from shell/browser/api/views/atom_api_label_button.h rename to shell/browser/api/views/electron_api_label_button.h index 2b966faa617be..a512e6c367bb8 100644 --- a/shell/browser/api/views/atom_api_label_button.h +++ b/shell/browser/api/views/electron_api_label_button.h @@ -2,12 +2,12 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_VIEWS_ATOM_API_LABEL_BUTTON_H_ -#define SHELL_BROWSER_API_VIEWS_ATOM_API_LABEL_BUTTON_H_ +#ifndef SHELL_BROWSER_API_VIEWS_ELECTRON_API_LABEL_BUTTON_H_ +#define SHELL_BROWSER_API_VIEWS_ELECTRON_API_LABEL_BUTTON_H_ #include -#include "shell/browser/api/views/atom_api_button.h" +#include "shell/browser/api/views/electron_api_button.h" #include "ui/views/controls/button/label_button.h" namespace electron { @@ -44,4 +44,4 @@ class LabelButton : public Button { } // namespace electron -#endif // SHELL_BROWSER_API_VIEWS_ATOM_API_LABEL_BUTTON_H_ +#endif // SHELL_BROWSER_API_VIEWS_ELECTRON_API_LABEL_BUTTON_H_ diff --git a/shell/browser/api/views/atom_api_layout_manager.cc b/shell/browser/api/views/electron_api_layout_manager.cc similarity index 96% rename from shell/browser/api/views/atom_api_layout_manager.cc rename to shell/browser/api/views/electron_api_layout_manager.cc index c5720178d241a..d24c4edbf9b35 100644 --- a/shell/browser/api/views/atom_api_layout_manager.cc +++ b/shell/browser/api/views/electron_api_layout_manager.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/views/atom_api_layout_manager.h" +#include "shell/browser/api/views/electron_api_layout_manager.h" #include "native_mate/dictionary.h" #include "shell/common/api/constructor.h" diff --git a/shell/browser/api/views/atom_api_layout_manager.h b/shell/browser/api/views/electron_api_layout_manager.h similarity index 84% rename from shell/browser/api/views/atom_api_layout_manager.h rename to shell/browser/api/views/electron_api_layout_manager.h index 9281e58ef52d1..f629c2c2d8600 100644 --- a/shell/browser/api/views/atom_api_layout_manager.h +++ b/shell/browser/api/views/electron_api_layout_manager.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_VIEWS_ATOM_API_LAYOUT_MANAGER_H_ -#define SHELL_BROWSER_API_VIEWS_ATOM_API_LAYOUT_MANAGER_H_ +#ifndef SHELL_BROWSER_API_VIEWS_ELECTRON_API_LAYOUT_MANAGER_H_ +#define SHELL_BROWSER_API_VIEWS_ELECTRON_API_LAYOUT_MANAGER_H_ #include @@ -41,4 +41,4 @@ class LayoutManager : public mate::TrackableObject { } // namespace electron -#endif // SHELL_BROWSER_API_VIEWS_ATOM_API_LAYOUT_MANAGER_H_ +#endif // SHELL_BROWSER_API_VIEWS_ELECTRON_API_LAYOUT_MANAGER_H_ diff --git a/shell/browser/api/views/atom_api_md_text_button.cc b/shell/browser/api/views/electron_api_md_text_button.cc similarity index 96% rename from shell/browser/api/views/atom_api_md_text_button.cc rename to shell/browser/api/views/electron_api_md_text_button.cc index ca5dcfe679933..75fbc7513f5c3 100644 --- a/shell/browser/api/views/atom_api_md_text_button.cc +++ b/shell/browser/api/views/electron_api_md_text_button.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/views/atom_api_md_text_button.h" +#include "shell/browser/api/views/electron_api_md_text_button.h" #include "base/strings/utf_string_conversions.h" #include "native_mate/dictionary.h" diff --git a/shell/browser/api/views/atom_api_md_text_button.h b/shell/browser/api/views/electron_api_md_text_button.h similarity index 76% rename from shell/browser/api/views/atom_api_md_text_button.h rename to shell/browser/api/views/electron_api_md_text_button.h index 4538105809d9a..47834b873d466 100644 --- a/shell/browser/api/views/atom_api_md_text_button.h +++ b/shell/browser/api/views/electron_api_md_text_button.h @@ -2,12 +2,12 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_VIEWS_ATOM_API_MD_TEXT_BUTTON_H_ -#define SHELL_BROWSER_API_VIEWS_ATOM_API_MD_TEXT_BUTTON_H_ +#ifndef SHELL_BROWSER_API_VIEWS_ELECTRON_API_MD_TEXT_BUTTON_H_ +#define SHELL_BROWSER_API_VIEWS_ELECTRON_API_MD_TEXT_BUTTON_H_ #include -#include "shell/browser/api/views/atom_api_label_button.h" +#include "shell/browser/api/views/electron_api_label_button.h" #include "ui/views/controls/button/md_text_button.h" namespace electron { @@ -38,4 +38,4 @@ class MdTextButton : public LabelButton { } // namespace electron -#endif // SHELL_BROWSER_API_VIEWS_ATOM_API_MD_TEXT_BUTTON_H_ +#endif // SHELL_BROWSER_API_VIEWS_ELECTRON_API_MD_TEXT_BUTTON_H_ diff --git a/shell/browser/api/views/atom_api_resize_area.cc b/shell/browser/api/views/electron_api_resize_area.cc similarity index 96% rename from shell/browser/api/views/atom_api_resize_area.cc rename to shell/browser/api/views/electron_api_resize_area.cc index 7690fa33a9998..b92751707b022 100644 --- a/shell/browser/api/views/atom_api_resize_area.cc +++ b/shell/browser/api/views/electron_api_resize_area.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/views/atom_api_resize_area.h" +#include "shell/browser/api/views/electron_api_resize_area.h" #include "native_mate/dictionary.h" #include "shell/common/api/constructor.h" diff --git a/shell/browser/api/views/atom_api_resize_area.h b/shell/browser/api/views/electron_api_resize_area.h similarity index 79% rename from shell/browser/api/views/atom_api_resize_area.h rename to shell/browser/api/views/electron_api_resize_area.h index bdb479ebe9b15..af2263e66069e 100644 --- a/shell/browser/api/views/atom_api_resize_area.h +++ b/shell/browser/api/views/electron_api_resize_area.h @@ -2,11 +2,11 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_VIEWS_ATOM_API_RESIZE_AREA_H_ -#define SHELL_BROWSER_API_VIEWS_ATOM_API_RESIZE_AREA_H_ +#ifndef SHELL_BROWSER_API_VIEWS_ELECTRON_API_RESIZE_AREA_H_ +#define SHELL_BROWSER_API_VIEWS_ELECTRON_API_RESIZE_AREA_H_ #include "native_mate/handle.h" -#include "shell/browser/api/atom_api_view.h" +#include "shell/browser/api/electron_api_view.h" #include "ui/views/controls/resize_area.h" #include "ui/views/controls/resize_area_delegate.h" @@ -40,4 +40,4 @@ class ResizeArea : public View, protected views::ResizeAreaDelegate { } // namespace electron -#endif // SHELL_BROWSER_API_VIEWS_ATOM_API_RESIZE_AREA_H_ +#endif // SHELL_BROWSER_API_VIEWS_ELECTRON_API_RESIZE_AREA_H_ diff --git a/shell/browser/api/views/atom_api_text_field.cc b/shell/browser/api/views/electron_api_text_field.cc similarity index 96% rename from shell/browser/api/views/atom_api_text_field.cc rename to shell/browser/api/views/electron_api_text_field.cc index 2a5333c8b75aa..640c095b49e33 100644 --- a/shell/browser/api/views/atom_api_text_field.cc +++ b/shell/browser/api/views/electron_api_text_field.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/api/views/atom_api_text_field.h" +#include "shell/browser/api/views/electron_api_text_field.h" #include "native_mate/dictionary.h" #include "shell/common/api/constructor.h" diff --git a/shell/browser/api/views/atom_api_text_field.h b/shell/browser/api/views/electron_api_text_field.h similarity index 78% rename from shell/browser/api/views/atom_api_text_field.h rename to shell/browser/api/views/electron_api_text_field.h index 7b6c0140287c8..9b9f16996a66f 100644 --- a/shell/browser/api/views/atom_api_text_field.h +++ b/shell/browser/api/views/electron_api_text_field.h @@ -2,11 +2,11 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_API_VIEWS_ATOM_API_TEXT_FIELD_H_ -#define SHELL_BROWSER_API_VIEWS_ATOM_API_TEXT_FIELD_H_ +#ifndef SHELL_BROWSER_API_VIEWS_ELECTRON_API_TEXT_FIELD_H_ +#define SHELL_BROWSER_API_VIEWS_ELECTRON_API_TEXT_FIELD_H_ #include "native_mate/handle.h" -#include "shell/browser/api/atom_api_view.h" +#include "shell/browser/api/electron_api_view.h" #include "ui/views/controls/textfield/textfield.h" namespace electron { @@ -39,4 +39,4 @@ class TextField : public View { } // namespace electron -#endif // SHELL_BROWSER_API_VIEWS_ATOM_API_TEXT_FIELD_H_ +#endif // SHELL_BROWSER_API_VIEWS_ELECTRON_API_TEXT_FIELD_H_ diff --git a/shell/browser/atom_navigation_throttle.h b/shell/browser/atom_navigation_throttle.h deleted file mode 100644 index ca67a8cd25d92..0000000000000 --- a/shell/browser/atom_navigation_throttle.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2018 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef SHELL_BROWSER_ATOM_NAVIGATION_THROTTLE_H_ -#define SHELL_BROWSER_ATOM_NAVIGATION_THROTTLE_H_ - -#include "content/public/browser/navigation_throttle.h" - -namespace electron { - -class AtomNavigationThrottle : public content::NavigationThrottle { - public: - explicit AtomNavigationThrottle(content::NavigationHandle* handle); - ~AtomNavigationThrottle() override; - - AtomNavigationThrottle::ThrottleCheckResult WillRedirectRequest() override; - - const char* GetNameForLogging() override; - - private: - DISALLOW_COPY_AND_ASSIGN(AtomNavigationThrottle); -}; - -} // namespace electron - -#endif // SHELL_BROWSER_ATOM_NAVIGATION_THROTTLE_H_ diff --git a/shell/browser/atom_speech_recognition_manager_delegate.cc b/shell/browser/atom_speech_recognition_manager_delegate.cc deleted file mode 100644 index 403cdda6c6795..0000000000000 --- a/shell/browser/atom_speech_recognition_manager_delegate.cc +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "shell/browser/atom_speech_recognition_manager_delegate.h" - -#include -#include - -#include "base/callback.h" - -namespace electron { - -AtomSpeechRecognitionManagerDelegate::AtomSpeechRecognitionManagerDelegate() = - default; - -AtomSpeechRecognitionManagerDelegate::~AtomSpeechRecognitionManagerDelegate() = - default; - -void AtomSpeechRecognitionManagerDelegate::OnRecognitionStart(int session_id) {} - -void AtomSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) {} - -void AtomSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete( - int session_id) {} - -void AtomSpeechRecognitionManagerDelegate::OnSoundStart(int session_id) {} - -void AtomSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) {} - -void AtomSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) {} - -void AtomSpeechRecognitionManagerDelegate::OnRecognitionEnd(int session_id) {} - -void AtomSpeechRecognitionManagerDelegate::OnRecognitionResults( - int session_id, - const std::vector& results) {} - -void AtomSpeechRecognitionManagerDelegate::OnRecognitionError( - int session_id, - const blink::mojom::SpeechRecognitionError& error) {} - -void AtomSpeechRecognitionManagerDelegate::OnAudioLevelsChange( - int session_id, - float volume, - float noise_volume) {} - -void AtomSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed( - int session_id, - base::OnceCallback callback) { - std::move(callback).Run(true, true); -} - -content::SpeechRecognitionEventListener* -AtomSpeechRecognitionManagerDelegate::GetEventListener() { - return this; -} - -bool AtomSpeechRecognitionManagerDelegate::FilterProfanities( - int render_process_id) { - return false; -} - -} // namespace electron diff --git a/shell/browser/browser.cc b/shell/browser/browser.cc index 06b5fbd97beea..b823a1dce2da0 100644 --- a/shell/browser/browser.cc +++ b/shell/browser/browser.cc @@ -15,9 +15,9 @@ #include "base/run_loop.h" #include "base/threading/thread_restrictions.h" #include "base/threading/thread_task_runner_handle.h" -#include "shell/browser/atom_browser_main_parts.h" -#include "shell/browser/atom_paths.h" #include "shell/browser/browser_observer.h" +#include "shell/browser/electron_browser_main_parts.h" +#include "shell/browser/electron_paths.h" #include "shell/browser/login_handler.h" #include "shell/browser/native_window.h" #include "shell/browser/window_list.h" @@ -25,6 +25,21 @@ namespace electron { +namespace { + +// Call |quit| after Chromium is fully started. +// +// This is important for quitting immediately in the "ready" event, when +// certain initialization task may still be pending, and quitting at that time +// could end up with crash on exit. +void RunQuitClosure(base::OnceClosure quit) { + // On Linux/Windows the "ready" event is emitted in "PreMainMessageLoopRun", + // make sure we quit after message loop has run for once. + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(quit)); +} + +} // namespace + Browser::LoginItemSettings::LoginItemSettings() = default; Browser::LoginItemSettings::~LoginItemSettings() = default; Browser::LoginItemSettings::LoginItemSettings(const LoginItemSettings& other) = @@ -40,7 +55,7 @@ Browser::~Browser() { // static Browser* Browser::Get() { - return AtomBrowserMainParts::Get()->browser(); + return ElectronBrowserMainParts::Get()->browser(); } void Browser::Quit() { @@ -61,7 +76,7 @@ void Browser::Exit(mate::Arguments* args) { int code = 0; args->GetNext(&code); - if (!AtomBrowserMainParts::Get()->SetExitCode(code)) { + if (!ElectronBrowserMainParts::Get()->SetExitCode(code)) { // Message loop is not ready, quit directly. exit(code); } else { @@ -93,7 +108,7 @@ void Browser::Shutdown() { observer.OnQuit(); if (quit_main_message_loop_) { - std::move(quit_main_message_loop_).Run(); + RunQuitClosure(std::move(quit_main_message_loop_)); } else { // There is no message loop available so we are in early stage, wait until // the quit_main_message_loop_ is available. @@ -180,13 +195,6 @@ void Browser::OnAccessibilitySupportChanged() { observer.OnAccessibilitySupportChanged(); } -void Browser::RequestLogin( - scoped_refptr login_handler, - std::unique_ptr request_details) { - for (BrowserObserver& observer : observers_) - observer.OnLogin(login_handler, *(request_details.get())); -} - void Browser::PreMainMessageLoopRun() { for (BrowserObserver& observer : observers_) { observer.OnPreMainMessageLoopRun(); @@ -195,7 +203,7 @@ void Browser::PreMainMessageLoopRun() { void Browser::SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure) { if (is_shutdown_) - std::move(quit_closure).Run(); + RunQuitClosure(std::move(quit_closure)); else quit_main_message_loop_ = std::move(quit_closure); } diff --git a/shell/browser/browser.h b/shell/browser/browser.h index 9c160cc37ed19..8b2165abeca66 100644 --- a/shell/browser/browser.h +++ b/shell/browser/browser.h @@ -34,7 +34,7 @@ class Image; namespace electron { -class AtomMenuModel; +class ElectronMenuModel; // This class is used for control application-wide operations. class Browser : public WindowListObserver { @@ -54,7 +54,7 @@ class Browser : public WindowListObserver { void Shutdown(); // Focus the application. - void Focus(); + void Focus(mate::Arguments* args); // Returns the version of the executable (or bundle). std::string GetVersion() const; @@ -89,6 +89,8 @@ class Browser : public WindowListObserver { bool IsDefaultProtocolClient(const std::string& protocol, mate::Arguments* args); + base::string16 GetApplicationNameForProtocol(const GURL& url); + // Set/Get the badge count. bool SetBadgeCount(int count); int GetBadgeCount(); @@ -179,7 +181,7 @@ class Browser : public WindowListObserver { bool DockIsVisible(); // Set docks' menu. - void DockSetMenu(AtomMenuModel* model); + void DockSetMenu(ElectronMenuModel* model); // Set docks' icon. void DockSetIcon(const gfx::Image& image); @@ -245,10 +247,6 @@ class Browser : public WindowListObserver { void OnAccessibilitySupportChanged(); - // Request basic auth login. - void RequestLogin(scoped_refptr login_handler, - std::unique_ptr request_details); - void PreMainMessageLoopRun(); // Stores the supplied |quit_closure|, to be run when the last Browser diff --git a/shell/browser/browser_linux.cc b/shell/browser/browser_linux.cc index 34994296dfba6..0b3ed121b1601 100644 --- a/shell/browser/browser_linux.cc +++ b/shell/browser/browser_linux.cc @@ -17,7 +17,7 @@ #if defined(USE_X11) #include "chrome/browser/ui/libgtkui/gtk_util.h" -#include "chrome/browser/ui/libgtkui/unity_service.h" +#include "shell/browser/linux/unity_service.h" #endif namespace electron { @@ -25,6 +25,14 @@ namespace electron { const char kXdgSettings[] = "xdg-settings"; const char kXdgSettingsDefaultSchemeHandler[] = "default-url-scheme-handler"; +// The use of the ForTesting flavors is a hack workaround to avoid having to +// patch these as friends into the associated guard classes. +class LaunchXdgUtilityScopedAllowBaseSyncPrimitives + : public base::ScopedAllowBaseSyncPrimitivesForTesting {}; + +class GetXdgAppOutputScopedAllowBlocking + : public base::ScopedAllowBlockingForTesting {}; + bool LaunchXdgUtility(const std::vector& argv, int* exit_code) { *exit_code = EXIT_FAILURE; int devnull = open("/dev/null", O_RDONLY); @@ -39,27 +47,44 @@ bool LaunchXdgUtility(const std::vector& argv, int* exit_code) { if (!process.IsValid()) return false; + LaunchXdgUtilityScopedAllowBaseSyncPrimitives allow_base_sync_primitives; return process.WaitForExit(exit_code); } +base::Optional GetXdgAppOutput( + const std::vector& argv) { + std::string reply; + int success_code; + GetXdgAppOutputScopedAllowBlocking allow_blocking; + bool ran_ok = base::GetAppOutputWithExitCode(base::CommandLine(argv), &reply, + &success_code); + + if (!ran_ok || success_code != EXIT_SUCCESS) + return base::Optional(); + + return base::make_optional(reply); +} + bool SetDefaultWebClient(const std::string& protocol) { std::unique_ptr env(base::Environment::Create()); - std::vector argv; - argv.emplace_back(kXdgSettings); - argv.emplace_back("set"); + std::vector argv = {kXdgSettings, "set"}; if (!protocol.empty()) { argv.emplace_back(kXdgSettingsDefaultSchemeHandler); - argv.push_back(protocol); + argv.emplace_back(protocol); + } + std::string desktop_name; + if (!env->GetVar("CHROME_DESKTOP", &desktop_name)) { + return false; } - argv.push_back(libgtkui::GetDesktopName(env.get())); + argv.emplace_back(desktop_name); int exit_code; bool ran_ok = LaunchXdgUtility(argv, &exit_code); return ran_ok && exit_code == EXIT_SUCCESS; } -void Browser::Focus() { +void Browser::Focus(mate::Arguments* args) { // Focus on the first visible window. for (auto* const window : WindowList::GetWindows()) { if (window->IsVisible()) { @@ -87,24 +112,18 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol, if (protocol.empty()) return false; - std::vector argv; - argv.emplace_back(kXdgSettings); - argv.emplace_back("check"); - argv.emplace_back(kXdgSettingsDefaultSchemeHandler); - argv.push_back(protocol); - argv.push_back(libgtkui::GetDesktopName(env.get())); - - std::string reply; - int success_code; - bool ran_ok = base::GetAppOutputWithExitCode(base::CommandLine(argv), &reply, - &success_code); - - if (!ran_ok || success_code != EXIT_SUCCESS) + std::string desktop_name; + if (!env->GetVar("CHROME_DESKTOP", &desktop_name)) + return false; + const std::vector argv = {kXdgSettings, "check", + kXdgSettingsDefaultSchemeHandler, + protocol, desktop_name}; + const auto output = GetXdgAppOutput(argv); + if (!output) return false; // Allow any reply that starts with "yes". - return base::StartsWith(reply, "yes", base::CompareCase::SENSITIVE) ? true - : false; + return base::StartsWith(output.value(), "yes", base::CompareCase::SENSITIVE); } // Todo implement @@ -113,6 +132,14 @@ bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol, return false; } +base::string16 Browser::GetApplicationNameForProtocol(const GURL& url) { + const std::vector argv = { + "xdg-mime", "query", "default", + std::string("x-scheme-handler/") + url.scheme()}; + + return base::ASCIIToUTF16(GetXdgAppOutput(argv).value_or(std::string())); +} + bool Browser::SetBadgeCount(int count) { if (IsUnityRunning()) { unity::SetDownloadCount(count); diff --git a/shell/browser/browser_mac.mm b/shell/browser/browser_mac.mm index ebf82554904e8..16eadde3fd467 100644 --- a/shell/browser/browser_mac.mm +++ b/shell/browser/browser_mac.mm @@ -13,13 +13,17 @@ #include "base/mac/scoped_cftyperef.h" #include "base/strings/string_number_conversions.h" #include "base/strings/sys_string_conversions.h" +#include "native_mate/dictionary.h" #include "net/base/mac/url_conversions.h" -#include "shell/browser/mac/atom_application.h" -#include "shell/browser/mac/atom_application_delegate.h" #include "shell/browser/mac/dict_util.h" +#include "shell/browser/mac/electron_application.h" +#include "shell/browser/mac/electron_application_delegate.h" #include "shell/browser/native_window.h" #include "shell/browser/window_list.h" #include "shell/common/application_info.h" +#include "shell/common/gin_helper/arguments.h" +#include "shell/common/gin_helper/dictionary.h" +#include "shell/common/gin_helper/error_thrower.h" #include "shell/common/platform_util.h" #include "shell/common/promise_util.h" #include "ui/gfx/image/image.h" @@ -31,8 +35,20 @@ [[AtomApplication sharedApplication] setShutdownHandler:std::move(handler)]; } -void Browser::Focus() { - [[AtomApplication sharedApplication] activateIgnoringOtherApps:NO]; +void Browser::Focus(mate::Arguments* args) { + mate::Dictionary opts; + bool steal_focus = false; + + if (args->GetNext(&opts)) { + gin_helper::ErrorThrower thrower(args->isolate()); + if (!opts.Get("steal", &steal_focus)) { + thrower.ThrowError( + "Expected options object to contain a 'steal' boolean property"); + return; + } + } + + [[AtomApplication sharedApplication] activateIgnoringOtherApps:steal_focus]; } void Browser::Hide() { @@ -131,6 +147,22 @@ return result == NSOrderedSame; } +base::string16 Browser::GetApplicationNameForProtocol(const GURL& url) { + NSURL* ns_url = [NSURL + URLWithString:base::SysUTF8ToNSString(url.possibly_invalid_spec())]; + base::ScopedCFTypeRef out_err; + base::ScopedCFTypeRef openingApp(LSCopyDefaultApplicationURLForURL( + (CFURLRef)ns_url, kLSRolesAll, out_err.InitializeInto())); + if (out_err) { + // likely kLSApplicationNotFoundErr + return base::string16(); + } + NSString* appPath = [base::mac::CFToNSCast(openingApp.get()) path]; + NSString* appDisplayName = + [[NSFileManager defaultManager] displayNameAtPath:appPath]; + return base::SysNSStringToUTF16(appDisplayName); +} + void Browser::SetAppUserModelID(const base::string16& name) {} bool Browser::SetBadgeCount(int count) { @@ -345,9 +377,9 @@ void RemoveFromLoginItems() { return handle; } -void Browser::DockSetMenu(AtomMenuModel* model) { - AtomApplicationDelegate* delegate = - (AtomApplicationDelegate*)[NSApp delegate]; +void Browser::DockSetMenu(ElectronMenuModel* model) { + ElectronApplicationDelegate* delegate = + (ElectronApplicationDelegate*)[NSApp delegate]; [delegate setApplicationDockMenu:model]; } @@ -360,11 +392,18 @@ void RemoveFromLoginItems() { NSDictionary* options = DictionaryValueToNSDictionary(about_panel_options_); // Credits must be a NSAttributedString instead of NSString - id credits = options[@"Credits"]; + NSString* credits = (NSString*)options[@"Credits"]; if (credits != nil) { - NSMutableDictionary* mutable_options = [options mutableCopy]; - mutable_options[@"Credits"] = [[[NSAttributedString alloc] - initWithString:(NSString*)credits] autorelease]; + base::scoped_nsobject mutable_options( + [options mutableCopy]); + base::scoped_nsobject creditString( + [[NSAttributedString alloc] + initWithString:credits + attributes:@{ + NSForegroundColorAttributeName : [NSColor textColor] + }]); + + [mutable_options setValue:creditString forKey:@"Credits"]; options = [NSDictionary dictionaryWithDictionary:mutable_options]; } diff --git a/shell/browser/browser_observer.h b/shell/browser/browser_observer.h index ab27ea992c4f3..9964924ec052b 100644 --- a/shell/browser/browser_observer.h +++ b/shell/browser/browser_observer.h @@ -49,10 +49,6 @@ class BrowserObserver : public base::CheckedObserver { virtual void OnWillFinishLaunching() {} virtual void OnFinishLaunching(const base::DictionaryValue& launch_info) {} - // The browser requests HTTP login. - virtual void OnLogin(scoped_refptr login_handler, - const base::DictionaryValue& request_details) {} - // The browser's accessibility suppport has changed. virtual void OnAccessibilitySupportChanged() {} diff --git a/shell/browser/browser_process_impl.cc b/shell/browser/browser_process_impl.cc index 9ec48cf948620..107b1d2f3ff30 100644 --- a/shell/browser/browser_process_impl.cc +++ b/shell/browser/browser_process_impl.cc @@ -267,11 +267,6 @@ resource_coordinator::TabManager* BrowserProcessImpl::GetTabManager() { return nullptr; } -shell_integration::DefaultWebClientState -BrowserProcessImpl::CachedDefaultWebClientState() { - return shell_integration::UNKNOWN_DEFAULT; -} - void BrowserProcessImpl::SetApplicationLocale(const std::string& locale) { locale_ = locale; } diff --git a/shell/browser/browser_process_impl.h b/shell/browser/browser_process_impl.h index 9904e14aaf672..b01d732e3a7d7 100644 --- a/shell/browser/browser_process_impl.h +++ b/shell/browser/browser_process_impl.h @@ -90,8 +90,6 @@ class BrowserProcessImpl : public BrowserProcess { resource_coordinator::ResourceCoordinatorParts* resource_coordinator_parts() override; resource_coordinator::TabManager* GetTabManager() override; - shell_integration::DefaultWebClientState CachedDefaultWebClientState() - override; void CreateDevToolsProtocolHandler() override {} void CreateDevToolsAutoOpener() override {} void set_background_mode_manager_for_test( diff --git a/shell/browser/browser_win.cc b/shell/browser/browser_win.cc index b272b179a4cda..17143691bd843 100644 --- a/shell/browser/browser_win.cc +++ b/shell/browser/browser_win.cc @@ -71,6 +71,68 @@ bool GetProtocolLaunchPath(mate::Arguments* args, base::string16* exe) { return true; } +// Windows treats a given scheme as an Internet scheme only if its registry +// entry has a "URL Protocol" key. Check this, otherwise we allow ProgIDs to be +// used as custom protocols which leads to security bugs. +bool IsValidCustomProtocol(const base::string16& scheme) { + if (scheme.empty()) + return false; + base::win::RegKey cmd_key(HKEY_CLASSES_ROOT, scheme.c_str(), KEY_QUERY_VALUE); + return cmd_key.Valid() && cmd_key.HasValue(L"URL Protocol"); +} + +// Windows 8 introduced a new protocol->executable binding system which cannot +// be retrieved in the HKCR registry subkey method implemented below. We call +// AssocQueryString with the new Win8-only flag ASSOCF_IS_PROTOCOL instead. +base::string16 GetAppForProtocolUsingAssocQuery(const GURL& url) { + const base::string16 url_scheme = base::ASCIIToUTF16(url.scheme()); + if (!IsValidCustomProtocol(url_scheme)) + return base::string16(); + + // Query AssocQueryString for a human-readable description of the program + // that will be invoked given the provided URL spec. This is used only to + // populate the external protocol dialog box the user sees when invoking + // an unknown external protocol. + wchar_t out_buffer[1024]; + DWORD buffer_size = base::size(out_buffer); + HRESULT hr = + AssocQueryString(ASSOCF_IS_PROTOCOL, ASSOCSTR_FRIENDLYAPPNAME, + url_scheme.c_str(), NULL, out_buffer, &buffer_size); + if (FAILED(hr)) { + DLOG(WARNING) << "AssocQueryString failed!"; + return base::string16(); + } + return base::string16(out_buffer); +} + +base::string16 GetAppForProtocolUsingRegistry(const GURL& url) { + const base::string16 url_scheme = base::ASCIIToUTF16(url.scheme()); + if (!IsValidCustomProtocol(url_scheme)) + return base::string16(); + + // First, try and extract the application's display name. + base::string16 command_to_launch; + base::win::RegKey cmd_key_name(HKEY_CLASSES_ROOT, url_scheme.c_str(), + KEY_READ); + if (cmd_key_name.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS && + !command_to_launch.empty()) { + return command_to_launch; + } + + // Otherwise, parse the command line in the registry, and return the basename + // of the program path if it exists. + const base::string16 cmd_key_path = url_scheme + L"\\shell\\open\\command"; + base::win::RegKey cmd_key_exe(HKEY_CLASSES_ROOT, cmd_key_path.c_str(), + KEY_READ); + if (cmd_key_exe.ReadValue(NULL, &command_to_launch) == ERROR_SUCCESS) { + base::CommandLine command_line( + base::CommandLine::FromString(command_to_launch)); + return command_line.GetProgram().BaseName().value(); + } + + return base::string16(); +} + bool FormatCommandLineString(base::string16* exe, const std::vector& launch_args) { if (exe->empty() && !GetProcessExecPath(exe)) { @@ -101,7 +163,7 @@ Browser::UserTask::UserTask() = default; Browser::UserTask::UserTask(const UserTask&) = default; Browser::UserTask::~UserTask() = default; -void Browser::Focus() { +void Browser::Focus(mate::Arguments* args) { // On Windows we just focus on the first window found for this process. DWORD pid = GetCurrentProcessId(); EnumWindows(&WindowsEnumerationHandler, reinterpret_cast(&pid)); @@ -293,6 +355,17 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol, return keyVal == exe; } +base::string16 Browser::GetApplicationNameForProtocol(const GURL& url) { + // Windows 8 or above has a new protocol association query. + if (base::win::GetVersion() >= base::win::Version::WIN8) { + base::string16 application_name = GetAppForProtocolUsingAssocQuery(url); + if (!application_name.empty()) + return application_name; + } + + return GetAppForProtocolUsingRegistry(url); +} + bool Browser::SetBadgeCount(int count) { return false; } diff --git a/shell/browser/common_web_contents_delegate.cc b/shell/browser/common_web_contents_delegate.cc index 0e2849c463de9..f3e70e0c5c82e 100644 --- a/shell/browser/common_web_contents_delegate.cc +++ b/shell/browser/common_web_contents_delegate.cc @@ -31,15 +31,15 @@ #include "content/public/browser/security_style_explanation.h" #include "content/public/browser/security_style_explanations.h" #include "printing/buildflags/buildflags.h" -#include "shell/browser/atom_browser_client.h" -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/electron_browser_client.h" +#include "shell/browser/electron_browser_context.h" #include "shell/browser/native_window.h" #include "shell/browser/ui/file_dialog.h" #include "shell/browser/web_contents_preferences.h" #include "shell/browser/web_dialog_helper.h" -#include "shell/common/atom_constants.h" +#include "shell/common/electron_constants.h" #include "shell/common/options_switches.h" -#include "storage/browser/fileapi/isolated_context.h" +#include "storage/browser/file_system/isolated_context.h" #if BUILDFLAG(ENABLE_COLOR_CHOOSER) #include "chrome/browser/ui/color_chooser.h" @@ -148,7 +148,7 @@ void AppendToFile(const base::FilePath& path, const std::string& content) { PrefService* GetPrefService(content::WebContents* web_contents) { auto* context = web_contents->GetBrowserContext(); - return static_cast(context)->prefs(); + return static_cast(context)->prefs(); } std::map GetAddedFileSystemPaths( @@ -186,7 +186,7 @@ CommonWebContentsDelegate::~CommonWebContentsDelegate() = default; void CommonWebContentsDelegate::InitWithWebContents( content::WebContents* web_contents, - AtomBrowserContext* browser_context, + ElectronBrowserContext* browser_context, bool is_guest) { browser_context_ = browser_context; web_contents->SetDelegate(this); @@ -242,7 +242,7 @@ void CommonWebContentsDelegate::ResetManagedWebContents(bool async) { base::ThreadTaskRunnerHandle::Get()->PostNonNestableTask( FROM_HERE, base::BindOnce( - [](scoped_refptr browser_context, + [](scoped_refptr browser_context, std::unique_ptr web_contents) { web_contents.reset(); }, @@ -282,8 +282,21 @@ content::WebContents* CommonWebContentsDelegate::OpenURLFromTab( load_url_params.should_replace_current_entry = params.should_replace_current_entry; load_url_params.is_renderer_initiated = params.is_renderer_initiated; + load_url_params.started_from_context_menu = params.started_from_context_menu; load_url_params.initiator_origin = params.initiator_origin; - load_url_params.should_clear_history_list = true; + load_url_params.source_site_instance = params.source_site_instance; + load_url_params.frame_tree_node_id = params.frame_tree_node_id; + load_url_params.redirect_chain = params.redirect_chain; + load_url_params.has_user_gesture = params.user_gesture; + load_url_params.blob_url_loader_factory = params.blob_url_loader_factory; + load_url_params.href_translate = params.href_translate; + load_url_params.reload_type = params.reload_type; + + if (params.post_data) { + load_url_params.load_type = + content::NavigationController::LOAD_TYPE_HTTP_POST; + load_url_params.post_data = params.post_data; + } source->GetController().LoadURLWithParams(load_url_params); return source; @@ -328,7 +341,7 @@ void CommonWebContentsDelegate::EnumerateDirectory( void CommonWebContentsDelegate::EnterFullscreenModeForTab( content::WebContents* source, const GURL& origin, - const blink::WebFullscreenOptions& options) { + const blink::mojom::FullscreenOptions& options) { if (!owner_window_) return; if (IsFullscreenForTabOrPending(source)) { @@ -345,6 +358,13 @@ void CommonWebContentsDelegate::ExitFullscreenModeForTab( return; SetHtmlApiFullscreen(false); owner_window_->NotifyWindowLeaveHtmlFullScreen(); + + if (native_fullscreen_) { + // Explicitly trigger a view resize, as the size is not actually changing if + // the browser is fullscreened, too. Chrome does this indirectly from + // `chrome/browser/ui/exclusive_access/fullscreen_controller.cc`. + source->GetRenderViewHost()->GetWidget()->SynchronizeVisualProperties(); + } } bool CommonWebContentsDelegate::IsFullscreenForTabOrPending( @@ -352,7 +372,7 @@ bool CommonWebContentsDelegate::IsFullscreenForTabOrPending( return html_fullscreen_; } -blink::WebSecurityStyle CommonWebContentsDelegate::GetSecurityStyle( +blink::SecurityStyle CommonWebContentsDelegate::GetSecurityStyle( content::WebContents* web_contents, content::SecurityStyleExplanations* security_style_explanations) { SecurityStateTabHelper* helper = diff --git a/shell/browser/common_web_contents_delegate.h b/shell/browser/common_web_contents_delegate.h index 756fbfc2b8ccd..456e86274e18e 100644 --- a/shell/browser/common_web_contents_delegate.h +++ b/shell/browser/common_web_contents_delegate.h @@ -24,7 +24,7 @@ class SequencedTaskRunner; namespace electron { -class AtomBrowserContext; +class ElectronBrowserContext; class NativeWindow; class WebDialogHelper; @@ -42,7 +42,7 @@ class CommonWebContentsDelegate : public content::WebContentsDelegate, // Creates a InspectableWebContents object and takes onwership of // |web_contents|. void InitWithWebContents(content::WebContents* web_contents, - AtomBrowserContext* browser_context, + ElectronBrowserContext* browser_context, bool is_guest); // Set the window as owner window. @@ -92,10 +92,10 @@ class CommonWebContentsDelegate : public content::WebContentsDelegate, void EnterFullscreenModeForTab( content::WebContents* source, const GURL& origin, - const blink::WebFullscreenOptions& options) override; + const blink::mojom::FullscreenOptions& options) override; void ExitFullscreenModeForTab(content::WebContents* source) override; bool IsFullscreenForTabOrPending(const content::WebContents* source) override; - blink::WebSecurityStyle GetSecurityStyle( + blink::SecurityStyle GetSecurityStyle( content::WebContents* web_contents, content::SecurityStyleExplanations* explanations) override; bool TakeFocus(content::WebContents* source, bool reverse) override; @@ -173,7 +173,7 @@ class CommonWebContentsDelegate : public content::WebContentsDelegate, scoped_refptr devtools_file_system_indexer_; // Make sure BrowserContext is alwasys destroyed after WebContents. - scoped_refptr browser_context_; + scoped_refptr browser_context_; // The stored InspectableWebContents object. // Notice that web_contents_ must be placed after dialog_manager_, so we can diff --git a/shell/browser/common_web_contents_delegate_views.cc b/shell/browser/common_web_contents_delegate_views.cc index 7c6bc9e26d12c..45c785684d4f7 100644 --- a/shell/browser/common_web_contents_delegate_views.cc +++ b/shell/browser/common_web_contents_delegate_views.cc @@ -6,7 +6,7 @@ #include "base/strings/string_util.h" #include "content/public/browser/native_web_keyboard_event.h" -#include "shell/browser/api/atom_api_web_contents_view.h" +#include "shell/browser/api/electron_api_web_contents_view.h" #include "shell/browser/native_window_views.h" #include "shell/browser/web_contents_preferences.h" #include "ui/events/keycodes/keyboard_codes.h" diff --git a/shell/browser/cookie_change_notifier.cc b/shell/browser/cookie_change_notifier.cc index 92448dd726881..84404e3e79b77 100644 --- a/shell/browser/cookie_change_notifier.cc +++ b/shell/browser/cookie_change_notifier.cc @@ -10,23 +10,24 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" #include "net/cookies/canonical_cookie.h" -#include "shell/browser/atom_browser_context.h" -#include "shell/browser/net/cookie_details.h" +#include "shell/browser/electron_browser_context.h" using content::BrowserThread; namespace electron { -CookieChangeNotifier::CookieChangeNotifier(AtomBrowserContext* browser_context) +CookieChangeNotifier::CookieChangeNotifier( + ElectronBrowserContext* browser_context) : browser_context_(browser_context), receiver_(this) { StartListening(); } CookieChangeNotifier::~CookieChangeNotifier() = default; -std::unique_ptr::Subscription> +std::unique_ptr< + base::CallbackList::Subscription> CookieChangeNotifier::RegisterCookieChangeCallback( - const base::Callback& cb) { + const base::Callback& cb) { DCHECK_CURRENTLY_ON(BrowserThread::UI); return cookie_change_sub_list_.Add(cb); @@ -57,14 +58,10 @@ void CookieChangeNotifier::OnConnectionError() { StartListening(); } -void CookieChangeNotifier::OnCookieChange( - const net::CanonicalCookie& cookie, - network::mojom::CookieChangeCause cause) { +void CookieChangeNotifier::OnCookieChange(const net::CookieChangeInfo& change) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - CookieDetails cookie_details( - &cookie, cause != network::mojom::CookieChangeCause::INSERTED, cause); - cookie_change_sub_list_.Notify(&cookie_details); + cookie_change_sub_list_.Notify(change); } } // namespace electron diff --git a/shell/browser/cookie_change_notifier.h b/shell/browser/cookie_change_notifier.h index 4de03d1a12084..14edc9fe8e28b 100644 --- a/shell/browser/cookie_change_notifier.h +++ b/shell/browser/cookie_change_notifier.h @@ -10,34 +10,35 @@ #include "base/callback_list.h" #include "base/macros.h" #include "mojo/public/cpp/bindings/receiver.h" +#include "net/cookies/cookie_change_dispatcher.h" #include "services/network/public/mojom/cookie_manager.mojom.h" namespace electron { -class AtomBrowserContext; -struct CookieDetails; +class ElectronBrowserContext; // Sends cookie-change notifications on the UI thread. class CookieChangeNotifier : public network::mojom::CookieChangeListener { public: - explicit CookieChangeNotifier(AtomBrowserContext* browser_context); + explicit CookieChangeNotifier(ElectronBrowserContext* browser_context); ~CookieChangeNotifier() override; // Register callbacks that needs to notified on any cookie store changes. - std::unique_ptr::Subscription> + std::unique_ptr::Subscription> RegisterCookieChangeCallback( - const base::Callback& cb); + const base::Callback& cb); private: void StartListening(); void OnConnectionError(); // network::mojom::CookieChangeListener implementation. - void OnCookieChange(const net::CanonicalCookie& cookie, - network::mojom::CookieChangeCause cause) override; + void OnCookieChange(const net::CookieChangeInfo& change) override; - AtomBrowserContext* browser_context_; - base::CallbackList cookie_change_sub_list_; + ElectronBrowserContext* browser_context_; + base::CallbackList + cookie_change_sub_list_; mojo::Receiver receiver_; diff --git a/shell/browser/atom_autofill_driver.cc b/shell/browser/electron_autofill_driver.cc similarity index 92% rename from shell/browser/atom_autofill_driver.cc rename to shell/browser/electron_autofill_driver.cc index c3ca0ac8d316d..4567816ee3daf 100644 --- a/shell/browser/atom_autofill_driver.cc +++ b/shell/browser/electron_autofill_driver.cc @@ -2,14 +2,14 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_autofill_driver.h" +#include "shell/browser/electron_autofill_driver.h" #include #include #include "content/public/browser/render_widget_host_view.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/native_window.h" namespace electron { diff --git a/shell/browser/atom_autofill_driver.h b/shell/browser/electron_autofill_driver.h similarity index 84% rename from shell/browser/atom_autofill_driver.h rename to shell/browser/electron_autofill_driver.h index b30521491d6b6..8cf68b68bfd5e 100644 --- a/shell/browser/atom_autofill_driver.h +++ b/shell/browser/electron_autofill_driver.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_AUTOFILL_DRIVER_H_ -#define SHELL_BROWSER_ATOM_AUTOFILL_DRIVER_H_ +#ifndef SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_ +#define SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_ #include #include @@ -41,4 +41,4 @@ class AutofillDriver : public mojom::ElectronAutofillDriver { } // namespace electron -#endif // SHELL_BROWSER_ATOM_AUTOFILL_DRIVER_H_ +#endif // SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_H_ diff --git a/shell/browser/atom_autofill_driver_factory.cc b/shell/browser/electron_autofill_driver_factory.cc similarity index 93% rename from shell/browser/atom_autofill_driver_factory.cc rename to shell/browser/electron_autofill_driver_factory.cc index efe0596c9444d..4e95cfb427894 100644 --- a/shell/browser/atom_autofill_driver_factory.cc +++ b/shell/browser/electron_autofill_driver_factory.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_autofill_driver_factory.h" +#include "shell/browser/electron_autofill_driver_factory.h" #include #include @@ -13,7 +13,7 @@ #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" -#include "shell/browser/atom_autofill_driver.h" +#include "shell/browser/electron_autofill_driver.h" namespace electron { diff --git a/shell/browser/atom_autofill_driver_factory.h b/shell/browser/electron_autofill_driver_factory.h similarity index 87% rename from shell/browser/atom_autofill_driver_factory.h rename to shell/browser/electron_autofill_driver_factory.h index bcf0b4a29cacc..23784fc9ec1e9 100644 --- a/shell/browser/atom_autofill_driver_factory.h +++ b/shell/browser/electron_autofill_driver_factory.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_AUTOFILL_DRIVER_FACTORY_H_ -#define SHELL_BROWSER_ATOM_AUTOFILL_DRIVER_FACTORY_H_ +#ifndef SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_ +#define SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_ #include #include @@ -54,4 +54,4 @@ class AutofillDriverFactory } // namespace electron -#endif // SHELL_BROWSER_ATOM_AUTOFILL_DRIVER_FACTORY_H_ +#endif // SHELL_BROWSER_ELECTRON_AUTOFILL_DRIVER_FACTORY_H_ diff --git a/shell/browser/atom_browser_client.cc b/shell/browser/electron_browser_client.cc similarity index 69% rename from shell/browser/atom_browser_client.cc rename to shell/browser/electron_browser_client.cc index af7f0adaf2898..d21ef890ad613 100644 --- a/shell/browser/atom_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_browser_client.h" +#include "shell/browser/electron_browser_client.h" #if defined(OS_WIN) #include @@ -26,14 +26,16 @@ #include "chrome/browser/browser_process.h" #include "chrome/common/chrome_version.h" #include "components/net_log/chrome_net_log.h" +#include "components/network_hints/common/network_hints.mojom.h" #include "content/public/browser/browser_ppapi_host.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/client_certificate_delegate.h" +#include "content/public/browser/login_delegate.h" +#include "content/public/browser/overlay_window.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/site_instance.h" -#include "content/public/browser/web_contents.h" #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "content/public/common/service_names.mojom.h" @@ -41,6 +43,7 @@ #include "content/public/common/web_preferences.h" #include "electron/buildflags/buildflags.h" #include "electron/grit/electron_resources.h" +#include "extensions/browser/extension_navigation_ui_data.h" #include "net/base/escape.h" #include "net/ssl/ssl_cert_request_info.h" #include "ppapi/host/ppapi_host.h" @@ -48,30 +51,32 @@ #include "services/device/public/cpp/geolocation/location_provider.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/resource_request_body.h" +#include "services/service_manager/public/cpp/binder_map.h" #include "shell/app/manifests.h" -#include "shell/browser/api/atom_api_app.h" -#include "shell/browser/api/atom_api_protocol_ns.h" -#include "shell/browser/api/atom_api_session.h" -#include "shell/browser/api/atom_api_web_contents.h" -#include "shell/browser/api/atom_api_web_request_ns.h" -#include "shell/browser/atom_autofill_driver_factory.h" -#include "shell/browser/atom_browser_context.h" -#include "shell/browser/atom_browser_main_parts.h" -#include "shell/browser/atom_navigation_throttle.h" -#include "shell/browser/atom_paths.h" -#include "shell/browser/atom_quota_permission_context.h" -#include "shell/browser/atom_speech_recognition_manager_delegate.h" +#include "shell/browser/api/electron_api_app.h" +#include "shell/browser/api/electron_api_protocol_ns.h" +#include "shell/browser/api/electron_api_session.h" +#include "shell/browser/api/electron_api_web_contents.h" +#include "shell/browser/api/electron_api_web_request_ns.h" #include "shell/browser/child_web_contents_tracker.h" +#include "shell/browser/electron_autofill_driver_factory.h" +#include "shell/browser/electron_browser_context.h" +#include "shell/browser/electron_browser_main_parts.h" +#include "shell/browser/electron_navigation_throttle.h" +#include "shell/browser/electron_paths.h" +#include "shell/browser/electron_quota_permission_context.h" +#include "shell/browser/electron_speech_recognition_manager_delegate.h" #include "shell/browser/font_defaults.h" #include "shell/browser/media/media_capture_devices_dispatcher.h" #include "shell/browser/native_window.h" #include "shell/browser/net/network_context_service.h" #include "shell/browser/net/network_context_service_factory.h" #include "shell/browser/net/proxying_url_loader_factory.h" +#include "shell/browser/net/proxying_websocket.h" #include "shell/browser/net/system_network_context_manager.h" +#include "shell/browser/network_hints_handler_impl.h" #include "shell/browser/notifications/notification_presenter.h" #include "shell/browser/notifications/platform_notification_service.h" -#include "shell/browser/renderer_host/electron_render_message_filter.h" #include "shell/browser/session_preferences.h" #include "shell/browser/ui/devtools_manager_delegate.h" #include "shell/browser/web_contents_permission_helper.h" @@ -95,11 +100,15 @@ #include "net/ssl/client_cert_store_win.h" #elif defined(OS_MACOSX) #include "net/ssl/client_cert_store_mac.h" -#include "services/audio/public/mojom/constants.mojom.h" #elif defined(USE_OPENSSL) #include "net/ssl/client_cert_store.h" #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "chrome/browser/spellchecker/spell_check_host_chrome_impl.h" // nogncheck +#include "components/spellcheck/common/spellcheck.mojom.h" // nogncheck +#endif + #if BUILDFLAG(ENABLE_PEPPER_FLASH) #include "chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h" #endif // BUILDFLAG(ENABLE_PEPPER_FLASH) @@ -141,7 +150,7 @@ bool IsSameWebSite(content::BrowserContext* browser_context, site_instance->GetSiteURL()); } -AtomBrowserClient* g_browser_client = nullptr; +ElectronBrowserClient* g_browser_client = nullptr; base::LazyInstance::DestructorAtExit g_io_thread_application_locale = LAZY_INSTANCE_INITIALIZER; @@ -153,6 +162,12 @@ void SetApplicationLocaleOnIOThread(const std::string& locale) { g_io_thread_application_locale.Get() = locale; } +void BindNetworkHintsHandler( + content::RenderFrameHost* frame_host, + mojo::PendingReceiver receiver) { + NetworkHintsHandlerImpl::Create(frame_host, std::move(receiver)); +} + #if defined(OS_WIN) const base::FilePath::StringPieceType kPathDelimiter = FILE_PATH_LITERAL(";"); #else @@ -162,16 +177,16 @@ const base::FilePath::StringPieceType kPathDelimiter = FILE_PATH_LITERAL(":"); } // namespace // static -void AtomBrowserClient::SuppressRendererProcessRestartForOnce() { +void ElectronBrowserClient::SuppressRendererProcessRestartForOnce() { g_suppress_renderer_process_restart = true; } -AtomBrowserClient* AtomBrowserClient::Get() { +ElectronBrowserClient* ElectronBrowserClient::Get() { return g_browser_client; } // static -void AtomBrowserClient::SetApplicationLocale(const std::string& locale) { +void ElectronBrowserClient::SetApplicationLocale(const std::string& locale) { if (!BrowserThread::IsThreadInitialized(BrowserThread::IO) || !base::PostTask( FROM_HERE, {BrowserThread::IO}, @@ -181,17 +196,17 @@ void AtomBrowserClient::SetApplicationLocale(const std::string& locale) { *g_application_locale = locale; } -AtomBrowserClient::AtomBrowserClient() { +ElectronBrowserClient::ElectronBrowserClient() { DCHECK(!g_browser_client); g_browser_client = this; } -AtomBrowserClient::~AtomBrowserClient() { +ElectronBrowserClient::~ElectronBrowserClient() { DCHECK(g_browser_client); g_browser_client = nullptr; } -content::WebContents* AtomBrowserClient::GetWebContentsFromProcessID( +content::WebContents* ElectronBrowserClient::GetWebContentsFromProcessID( int process_id) { // If the process is a pending process, we should use the web contents // for the frame host passed into RegisterPendingProcess. @@ -204,7 +219,7 @@ content::WebContents* AtomBrowserClient::GetWebContentsFromProcessID( return WebContentsPreferences::GetWebContentsFromProcessID(process_id); } -bool AtomBrowserClient::ShouldForceNewSiteInstance( +bool ElectronBrowserClient::ShouldForceNewSiteInstance( content::RenderFrameHost* current_rfh, content::RenderFrameHost* speculative_rfh, content::BrowserContext* browser_context, @@ -247,7 +262,7 @@ bool AtomBrowserClient::ShouldForceNewSiteInstance( return !IsSameWebSite(browser_context, current_instance, url); } -bool AtomBrowserClient::NavigationWasRedirectedCrossSite( +bool ElectronBrowserClient::NavigationWasRedirectedCrossSite( content::BrowserContext* browser_context, content::SiteInstance* current_instance, content::SiteInstance* speculative_instance, @@ -266,36 +281,36 @@ bool AtomBrowserClient::NavigationWasRedirectedCrossSite( return navigation_was_redirected; } -void AtomBrowserClient::AddProcessPreferences( +void ElectronBrowserClient::AddProcessPreferences( int process_id, - AtomBrowserClient::ProcessPreferences prefs) { + ElectronBrowserClient::ProcessPreferences prefs) { process_preferences_[process_id] = prefs; } -void AtomBrowserClient::RemoveProcessPreferences(int process_id) { +void ElectronBrowserClient::RemoveProcessPreferences(int process_id) { process_preferences_.erase(process_id); } -bool AtomBrowserClient::IsProcessObserved(int process_id) const { +bool ElectronBrowserClient::IsProcessObserved(int process_id) const { return process_preferences_.find(process_id) != process_preferences_.end(); } -bool AtomBrowserClient::IsRendererSandboxed(int process_id) const { +bool ElectronBrowserClient::IsRendererSandboxed(int process_id) const { auto it = process_preferences_.find(process_id); return it != process_preferences_.end() && it->second.sandbox; } -bool AtomBrowserClient::RendererUsesNativeWindowOpen(int process_id) const { +bool ElectronBrowserClient::RendererUsesNativeWindowOpen(int process_id) const { auto it = process_preferences_.find(process_id); return it != process_preferences_.end() && it->second.native_window_open; } -bool AtomBrowserClient::RendererDisablesPopups(int process_id) const { +bool ElectronBrowserClient::RendererDisablesPopups(int process_id) const { auto it = process_preferences_.find(process_id); return it != process_preferences_.end() && it->second.disable_popups; } -std::string AtomBrowserClient::GetAffinityPreference( +std::string ElectronBrowserClient::GetAffinityPreference( content::RenderFrameHost* rfh) const { auto* web_contents = content::WebContents::FromRenderFrameHost(rfh); auto* web_preferences = WebContentsPreferences::From(web_contents); @@ -309,7 +324,7 @@ std::string AtomBrowserClient::GetAffinityPreference( return affinity; } -content::SiteInstance* AtomBrowserClient::GetSiteInstanceFromAffinity( +content::SiteInstance* ElectronBrowserClient::GetSiteInstanceFromAffinity( content::BrowserContext* browser_context, const GURL& url, content::RenderFrameHost* rfh) const { @@ -326,7 +341,7 @@ content::SiteInstance* AtomBrowserClient::GetSiteInstanceFromAffinity( return nullptr; } -void AtomBrowserClient::ConsiderSiteInstanceForAffinity( +void ElectronBrowserClient::ConsiderSiteInstanceForAffinity( content::RenderFrameHost* rfh, content::SiteInstance* site_instance) { std::string affinity = GetAffinityPreference(rfh); @@ -335,11 +350,11 @@ void AtomBrowserClient::ConsiderSiteInstanceForAffinity( } } -bool AtomBrowserClient::IsRendererSubFrame(int process_id) const { +bool ElectronBrowserClient::IsRendererSubFrame(int process_id) const { return base::Contains(renderer_is_subframe_, process_id); } -void AtomBrowserClient::RenderProcessWillLaunch( +void ElectronBrowserClient::RenderProcessWillLaunch( content::RenderProcessHost* host) { // When a render process is crashed, it might be reused. int process_id = host->GetID(); @@ -361,21 +376,21 @@ void AtomBrowserClient::RenderProcessWillLaunch( prefs.disable_popups = web_preferences->IsEnabled("disablePopups"); prefs.web_security = web_preferences->IsEnabled(options::kWebSecurity, true /* default value */); + prefs.browser_context = host->GetBrowserContext(); } - host->AddFilter(new ElectronRenderMessageFilter(host->GetBrowserContext())); - AddProcessPreferences(host->GetID(), prefs); // ensure the ProcessPreferences is removed later host->AddObserver(this); } content::SpeechRecognitionManagerDelegate* -AtomBrowserClient::CreateSpeechRecognitionManagerDelegate() { - return new AtomSpeechRecognitionManagerDelegate; +ElectronBrowserClient::CreateSpeechRecognitionManagerDelegate() { + return new ElectronSpeechRecognitionManagerDelegate; } -content::TtsControllerDelegate* AtomBrowserClient::GetTtsControllerDelegate() { +content::TtsControllerDelegate* +ElectronBrowserClient::GetTtsControllerDelegate() { #if BUILDFLAG(ENABLE_TTS) return TtsControllerDelegateImpl::GetInstance(); #else @@ -383,8 +398,9 @@ content::TtsControllerDelegate* AtomBrowserClient::GetTtsControllerDelegate() { #endif } -void AtomBrowserClient::OverrideWebkitPrefs(content::RenderViewHost* host, - content::WebPreferences* prefs) { +void ElectronBrowserClient::OverrideWebkitPrefs( + content::RenderViewHost* host, + content::WebPreferences* prefs) { prefs->javascript_enabled = true; prefs->web_security_enabled = true; prefs->plugins_enabled = true; @@ -406,11 +422,6 @@ void AtomBrowserClient::OverrideWebkitPrefs(content::RenderViewHost* host, prefs->picture_in_picture_enabled = false; #endif - ui::NativeTheme* native_theme = ui::NativeTheme::GetInstanceForNativeUi(); - prefs->preferred_color_scheme = native_theme->ShouldUseDarkColors() - ? blink::PreferredColorScheme::kDark - : blink::PreferredColorScheme::kLight; - SetFontDefaults(prefs); // Custom preferences of guest page. @@ -420,16 +431,21 @@ void AtomBrowserClient::OverrideWebkitPrefs(content::RenderViewHost* host, web_preferences->OverrideWebkitPrefs(prefs); } -void AtomBrowserClient::SetCanUseCustomSiteInstance(bool should_disable) { +void ElectronBrowserClient::SetCanUseCustomSiteInstance(bool should_disable) { disable_process_restart_tricks_ = should_disable; + disable_process_restart_tricks_is_default_value_ = false; } -bool AtomBrowserClient::CanUseCustomSiteInstance() { +bool ElectronBrowserClient::CanUseCustomSiteInstance() { return disable_process_restart_tricks_; } +bool ElectronBrowserClient::CanUseCustomSiteInstanceIsDefaultValue() { + return disable_process_restart_tricks_is_default_value_; +} + content::ContentBrowserClient::SiteInstanceForNavigationType -AtomBrowserClient::ShouldOverrideSiteInstanceForNavigation( +ElectronBrowserClient::ShouldOverrideSiteInstanceForNavigation( content::RenderFrameHost* current_rfh, content::RenderFrameHost* speculative_rfh, content::BrowserContext* browser_context, @@ -479,7 +495,7 @@ AtomBrowserClient::ShouldOverrideSiteInstanceForNavigation( return SiteInstanceForNavigationType::FORCE_CANDIDATE_OR_NEW; } -void AtomBrowserClient::RegisterPendingSiteInstance( +void ElectronBrowserClient::RegisterPendingSiteInstance( content::RenderFrameHost* rfh, content::SiteInstance* pending_site_instance) { // Do we have an affinity site to manage? @@ -496,7 +512,7 @@ void AtomBrowserClient::RegisterPendingSiteInstance( renderer_is_subframe_.erase(pending_process->GetID()); } -void AtomBrowserClient::AppendExtraCommandLineSwitches( +void ElectronBrowserClient::AppendExtraCommandLineSwitches( base::CommandLine* command_line, int process_id) { // Make sure we're about to launch a known executable @@ -527,72 +543,59 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches( std::string process_type = command_line->GetSwitchValueASCII(::switches::kProcessType); - if (process_type != ::switches::kRendererProcess) - return; - // Copy following switches to child process. - static const char* const kCommonSwitchNames[] = { - switches::kStandardSchemes, switches::kEnableSandbox, - switches::kSecureSchemes, switches::kBypassCSPSchemes, - switches::kCORSSchemes, switches::kFetchSchemes, - switches::kServiceWorkerSchemes, switches::kEnableApiFilteringLogging}; - command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), - kCommonSwitchNames, - base::size(kCommonSwitchNames)); + if (process_type == ::switches::kUtilityProcess || + process_type == ::switches::kRendererProcess) { + // Copy following switches to child process. + static const char* const kCommonSwitchNames[] = { + switches::kStandardSchemes, switches::kEnableSandbox, + switches::kSecureSchemes, switches::kBypassCSPSchemes, + switches::kCORSSchemes, switches::kFetchSchemes, + switches::kServiceWorkerSchemes, switches::kEnableApiFilteringLogging}; + command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), + kCommonSwitchNames, + base::size(kCommonSwitchNames)); + } + if (process_type == ::switches::kRendererProcess) { #if defined(OS_WIN) - // Append --app-user-model-id. - PWSTR current_app_id; - if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(¤t_app_id))) { - command_line->AppendSwitchNative(switches::kAppUserModelId, current_app_id); - CoTaskMemFree(current_app_id); - } + // Append --app-user-model-id. + PWSTR current_app_id; + if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(¤t_app_id))) { + command_line->AppendSwitchNative(switches::kAppUserModelId, + current_app_id); + CoTaskMemFree(current_app_id); + } #endif - if (delegate_) { - auto app_path = static_cast(delegate_)->GetAppPath(); - command_line->AppendSwitchPath(switches::kAppPath, app_path); - } - - content::WebContents* web_contents = GetWebContentsFromProcessID(process_id); - if (web_contents) { - auto* web_preferences = WebContentsPreferences::From(web_contents); - if (web_preferences) - web_preferences->AppendCommandLineSwitches( - command_line, IsRendererSubFrame(process_id)); - auto preloads = - SessionPreferences::GetValidPreloads(web_contents->GetBrowserContext()); - if (!preloads.empty()) - command_line->AppendSwitchNative( - switches::kPreloadScripts, - base::JoinString(preloads, kPathDelimiter)); - if (CanUseCustomSiteInstance()) { - command_line->AppendSwitch( - switches::kDisableElectronSiteInstanceOverrides); + if (delegate_) { + auto app_path = static_cast(delegate_)->GetAppPath(); + command_line->AppendSwitchPath(switches::kAppPath, app_path); } - } -} -void AtomBrowserClient::AdjustUtilityServiceProcessCommandLine( - const service_manager::Identity& identity, - base::CommandLine* command_line) { -#if defined(OS_MACOSX) - if (identity.name() == audio::mojom::kServiceName) - command_line->AppendSwitch(::switches::kMessageLoopTypeUi); -#endif - if (identity.name() == content::mojom::kNetworkServiceName) { - // Copy following switches to network service process. - static const char* const kCommonSwitchNames[] = { - switches::kStandardSchemes, switches::kSecureSchemes, - switches::kBypassCSPSchemes, switches::kCORSSchemes, - switches::kFetchSchemes, switches::kServiceWorkerSchemes}; - command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), - kCommonSwitchNames, - base::size(kCommonSwitchNames)); + content::WebContents* web_contents = + GetWebContentsFromProcessID(process_id); + if (web_contents) { + auto* web_preferences = WebContentsPreferences::From(web_contents); + if (web_preferences) + web_preferences->AppendCommandLineSwitches( + command_line, IsRendererSubFrame(process_id)); + auto preloads = SessionPreferences::GetValidPreloads( + web_contents->GetBrowserContext()); + if (!preloads.empty()) + command_line->AppendSwitchNative( + switches::kPreloadScripts, + base::JoinString(preloads, kPathDelimiter)); + if (CanUseCustomSiteInstance()) { + command_line->AppendSwitch( + switches::kDisableElectronSiteInstanceOverrides); + } + } } } -void AtomBrowserClient::DidCreatePpapiPlugin(content::BrowserPpapiHost* host) { +void ElectronBrowserClient::DidCreatePpapiPlugin( + content::BrowserPpapiHost* host) { #if BUILDFLAG(ENABLE_PEPPER_FLASH) host->GetPpapiHost()->AddHostFactoryFilter( base::WrapUnique(new ChromeBrowserPepperHostFactory(host))); @@ -600,7 +603,7 @@ void AtomBrowserClient::DidCreatePpapiPlugin(content::BrowserPpapiHost* host) { } // attempt to get api key from env -std::string AtomBrowserClient::GetGeolocationApiKey() { +std::string ElectronBrowserClient::GetGeolocationApiKey() { std::unique_ptr env(base::Environment::Create()); std::string api_key; env->GetVar("GOOGLE_API_KEY", &api_key); @@ -608,12 +611,12 @@ std::string AtomBrowserClient::GetGeolocationApiKey() { } scoped_refptr -AtomBrowserClient::CreateQuotaPermissionContext() { - return new AtomQuotaPermissionContext; +ElectronBrowserClient::CreateQuotaPermissionContext() { + return new ElectronQuotaPermissionContext; } content::GeneratedCodeCacheSettings -AtomBrowserClient::GetGeneratedCodeCacheSettings( +ElectronBrowserClient::GetGeneratedCodeCacheSettings( content::BrowserContext* context) { // TODO(deepak1556): Use platform cache directory. base::FilePath cache_path = context->GetPath(); @@ -623,23 +626,22 @@ AtomBrowserClient::GetGeneratedCodeCacheSettings( return content::GeneratedCodeCacheSettings(true, 0, cache_path); } -void AtomBrowserClient::AllowCertificateError( +void ElectronBrowserClient::AllowCertificateError( content::WebContents* web_contents, int cert_error, const net::SSLInfo& ssl_info, const GURL& request_url, bool is_main_frame_request, bool strict_enforcement, - const base::RepeatingCallback& - callback) { + base::OnceCallback callback) { if (delegate_) { delegate_->AllowCertificateError(web_contents, cert_error, ssl_info, request_url, is_main_frame_request, - strict_enforcement, callback); + strict_enforcement, std::move(callback)); } } -base::OnceClosure AtomBrowserClient::SelectClientCertificate( +base::OnceClosure ElectronBrowserClient::SelectClientCertificate( content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, net::ClientCertIdentityList client_certs, @@ -652,7 +654,7 @@ base::OnceClosure AtomBrowserClient::SelectClientCertificate( return base::OnceClosure(); } -bool AtomBrowserClient::CanCreateWindow( +bool ElectronBrowserClient::CanCreateWindow( content::RenderFrameHost* opener, const GURL& opener_url, const GURL& opener_top_level_frame_url, @@ -696,13 +698,13 @@ bool AtomBrowserClient::CanCreateWindow( #if BUILDFLAG(ENABLE_PICTURE_IN_PICTURE) std::unique_ptr -AtomBrowserClient::CreateWindowForPictureInPicture( +ElectronBrowserClient::CreateWindowForPictureInPicture( content::PictureInPictureWindowController* controller) { return content::OverlayWindow::Create(controller); } #endif -void AtomBrowserClient::GetAdditionalAllowedSchemesForFileSystem( +void ElectronBrowserClient::GetAdditionalAllowedSchemesForFileSystem( std::vector* additional_schemes) { auto schemes_list = api::GetStandardSchemes(); if (!schemes_list.empty()) @@ -711,12 +713,20 @@ void AtomBrowserClient::GetAdditionalAllowedSchemesForFileSystem( additional_schemes->push_back(content::kChromeDevToolsScheme); } -void AtomBrowserClient::GetAdditionalWebUISchemes( +void ElectronBrowserClient::GetAdditionalWebUISchemes( std::vector* additional_schemes) { additional_schemes->push_back(content::kChromeDevToolsScheme); } -void AtomBrowserClient::SiteInstanceDeleting( +bool ElectronBrowserClient::ArePersistentMediaDeviceIDsAllowed( + content::BrowserContext* browser_context, + const GURL& scope, + const GURL& site_for_cookies, + const base::Optional& top_frame_origin) { + return true; +} + +void ElectronBrowserClient::SiteInstanceDeleting( content::SiteInstance* site_instance) { // We are storing weak_ptr, is it fundamental to maintain the map up-to-date // when an instance is destroyed. @@ -729,8 +739,9 @@ void AtomBrowserClient::SiteInstanceDeleting( } } -std::unique_ptr AtomBrowserClient::CreateClientCertStore( - content::ResourceContext* resource_context) { +std::unique_ptr +ElectronBrowserClient::CreateClientCertStore( + content::BrowserContext* browser_context) { #if defined(USE_NSS_CERTS) return std::make_unique( net::ClientCertStoreNSS::PasswordDelegateFactory()); @@ -744,7 +755,7 @@ std::unique_ptr AtomBrowserClient::CreateClientCertStore( } std::unique_ptr -AtomBrowserClient::OverrideSystemLocationProvider() { +ElectronBrowserClient::OverrideSystemLocationProvider() { #if BUILDFLAG(OVERRIDE_LOCATION_PROVIDER) return std::make_unique(); #else @@ -753,7 +764,7 @@ AtomBrowserClient::OverrideSystemLocationProvider() { } mojo::Remote -AtomBrowserClient::CreateNetworkContext( +ElectronBrowserClient::CreateNetworkContext( content::BrowserContext* browser_context, bool /*in_memory*/, const base::FilePath& /*relative_partition_path*/) { @@ -762,26 +773,27 @@ AtomBrowserClient::CreateNetworkContext( ->CreateNetworkContext(); } -network::mojom::NetworkContext* AtomBrowserClient::GetSystemNetworkContext() { +network::mojom::NetworkContext* +ElectronBrowserClient::GetSystemNetworkContext() { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(g_browser_process->system_network_context_manager()); return g_browser_process->system_network_context_manager()->GetContext(); } base::Optional -AtomBrowserClient::GetServiceManifestOverlay(base::StringPiece name) { +ElectronBrowserClient::GetServiceManifestOverlay(base::StringPiece name) { if (name == content::mojom::kBrowserServiceName) return GetElectronContentBrowserOverlayManifest(); return base::nullopt; } std::unique_ptr -AtomBrowserClient::CreateBrowserMainParts( +ElectronBrowserClient::CreateBrowserMainParts( const content::MainFunctionParams& params) { - return std::make_unique(params); + return std::make_unique(params); } -void AtomBrowserClient::WebNotificationAllowed( +void ElectronBrowserClient::WebNotificationAllowed( int render_process_id, base::OnceCallback callback) { content::WebContents* web_contents = @@ -800,7 +812,7 @@ void AtomBrowserClient::WebNotificationAllowed( base::BindOnce(std::move(callback), web_contents->IsAudioMuted())); } -void AtomBrowserClient::RenderProcessHostDestroyed( +void ElectronBrowserClient::RenderProcessHostDestroyed( content::RenderProcessHost* host) { int process_id = host->GetID(); pending_processes_.erase(process_id); @@ -808,7 +820,8 @@ void AtomBrowserClient::RenderProcessHostDestroyed( RemoveProcessPreferences(process_id); } -void AtomBrowserClient::RenderProcessReady(content::RenderProcessHost* host) { +void ElectronBrowserClient::RenderProcessReady( + content::RenderProcessHost* host) { render_process_host_pids_[host->GetID()] = base::GetProcId(host->GetProcess().Handle()); if (delegate_) { @@ -816,7 +829,7 @@ void AtomBrowserClient::RenderProcessReady(content::RenderProcessHost* host) { } } -void AtomBrowserClient::RenderProcessExited( +void ElectronBrowserClient::RenderProcessExited( content::RenderProcessHost* host, const content::ChildProcessTerminationInfo& info) { auto host_pid = render_process_host_pids_.find(host->GetID()); @@ -838,9 +851,9 @@ void OnOpenExternal(const GURL& escaped_url, bool allowed) { void HandleExternalProtocolInUI( const GURL& url, - const content::WebContents::Getter& web_contents_getter, + content::WebContents::OnceGetter web_contents_getter, bool has_user_gesture) { - content::WebContents* web_contents = web_contents_getter.Run(); + content::WebContents* web_contents = std::move(web_contents_getter).Run(); if (!web_contents) return; @@ -855,39 +868,41 @@ void HandleExternalProtocolInUI( has_user_gesture, url); } -bool AtomBrowserClient::HandleExternalProtocol( +bool ElectronBrowserClient::HandleExternalProtocol( const GURL& url, - content::WebContents::Getter web_contents_getter, + content::WebContents::OnceGetter web_contents_getter, int child_id, content::NavigationUIData* navigation_data, bool is_main_frame, ui::PageTransition page_transition, bool has_user_gesture, - network::mojom::URLLoaderFactoryPtr* out_factory) { - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&HandleExternalProtocolInUI, url, - web_contents_getter, has_user_gesture)); + const base::Optional& initiating_origin, + mojo::PendingRemote* out_factory) { + base::PostTask( + FROM_HERE, {BrowserThread::UI}, + base::BindOnce(&HandleExternalProtocolInUI, url, + std::move(web_contents_getter), has_user_gesture)); return true; } std::vector> -AtomBrowserClient::CreateThrottlesForNavigation( +ElectronBrowserClient::CreateThrottlesForNavigation( content::NavigationHandle* handle) { std::vector> throttles; - throttles.push_back(std::make_unique(handle)); + throttles.push_back(std::make_unique(handle)); return throttles; } -content::MediaObserver* AtomBrowserClient::GetMediaObserver() { +content::MediaObserver* ElectronBrowserClient::GetMediaObserver() { return MediaCaptureDevicesDispatcher::GetInstance(); } content::DevToolsManagerDelegate* -AtomBrowserClient::GetDevToolsManagerDelegate() { +ElectronBrowserClient::GetDevToolsManagerDelegate() { return new DevToolsManagerDelegate; } -NotificationPresenter* AtomBrowserClient::GetNotificationPresenter() { +NotificationPresenter* ElectronBrowserClient::GetNotificationPresenter() { if (!notification_presenter_) { notification_presenter_.reset(NotificationPresenter::Create()); } @@ -895,7 +910,7 @@ NotificationPresenter* AtomBrowserClient::GetNotificationPresenter() { } content::PlatformNotificationService* -AtomBrowserClient::GetPlatformNotificationService( +ElectronBrowserClient::GetPlatformNotificationService( content::BrowserContext* browser_context) { if (!notification_service_) { notification_service_ = std::make_unique(this); @@ -903,7 +918,7 @@ AtomBrowserClient::GetPlatformNotificationService( return notification_service_.get(); } -base::FilePath AtomBrowserClient::GetDefaultDownloadDirectory() { +base::FilePath ElectronBrowserClient::GetDefaultDownloadDirectory() { // ~/Downloads base::FilePath path; if (base::PathService::Get(base::DIR_HOME, &path)) @@ -913,13 +928,13 @@ base::FilePath AtomBrowserClient::GetDefaultDownloadDirectory() { } scoped_refptr -AtomBrowserClient::GetSystemSharedURLLoaderFactory() { +ElectronBrowserClient::GetSystemSharedURLLoaderFactory() { if (!g_browser_process) return nullptr; return g_browser_process->shared_url_loader_factory(); } -void AtomBrowserClient::OnNetworkServiceCreated( +void ElectronBrowserClient::OnNetworkServiceCreated( network::mojom::NetworkService* network_service) { if (!g_browser_process) return; @@ -929,7 +944,7 @@ void AtomBrowserClient::OnNetworkServiceCreated( } std::vector -AtomBrowserClient::GetNetworkContextsParentDirectory() { +ElectronBrowserClient::GetNetworkContextsParentDirectory() { base::FilePath user_data_dir; base::PathService::Get(DIR_USER_DATA, &user_data_dir); DCHECK(!user_data_dir.empty()); @@ -937,21 +952,21 @@ AtomBrowserClient::GetNetworkContextsParentDirectory() { return {user_data_dir}; } -std::string AtomBrowserClient::GetProduct() { +std::string ElectronBrowserClient::GetProduct() { return "Chrome/" CHROME_VERSION_STRING; } -std::string AtomBrowserClient::GetUserAgent() { +std::string ElectronBrowserClient::GetUserAgent() { if (user_agent_override_.empty()) return GetApplicationUserAgent(); return user_agent_override_; } -void AtomBrowserClient::SetUserAgent(const std::string& user_agent) { +void ElectronBrowserClient::SetUserAgent(const std::string& user_agent) { user_agent_override_ = user_agent; } -void AtomBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories( +void ElectronBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories( int frame_tree_node_id, NonNetworkURLLoaderFactoryMap* factories) { content::WebContents* web_contents = @@ -962,7 +977,7 @@ void AtomBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories( protocol->RegisterURLLoaderFactories(factories); } -void AtomBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories( +void ElectronBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories( int render_process_id, int render_frame_id, NonNetworkURLLoaderFactoryMap* factories) { @@ -979,16 +994,54 @@ void AtomBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories( } } -bool AtomBrowserClient::WillCreateURLLoaderFactory( +bool ElectronBrowserClient::WillInterceptWebSocket( + content::RenderFrameHost* frame) { + if (!frame) + return false; + + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + auto* browser_context = frame->GetProcess()->GetBrowserContext(); + auto web_request = api::WebRequestNS::FromOrCreate(isolate, browser_context); + + // NOTE: Some unit test environments do not initialize + // BrowserContextKeyedAPI factories for e.g. WebRequest. + if (!web_request.get()) + return false; + + return web_request->HasListener(); +} + +void ElectronBrowserClient::CreateWebSocket( + content::RenderFrameHost* frame, + WebSocketFactory factory, + const GURL& url, + const GURL& site_for_cookies, + const base::Optional& user_agent, + mojo::PendingRemote + handshake_client) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + auto* browser_context = frame->GetProcess()->GetBrowserContext(); + auto web_request = api::WebRequestNS::FromOrCreate(isolate, browser_context); + DCHECK(web_request.get()); + ProxyingWebSocket::StartProxying( + web_request.get(), std::move(factory), url, site_for_cookies, user_agent, + std::move(handshake_client), true, frame->GetProcess()->GetID(), + frame->GetRoutingID(), frame->GetLastCommittedOrigin(), browser_context, + &next_id_); +} + +bool ElectronBrowserClient::WillCreateURLLoaderFactory( content::BrowserContext* browser_context, content::RenderFrameHost* frame_host, int render_process_id, URLLoaderFactoryType type, const url::Origin& request_initiator, + base::Optional navigation_id, mojo::PendingReceiver* factory_receiver, mojo::PendingRemote* header_client, - bool* bypass_redirect_checks) { + bool* bypass_redirect_checks, + network::mojom::URLLoaderFactoryOverridePtr* factory_override) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); api::ProtocolNS* protocol = api::ProtocolNS::FromWrappedClass(isolate, browser_context); @@ -997,8 +1050,19 @@ bool AtomBrowserClient::WillCreateURLLoaderFactory( DCHECK(web_request.get()); auto proxied_receiver = std::move(*factory_receiver); - network::mojom::URLLoaderFactoryPtrInfo target_factory_info; - *factory_receiver = mojo::MakeRequest(&target_factory_info); + mojo::PendingRemote target_factory_remote; + *factory_receiver = target_factory_remote.InitWithNewPipeAndPassReceiver(); + + // Required by WebRequestInfoInitParams. + // + // Note that in Electron we allow webRequest to capture requests sent from + // browser process, so creation of |navigation_ui_data| is different from + // Chromium which only does for renderer-initialized navigations. + std::unique_ptr navigation_ui_data; + if (navigation_id.has_value()) { + navigation_ui_data = + std::make_unique(); + } mojo::PendingReceiver header_client_receiver; @@ -1006,47 +1070,32 @@ bool AtomBrowserClient::WillCreateURLLoaderFactory( header_client_receiver = header_client->InitWithNewPipeAndPassReceiver(); new ProxyingURLLoaderFactory( - web_request.get(), protocol->intercept_handlers(), render_process_id, - std::move(proxied_receiver), std::move(target_factory_info), - std::move(header_client_receiver), type); + web_request.get(), protocol->intercept_handlers(), browser_context, + render_process_id, &next_id_, std::move(navigation_ui_data), + std::move(navigation_id), std::move(proxied_receiver), + std::move(target_factory_remote), std::move(header_client_receiver), + type); if (bypass_redirect_checks) *bypass_redirect_checks = true; return true; } -network::mojom::URLLoaderFactoryPtrInfo -AtomBrowserClient::CreateURLLoaderFactoryForNetworkRequests( +void ElectronBrowserClient::OverrideURLLoaderFactoryParams( content::RenderProcessHost* process, - network::mojom::NetworkContext* network_context, - mojo::PendingRemote* - header_client, - const url::Origin& request_initiator, - const base::Optional& network_isolation_key) { - auto render_process_id = process->GetID(); - auto it = process_preferences_.find(render_process_id); - if (it != process_preferences_.end() && !it->second.web_security) { + const url::Origin& origin, + network::mojom::URLLoaderFactoryParams* factory_params) { + const auto& iter = process_preferences_.find(process->GetID()); + if (iter != process_preferences_.end() && !iter->second.web_security) { // bypass CORB - network::mojom::URLLoaderFactoryParamsPtr params = - network::mojom::URLLoaderFactoryParams::New(); - - if (header_client) - params->header_client = std::move(*header_client); - params->process_id = render_process_id; - params->is_corb_enabled = false; - - // Create the URLLoaderFactory. - network::mojom::URLLoaderFactoryPtrInfo factory_info; - network_context->CreateURLLoaderFactory(mojo::MakeRequest(&factory_info), - std::move(params)); - return factory_info; + factory_params->process_id = iter->first; + factory_params->is_corb_enabled = false; } - return network::mojom::URLLoaderFactoryPtrInfo(); } #if defined(OS_WIN) -bool AtomBrowserClient::PreSpawnRenderer(sandbox::TargetPolicy* policy, - RendererSpawnFlags flags) { +bool ElectronBrowserClient::PreSpawnRenderer(sandbox::TargetPolicy* policy, + RendererSpawnFlags flags) { // Allow crashpad to communicate via named pipe. sandbox::ResultCode result = policy->AddRule( sandbox::TargetPolicy::SUBSYS_FILES, @@ -1057,7 +1106,7 @@ bool AtomBrowserClient::PreSpawnRenderer(sandbox::TargetPolicy* policy, } #endif // defined(OS_WIN) -bool AtomBrowserClient::BindAssociatedReceiverFromFrame( +bool ElectronBrowserClient::BindAssociatedReceiverFromFrame( content::RenderFrameHost* render_frame_host, const std::string& interface_name, mojo::ScopedInterfaceEndpointHandle* handle) { @@ -1071,22 +1120,56 @@ bool AtomBrowserClient::BindAssociatedReceiverFromFrame( return false; } -std::string AtomBrowserClient::GetApplicationLocale() { +std::string ElectronBrowserClient::GetApplicationLocale() { if (BrowserThread::CurrentlyOn(BrowserThread::IO)) return g_io_thread_application_locale.Get(); return *g_application_locale; } -base::FilePath AtomBrowserClient::GetFontLookupTableCacheDir() { +base::FilePath ElectronBrowserClient::GetFontLookupTableCacheDir() { base::FilePath user_data_dir; base::PathService::Get(DIR_USER_DATA, &user_data_dir); DCHECK(!user_data_dir.empty()); return user_data_dir.Append(FILE_PATH_LITERAL("FontLookupTableCache")); } -bool AtomBrowserClient::ShouldEnableStrictSiteIsolation() { +bool ElectronBrowserClient::ShouldEnableStrictSiteIsolation() { // Enable site isolation. It is off by default in Chromium <= 69. return true; } +void ElectronBrowserClient::BindHostReceiverForRenderer( + content::RenderProcessHost* render_process_host, + mojo::GenericPendingReceiver receiver) { +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + if (auto host_receiver = receiver.As()) { + SpellCheckHostChromeImpl::Create(render_process_host->GetID(), + std::move(host_receiver)); + return; + } +#endif +} + +void ElectronBrowserClient::RegisterBrowserInterfaceBindersForFrame( + content::RenderFrameHost* render_frame_host, + service_manager::BinderMapWithContext* map) { + map->Add( + base::BindRepeating(&BindNetworkHintsHandler)); +} + +std::unique_ptr +ElectronBrowserClient::CreateLoginDelegate( + const net::AuthChallengeInfo& auth_info, + content::WebContents* web_contents, + const content::GlobalRequestID& request_id, + bool is_main_frame, + const GURL& url, + scoped_refptr response_headers, + bool first_auth_attempt, + LoginAuthRequiredCallback auth_required_callback) { + return std::make_unique( + auth_info, web_contents, is_main_frame, url, response_headers, + first_auth_attempt, std::move(auth_required_callback)); +} + } // namespace electron diff --git a/shell/browser/atom_browser_client.h b/shell/browser/electron_browser_client.h similarity index 78% rename from shell/browser/atom_browser_client.h rename to shell/browser/electron_browser_client.h index 6ca10a250c078..49a01506665e2 100644 --- a/shell/browser/atom_browser_client.h +++ b/shell/browser/electron_browser_client.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_BROWSER_CLIENT_H_ -#define SHELL_BROWSER_ATOM_BROWSER_CLIENT_H_ +#ifndef SHELL_BROWSER_ELECTRON_BROWSER_CLIENT_H_ +#define SHELL_BROWSER_ELECTRON_BROWSER_CLIENT_H_ #include #include @@ -15,12 +15,13 @@ #include "base/synchronization/lock.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/render_process_host_observer.h" +#include "content/public/browser/web_contents.h" #include "electron/buildflags/buildflags.h" #include "net/ssl/client_cert_identity.h" namespace content { -class QuotaPermissionContext; class ClientCertificateDelegate; +class QuotaPermissionContext; } // namespace content namespace net { @@ -32,14 +33,14 @@ namespace electron { class NotificationPresenter; class PlatformNotificationService; -class AtomBrowserClient : public content::ContentBrowserClient, - public content::RenderProcessHostObserver { +class ElectronBrowserClient : public content::ContentBrowserClient, + public content::RenderProcessHostObserver { public: - static AtomBrowserClient* Get(); + static ElectronBrowserClient* Get(); static void SetApplicationLocale(const std::string& locale); - AtomBrowserClient(); - ~AtomBrowserClient() override; + ElectronBrowserClient(); + ~ElectronBrowserClient() override; using Delegate = content::ContentBrowserClient; void set_delegate(Delegate* delegate) { delegate_ = delegate; } @@ -62,15 +63,21 @@ class AtomBrowserClient : public content::ContentBrowserClient, // content::ContentBrowserClient: std::string GetApplicationLocale() override; base::FilePath GetFontLookupTableCacheDir() override; - - // content::ContentBrowserClient: bool ShouldEnableStrictSiteIsolation() override; + void BindHostReceiverForRenderer( + content::RenderProcessHost* render_process_host, + mojo::GenericPendingReceiver receiver) override; + void RegisterBrowserInterfaceBindersForFrame( + content::RenderFrameHost* render_frame_host, + service_manager::BinderMapWithContext* map) + override; std::string GetUserAgent() override; void SetUserAgent(const std::string& user_agent); void SetCanUseCustomSiteInstance(bool should_disable); bool CanUseCustomSiteInstance() override; + bool CanUseCustomSiteInstanceIsDefaultValue(); protected: void RenderProcessWillLaunch(content::RenderProcessHost* host) override; @@ -92,9 +99,6 @@ class AtomBrowserClient : public content::ContentBrowserClient, content::SiteInstance* pending_site_instance) override; void AppendExtraCommandLineSwitches(base::CommandLine* command_line, int child_process_id) override; - void AdjustUtilityServiceProcessCommandLine( - const service_manager::Identity& identity, - base::CommandLine* command_line) override; void DidCreatePpapiPlugin(content::BrowserPpapiHost* browser_host) override; std::string GetGeolocationApiKey() override; scoped_refptr CreateQuotaPermissionContext() @@ -108,8 +112,8 @@ class AtomBrowserClient : public content::ContentBrowserClient, const GURL& request_url, bool is_main_frame_request, bool strict_enforcement, - const base::Callback& - callback) override; + base::OnceCallback callback) + override; base::OnceClosure SelectClientCertificate( content::WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, @@ -140,7 +144,7 @@ class AtomBrowserClient : public content::ContentBrowserClient, std::vector* additional_schemes) override; void SiteInstanceDeleting(content::SiteInstance* site_instance) override; std::unique_ptr CreateClientCertStore( - content::ResourceContext* resource_context) override; + content::BrowserContext* browser_context) override; std::unique_ptr OverrideSystemLocationProvider() override; mojo::Remote CreateNetworkContext( @@ -170,25 +174,31 @@ class AtomBrowserClient : public content::ContentBrowserClient, int render_process_id, int render_frame_id, NonNetworkURLLoaderFactoryMap* factories) override; + void CreateWebSocket( + content::RenderFrameHost* frame, + WebSocketFactory factory, + const GURL& url, + const GURL& site_for_cookies, + const base::Optional& user_agent, + mojo::PendingRemote + handshake_client) override; + bool WillInterceptWebSocket(content::RenderFrameHost*) override; bool WillCreateURLLoaderFactory( content::BrowserContext* browser_context, content::RenderFrameHost* frame, int render_process_id, URLLoaderFactoryType type, const url::Origin& request_initiator, + base::Optional navigation_id, mojo::PendingReceiver* factory_receiver, mojo::PendingRemote* header_client, - bool* bypass_redirect_checks) override; - network::mojom::URLLoaderFactoryPtrInfo - CreateURLLoaderFactoryForNetworkRequests( + bool* bypass_redirect_checks, + network::mojom::URLLoaderFactoryOverridePtr* factory_override) override; + void OverrideURLLoaderFactoryParams( content::RenderProcessHost* process, - network::mojom::NetworkContext* network_context, - mojo::PendingRemote* - header_client, - const url::Origin& request_initiator, - const base::Optional& network_isolation_key) - override; + const url::Origin& origin, + network::mojom::URLLoaderFactoryParams* factory_params) override; #if defined(OS_WIN) bool PreSpawnRenderer(sandbox::TargetPolicy* policy, RendererSpawnFlags flags) override; @@ -200,13 +210,29 @@ class AtomBrowserClient : public content::ContentBrowserClient, bool HandleExternalProtocol( const GURL& url, - content::WebContents::Getter web_contents_getter, + content::WebContents::OnceGetter web_contents_getter, int child_id, content::NavigationUIData* navigation_data, bool is_main_frame, ui::PageTransition page_transition, bool has_user_gesture, - network::mojom::URLLoaderFactoryPtr* out_factory) override; + const base::Optional& initiating_origin, + mojo::PendingRemote* out_factory) + override; + std::unique_ptr CreateLoginDelegate( + const net::AuthChallengeInfo& auth_info, + content::WebContents* web_contents, + const content::GlobalRequestID& request_id, + bool is_main_frame, + const GURL& url, + scoped_refptr response_headers, + bool first_auth_attempt, + LoginAuthRequiredCallback auth_required_callback) override; + bool ArePersistentMediaDeviceIDsAllowed( + content::BrowserContext* browser_context, + const GURL& scope, + const GURL& site_for_cookies, + const base::Optional& top_frame_origin) override; // content::RenderProcessHostObserver: void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; @@ -221,6 +247,7 @@ class AtomBrowserClient : public content::ContentBrowserClient, bool native_window_open = false; bool disable_popups = false; bool web_security = true; + content::BrowserContext* browser_context = nullptr; }; bool ShouldForceNewSiteInstance(content::RenderFrameHost* current_rfh, @@ -270,10 +297,15 @@ class AtomBrowserClient : public content::ContentBrowserClient, std::string user_agent_override_ = ""; bool disable_process_restart_tricks_ = false; + bool disable_process_restart_tricks_is_default_value_ = true; + + // Simple shared ID generator, used by ProxyingURLLoaderFactory and + // ProxyingWebSocket classes. + uint64_t next_id_ = 0; - DISALLOW_COPY_AND_ASSIGN(AtomBrowserClient); + DISALLOW_COPY_AND_ASSIGN(ElectronBrowserClient); }; } // namespace electron -#endif // SHELL_BROWSER_ATOM_BROWSER_CLIENT_H_ +#endif // SHELL_BROWSER_ELECTRON_BROWSER_CLIENT_H_ diff --git a/shell/browser/atom_browser_context.cc b/shell/browser/electron_browser_context.cc similarity index 59% rename from shell/browser/atom_browser_context.cc rename to shell/browser/electron_browser_context.cc index fd9995680c291..74a767f2f094b 100644 --- a/shell/browser/atom_browser_context.cc +++ b/shell/browser/electron_browser_context.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/electron_browser_context.h" #include @@ -28,15 +28,18 @@ #include "content/browser/blob_storage/chrome_blob_storage_context.h" // nogncheck #include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "net/base/escape.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" -#include "shell/browser/atom_browser_client.h" -#include "shell/browser/atom_browser_main_parts.h" -#include "shell/browser/atom_download_manager_delegate.h" -#include "shell/browser/atom_paths.h" -#include "shell/browser/atom_permission_manager.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "shell/browser/api/electron_api_url_loader.h" #include "shell/browser/cookie_change_notifier.h" +#include "shell/browser/electron_browser_client.h" +#include "shell/browser/electron_browser_main_parts.h" +#include "shell/browser/electron_download_manager_delegate.h" +#include "shell/browser/electron_paths.h" +#include "shell/browser/electron_permission_manager.h" #include "shell/browser/net/resolve_proxy_helper.h" #include "shell/browser/pref_store_delegate.h" #include "shell/browser/special_storage_policy.h" @@ -47,21 +50,32 @@ #include "shell/common/options_switches.h" #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) -#include "components/pref_registry/pref_registry_syncable.h" -#include "components/user_prefs/user_prefs.h" #include "extensions/browser/browser_context_keyed_service_factories.h" #include "extensions/browser/extension_pref_store.h" #include "extensions/browser/extension_pref_value_map_factory.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/pref_names.h" #include "extensions/common/extension_api.h" -#include "shell/browser/extensions/atom_browser_context_keyed_service_factories.h" -#include "shell/browser/extensions/atom_extension_system.h" -#include "shell/browser/extensions/atom_extension_system_factory.h" -#include "shell/browser/extensions/atom_extensions_browser_client.h" -#include "shell/common/extensions/atom_extensions_client.h" +#include "shell/browser/extensions/electron_browser_context_keyed_service_factories.h" +#include "shell/browser/extensions/electron_extension_system.h" +#include "shell/browser/extensions/electron_extension_system_factory.h" +#include "shell/browser/extensions/electron_extensions_browser_client.h" +#include "shell/common/extensions/electron_extensions_client.h" #endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) || \ + BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "components/pref_registry/pref_registry_syncable.h" +#include "components/user_prefs/user_prefs.h" +#endif + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "base/i18n/rtl.h" +#include "components/language/core/browser/language_prefs.h" +#include "components/spellcheck/browser/pref_names.h" +#include "components/spellcheck/common/spellcheck_common.h" +#endif + using content::BrowserThread; namespace electron { @@ -76,18 +90,20 @@ std::string MakePartitionName(const std::string& input) { } // namespace // static -AtomBrowserContext::BrowserContextMap AtomBrowserContext::browser_context_map_; +ElectronBrowserContext::BrowserContextMap + ElectronBrowserContext::browser_context_map_; -AtomBrowserContext::AtomBrowserContext(const std::string& partition, - bool in_memory, - const base::DictionaryValue& options) - : base::RefCountedDeleteOnSequence( +ElectronBrowserContext::ElectronBrowserContext( + const std::string& partition, + bool in_memory, + const base::DictionaryValue& options) + : base::RefCountedDeleteOnSequence( base::ThreadTaskRunnerHandle::Get()), in_memory_pref_store_(nullptr), storage_policy_(new SpecialStoragePolicy), in_memory_(in_memory), weak_factory_(this) { - user_agent_ = AtomBrowserClient::Get()->GetUserAgent(); + user_agent_ = ElectronBrowserClient::Get()->GetUserAgent(); // Read options. base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); @@ -101,6 +117,10 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition, base::PathService::Get(DIR_APP_DATA, &path_); path_ = path_.Append(base::FilePath::FromUTF8Unsafe(GetApplicationName())); base::PathService::Override(DIR_USER_DATA, path_); + base::PathService::Override(chrome::DIR_USER_DATA, path_); + base::PathService::Override( + chrome::DIR_APP_DICTIONARIES, + path_.Append(base::FilePath::FromUTF8Unsafe("Dictionaries"))); } if (!in_memory && !partition.empty()) @@ -121,14 +141,14 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition, BrowserContextDependencyManager::GetInstance()->CreateBrowserContextServices( this); - extension_system_ = static_cast( + extension_system_ = static_cast( extensions::ExtensionSystem::Get(this)); extension_system_->InitForRegularProfile(true /* extensions_enabled */); extension_system_->FinishInitialization(); #endif } -AtomBrowserContext::~AtomBrowserContext() { +ElectronBrowserContext::~ElectronBrowserContext() { DCHECK_CURRENTLY_ON(BrowserThread::UI); NotifyWillBeDestroyed(this); ShutdownStoragePartitions(); @@ -141,7 +161,7 @@ AtomBrowserContext::~AtomBrowserContext() { this); } -void AtomBrowserContext::InitPrefs() { +void ElectronBrowserContext::InitPrefs() { auto prefs_path = GetPath().Append(FILE_PATH_LITERAL("Preferences")); base::ThreadRestrictions::ScopedAllowIO allow_io; PrefServiceFactory prefs_factory; @@ -155,7 +175,10 @@ void AtomBrowserContext::InitPrefs() { ExtensionPrefValueMapFactory::GetForBrowserContext(this), IsOffTheRecord()); prefs_factory.set_extension_prefs(ext_pref_store); +#endif +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) || \ + BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) auto registry = WrapRefCounted(new user_prefs::PrefRegistrySyncable); #else auto registry = WrapRefCounted(new PrefRegistrySimple); @@ -176,49 +199,72 @@ void AtomBrowserContext::InitPrefs() { extensions::ExtensionPrefs::RegisterProfilePrefs(registry.get()); #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + BrowserContextDependencyManager::GetInstance() + ->RegisterProfilePrefsForServices(registry.get()); + + language::LanguagePrefs::RegisterProfilePrefs(registry.get()); +#endif + prefs_ = prefs_factory.Create( registry.get(), std::make_unique(weak_factory_.GetWeakPtr())); prefs_->UpdateCommandLinePrefStore(new ValueMapPrefStore); -#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) || \ + BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) user_prefs::UserPrefs::Set(this, prefs_.get()); #endif + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + auto* current_dictionaries = + prefs()->Get(spellcheck::prefs::kSpellCheckDictionaries); + // No configured dictionaries, the default will be en-US + if (current_dictionaries->GetList().size() == 0) { + std::string default_code = spellcheck::GetCorrespondingSpellCheckLanguage( + base::i18n::GetConfiguredLocale()); + if (!default_code.empty()) { + base::ListValue language_codes; + language_codes.AppendString(default_code); + prefs()->Set(spellcheck::prefs::kSpellCheckDictionaries, language_codes); + } + } +#endif } -void AtomBrowserContext::SetUserAgent(const std::string& user_agent) { +void ElectronBrowserContext::SetUserAgent(const std::string& user_agent) { user_agent_ = user_agent; } -base::FilePath AtomBrowserContext::GetPath() { +base::FilePath ElectronBrowserContext::GetPath() { return path_; } -bool AtomBrowserContext::IsOffTheRecord() { +bool ElectronBrowserContext::IsOffTheRecord() { return in_memory_; } -bool AtomBrowserContext::CanUseHttpCache() const { +bool ElectronBrowserContext::CanUseHttpCache() const { return use_cache_; } -int AtomBrowserContext::GetMaxCacheSize() const { +int ElectronBrowserContext::GetMaxCacheSize() const { return max_cache_size_; } -content::ResourceContext* AtomBrowserContext::GetResourceContext() { +content::ResourceContext* ElectronBrowserContext::GetResourceContext() { if (!resource_context_) resource_context_ = std::make_unique(); return resource_context_.get(); } -std::string AtomBrowserContext::GetMediaDeviceIDSalt() { +std::string ElectronBrowserContext::GetMediaDeviceIDSalt() { if (!media_device_id_salt_.get()) media_device_id_salt_ = std::make_unique(prefs_.get()); return media_device_id_salt_->GetSalt(); } std::unique_ptr -AtomBrowserContext::CreateZoomLevelDelegate( +ElectronBrowserContext::CreateZoomLevelDelegate( const base::FilePath& partition_path) { if (!IsOffTheRecord()) { return std::make_unique(prefs(), partition_path); @@ -227,37 +273,38 @@ AtomBrowserContext::CreateZoomLevelDelegate( } content::DownloadManagerDelegate* -AtomBrowserContext::GetDownloadManagerDelegate() { +ElectronBrowserContext::GetDownloadManagerDelegate() { if (!download_manager_delegate_.get()) { auto* download_manager = content::BrowserContext::GetDownloadManager(this); download_manager_delegate_ = - std::make_unique(download_manager); + std::make_unique(download_manager); } return download_manager_delegate_.get(); } -content::BrowserPluginGuestManager* AtomBrowserContext::GetGuestManager() { +content::BrowserPluginGuestManager* ElectronBrowserContext::GetGuestManager() { if (!guest_manager_) guest_manager_ = std::make_unique(); return guest_manager_.get(); } content::PermissionControllerDelegate* -AtomBrowserContext::GetPermissionControllerDelegate() { +ElectronBrowserContext::GetPermissionControllerDelegate() { if (!permission_manager_.get()) - permission_manager_ = std::make_unique(); + permission_manager_ = std::make_unique(); return permission_manager_.get(); } -storage::SpecialStoragePolicy* AtomBrowserContext::GetSpecialStoragePolicy() { +storage::SpecialStoragePolicy* +ElectronBrowserContext::GetSpecialStoragePolicy() { return storage_policy_.get(); } -std::string AtomBrowserContext::GetUserAgent() const { +std::string ElectronBrowserContext::GetUserAgent() const { return user_agent_; } -predictors::PreconnectManager* AtomBrowserContext::GetPreconnectManager() { +predictors::PreconnectManager* ElectronBrowserContext::GetPreconnectManager() { if (!preconnect_manager_.get()) { preconnect_manager_ = std::make_unique(nullptr, this); @@ -266,26 +313,28 @@ predictors::PreconnectManager* AtomBrowserContext::GetPreconnectManager() { } scoped_refptr -AtomBrowserContext::GetURLLoaderFactory() { +ElectronBrowserContext::GetURLLoaderFactory() { if (url_loader_factory_) return url_loader_factory_; - network::mojom::URLLoaderFactoryPtr network_factory; - mojo::PendingReceiver factory_request = - mojo::MakeRequest(&network_factory); + mojo::PendingRemote network_factory_remote; + mojo::PendingReceiver factory_receiver = + network_factory_remote.InitWithNewPipeAndPassReceiver(); // Consult the embedder. mojo::PendingRemote header_client; - static_cast(AtomBrowserClient::Get()) + static_cast(ElectronBrowserClient::Get()) ->WillCreateURLLoaderFactory( this, nullptr, -1, content::ContentBrowserClient::URLLoaderFactoryType::kNavigation, - url::Origin(), &factory_request, &header_client, nullptr); + url::Origin(), base::nullopt, &factory_receiver, &header_client, + nullptr, nullptr); network::mojom::URLLoaderFactoryParamsPtr params = network::mojom::URLLoaderFactoryParams::New(); params->header_client = std::move(header_client); + params->auth_client = auth_client_.BindNewPipeAndPassRemote(); params->process_id = network::mojom::kBrowserProcessId; params->is_trusted = true; params->is_corb_enabled = false; @@ -296,42 +345,83 @@ AtomBrowserContext::GetURLLoaderFactory() { auto* storage_partition = content::BrowserContext::GetDefaultStoragePartition(this); storage_partition->GetNetworkContext()->CreateURLLoaderFactory( - std::move(factory_request), std::move(params)); + std::move(factory_receiver), std::move(params)); url_loader_factory_ = base::MakeRefCounted( - std::move(network_factory)); + std::move(network_factory_remote)); return url_loader_factory_; } -content::PushMessagingService* AtomBrowserContext::GetPushMessagingService() { +class AuthResponder : public network::mojom::TrustedAuthClient { + public: + AuthResponder() {} + ~AuthResponder() override = default; + + private: + void OnAuthRequired( + const base::Optional<::base::UnguessableToken>& window_id, + uint32_t process_id, + uint32_t routing_id, + uint32_t request_id, + const ::GURL& url, + bool first_auth_attempt, + const ::net::AuthChallengeInfo& auth_info, + ::network::mojom::URLResponseHeadPtr head, + mojo::PendingRemote + auth_challenge_responder) override { + api::SimpleURLLoaderWrapper* url_loader = + api::SimpleURLLoaderWrapper::FromID(routing_id); + if (url_loader) { + url_loader->OnAuthRequired(url, first_auth_attempt, auth_info, + std::move(head), + std::move(auth_challenge_responder)); + } + } +}; + +void ElectronBrowserContext::OnLoaderCreated( + int32_t request_id, + mojo::PendingReceiver auth_client) { + mojo::MakeSelfOwnedReceiver(std::make_unique(), + std::move(auth_client)); +} + +content::PushMessagingService* +ElectronBrowserContext::GetPushMessagingService() { return nullptr; } -content::SSLHostStateDelegate* AtomBrowserContext::GetSSLHostStateDelegate() { +content::SSLHostStateDelegate* +ElectronBrowserContext::GetSSLHostStateDelegate() { return nullptr; } content::BackgroundFetchDelegate* -AtomBrowserContext::GetBackgroundFetchDelegate() { +ElectronBrowserContext::GetBackgroundFetchDelegate() { return nullptr; } content::BackgroundSyncController* -AtomBrowserContext::GetBackgroundSyncController() { +ElectronBrowserContext::GetBackgroundSyncController() { return nullptr; } content::BrowsingDataRemoverDelegate* -AtomBrowserContext::GetBrowsingDataRemoverDelegate() { +ElectronBrowserContext::GetBrowsingDataRemoverDelegate() { return nullptr; } content::ClientHintsControllerDelegate* -AtomBrowserContext::GetClientHintsControllerDelegate() { +ElectronBrowserContext::GetClientHintsControllerDelegate() { + return nullptr; +} + +content::StorageNotificationService* +ElectronBrowserContext::GetStorageNotificationService() { return nullptr; } -void AtomBrowserContext::SetCorsOriginAccessListForOrigin( +void ElectronBrowserContext::SetCorsOriginAccessListForOrigin( const url::Origin& source_origin, std::vector allow_patterns, std::vector block_patterns, @@ -341,7 +431,7 @@ void AtomBrowserContext::SetCorsOriginAccessListForOrigin( base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(closure)); } -ResolveProxyHelper* AtomBrowserContext::GetResolveProxyHelper() { +ResolveProxyHelper* ElectronBrowserContext::GetResolveProxyHelper() { if (!resolve_proxy_helper_) { resolve_proxy_helper_ = base::MakeRefCounted(this); } @@ -349,18 +439,18 @@ ResolveProxyHelper* AtomBrowserContext::GetResolveProxyHelper() { } // static -scoped_refptr AtomBrowserContext::From( +scoped_refptr ElectronBrowserContext::From( const std::string& partition, bool in_memory, const base::DictionaryValue& options) { PartitionKey key(partition, in_memory); auto* browser_context = browser_context_map_[key].get(); if (browser_context) - return scoped_refptr(browser_context); + return scoped_refptr(browser_context); - auto* new_context = new AtomBrowserContext(partition, in_memory, options); + auto* new_context = new ElectronBrowserContext(partition, in_memory, options); browser_context_map_[key] = new_context->GetWeakPtr(); - return scoped_refptr(new_context); + return scoped_refptr(new_context); } } // namespace electron diff --git a/shell/browser/atom_browser_context.h b/shell/browser/electron_browser_context.h similarity index 76% rename from shell/browser/atom_browser_context.h rename to shell/browser/electron_browser_context.h index 685d3a147fc80..bcf9ca3ca582b 100644 --- a/shell/browser/atom_browser_context.h +++ b/shell/browser/electron_browser_context.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_BROWSER_CONTEXT_H_ -#define SHELL_BROWSER_ATOM_BROWSER_CONTEXT_H_ +#ifndef SHELL_BROWSER_ELECTRON_BROWSER_CONTEXT_H_ +#define SHELL_BROWSER_ELECTRON_BROWSER_CONTEXT_H_ #include #include @@ -17,6 +17,7 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/resource_context.h" #include "electron/buildflags/buildflags.h" +#include "services/network/public/mojom/network_context.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "shell/browser/media/media_device_id_salt.h" @@ -34,23 +35,24 @@ class SpecialStoragePolicy; #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) namespace extensions { -class AtomExtensionSystem; +class ElectronExtensionSystem; } #endif namespace electron { -class AtomBrowserContext; -class AtomDownloadManagerDelegate; -class AtomPermissionManager; +class ElectronBrowserContext; +class ElectronDownloadManagerDelegate; +class ElectronPermissionManager; class CookieChangeNotifier; class ResolveProxyHelper; class SpecialStoragePolicy; class WebViewManager; -class AtomBrowserContext - : public base::RefCountedDeleteOnSequence, - public content::BrowserContext { +class ElectronBrowserContext + : public base::RefCountedDeleteOnSequence, + public content::BrowserContext, + public network::mojom::TrustedURLLoaderAuthClient { public: // partition_id => browser_context struct PartitionKey { @@ -71,12 +73,12 @@ class AtomBrowserContext } }; using BrowserContextMap = - std::map>; + std::map>; // Get or create the BrowserContext according to its |partition| and // |in_memory|. The |options| will be passed to constructor when there is no // existing BrowserContext. - static scoped_refptr From( + static scoped_refptr From( const std::string& partition, bool in_memory, const base::DictionaryValue& options = base::DictionaryValue()); @@ -113,6 +115,7 @@ class AtomBrowserContext storage::SpecialStoragePolicy* GetSpecialStoragePolicy() override; content::ClientHintsControllerDelegate* GetClientHintsControllerDelegate() override; + content::StorageNotificationService* GetStorageNotificationService() override; // extensions deps void SetCorsOriginAccessListForOrigin( @@ -134,19 +137,23 @@ class AtomBrowserContext ValueMapPrefStore* in_memory_pref_store() const { return in_memory_pref_store_; } - base::WeakPtr GetWeakPtr() { + base::WeakPtr GetWeakPtr() { return weak_factory_.GetWeakPtr(); } protected: - AtomBrowserContext(const std::string& partition, - bool in_memory, - const base::DictionaryValue& options); - ~AtomBrowserContext() override; + ElectronBrowserContext(const std::string& partition, + bool in_memory, + const base::DictionaryValue& options); + ~ElectronBrowserContext() override; private: - friend class base::RefCountedDeleteOnSequence; - friend class base::DeleteHelper; + friend class base::RefCountedDeleteOnSequence; + friend class base::DeleteHelper; + + void OnLoaderCreated(int32_t request_id, + mojo::PendingReceiver + header_client) override; // Initialize pref registry. void InitPrefs(); @@ -158,9 +165,9 @@ class AtomBrowserContext std::unique_ptr resource_context_; std::unique_ptr cookie_change_notifier_; std::unique_ptr prefs_; - std::unique_ptr download_manager_delegate_; + std::unique_ptr download_manager_delegate_; std::unique_ptr guest_manager_; - std::unique_ptr permission_manager_; + std::unique_ptr permission_manager_; std::unique_ptr media_device_id_salt_; scoped_refptr resolve_proxy_helper_; scoped_refptr storage_policy_; @@ -179,17 +186,18 @@ class AtomBrowserContext #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) // Owned by the KeyedService system. - extensions::AtomExtensionSystem* extension_system_; + extensions::ElectronExtensionSystem* extension_system_; #endif // Shared URLLoaderFactory. scoped_refptr url_loader_factory_; + mojo::Receiver auth_client_{this}; - base::WeakPtrFactory weak_factory_; + base::WeakPtrFactory weak_factory_; - DISALLOW_COPY_AND_ASSIGN(AtomBrowserContext); + DISALLOW_COPY_AND_ASSIGN(ElectronBrowserContext); }; } // namespace electron -#endif // SHELL_BROWSER_ATOM_BROWSER_CONTEXT_H_ +#endif // SHELL_BROWSER_ELECTRON_BROWSER_CONTEXT_H_ diff --git a/shell/browser/atom_browser_main_parts.cc b/shell/browser/electron_browser_main_parts.cc similarity index 84% rename from shell/browser/atom_browser_main_parts.cc rename to shell/browser/electron_browser_main_parts.cc index b5f5c1209c652..90a0cca4d2554 100644 --- a/shell/browser/atom_browser_main_parts.cc +++ b/shell/browser/electron_browser_main_parts.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_browser_main_parts.h" +#include "shell/browser/electron_browser_main_parts.h" #include @@ -22,25 +22,26 @@ #include "chrome/browser/icon_manager.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/system_connector.h" #include "content/public/browser/web_ui_controller_factory.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/common/result_codes.h" -#include "content/public/common/service_manager_connection.h" #include "electron/buildflags/buildflags.h" #include "media/base/localized_strings.h" #include "services/device/public/mojom/constants.mojom.h" #include "services/network/public/cpp/features.h" #include "services/service_manager/public/cpp/connector.h" -#include "shell/app/atom_main_delegate.h" -#include "shell/browser/api/atom_api_app.h" +#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h" +#include "shell/app/electron_main_delegate.h" +#include "shell/browser/api/electron_api_app.h" #include "shell/browser/api/trackable_object.h" -#include "shell/browser/atom_browser_client.h" -#include "shell/browser/atom_browser_context.h" -#include "shell/browser/atom_paths.h" -#include "shell/browser/atom_web_ui_controller_factory.h" #include "shell/browser/browser.h" #include "shell/browser/browser_process_impl.h" +#include "shell/browser/electron_browser_client.h" +#include "shell/browser/electron_browser_context.h" +#include "shell/browser/electron_paths.h" +#include "shell/browser/electron_web_ui_controller_factory.h" #include "shell/browser/feature_list.h" #include "shell/browser/javascript_environment.h" #include "shell/browser/media/media_capture_devices_dispatcher.h" @@ -86,7 +87,7 @@ #if defined(OS_MACOSX) #include "shell/browser/ui/cocoa/views_delegate_mac.h" #else -#include "shell/browser/ui/views/atom_views_delegate.h" +#include "shell/browser/ui/views/electron_views_delegate.h" #endif #if defined(OS_LINUX) @@ -98,11 +99,15 @@ #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "extensions/browser/browser_context_keyed_service_factories.h" #include "extensions/common/extension_api.h" -#include "shell/browser/extensions/atom_browser_context_keyed_service_factories.h" -#include "shell/browser/extensions/atom_extensions_browser_client.h" -#include "shell/common/extensions/atom_extensions_client.h" +#include "shell/browser/extensions/electron_browser_context_keyed_service_factories.h" +#include "shell/browser/extensions/electron_extensions_browser_client.h" +#include "shell/common/extensions/electron_extensions_client.h" #endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "chrome/browser/spellchecker/spellcheck_factory.h" // nogncheck +#endif + namespace electron { namespace { @@ -210,23 +215,23 @@ int X11EmptyIOErrorHandler(Display* d) { } // namespace // static -AtomBrowserMainParts* AtomBrowserMainParts::self_ = nullptr; +ElectronBrowserMainParts* ElectronBrowserMainParts::self_ = nullptr; -AtomBrowserMainParts::AtomBrowserMainParts( +ElectronBrowserMainParts::ElectronBrowserMainParts( const content::MainFunctionParams& params) : fake_browser_process_(new BrowserProcessImpl), browser_(new Browser), node_bindings_( NodeBindings::Create(NodeBindings::BrowserEnvironment::BROWSER)), electron_bindings_(new ElectronBindings(uv_default_loop())) { - DCHECK(!self_) << "Cannot have two AtomBrowserMainParts"; + DCHECK(!self_) << "Cannot have two ElectronBrowserMainParts"; self_ = this; // Register extension scheme as web safe scheme. content::ChildProcessSecurityPolicy::GetInstance()->RegisterWebSafeScheme( "chrome-extension"); } -AtomBrowserMainParts::~AtomBrowserMainParts() { +ElectronBrowserMainParts::~ElectronBrowserMainParts() { asar::ClearArchives(); // Leak the JavascriptEnvironment on exit. // This is to work around the bug that V8 would be waiting for background @@ -240,12 +245,12 @@ AtomBrowserMainParts::~AtomBrowserMainParts() { } // static -AtomBrowserMainParts* AtomBrowserMainParts::Get() { +ElectronBrowserMainParts* ElectronBrowserMainParts::Get() { DCHECK(self_); return self_; } -bool AtomBrowserMainParts::SetExitCode(int code) { +bool ElectronBrowserMainParts::SetExitCode(int code) { if (!exit_code_) return false; @@ -253,11 +258,11 @@ bool AtomBrowserMainParts::SetExitCode(int code) { return true; } -int AtomBrowserMainParts::GetExitCode() { +int ElectronBrowserMainParts::GetExitCode() { return exit_code_ != nullptr ? *exit_code_ : 0; } -void AtomBrowserMainParts::RegisterDestructionCallback( +void ElectronBrowserMainParts::RegisterDestructionCallback( base::OnceClosure callback) { // The destructors should be called in reversed order, so dependencies between // JavaScript objects can be correctly resolved. @@ -265,7 +270,7 @@ void AtomBrowserMainParts::RegisterDestructionCallback( destructors_.insert(destructors_.begin(), std::move(callback)); } -int AtomBrowserMainParts::PreEarlyInitialization() { +int ElectronBrowserMainParts::PreEarlyInitialization() { field_trial_list_ = std::make_unique(nullptr); #if defined(USE_X11) views::LinuxUI::SetInstance(BuildGtkUi()); @@ -284,7 +289,7 @@ int AtomBrowserMainParts::PreEarlyInitialization() { return service_manager::RESULT_CODE_NORMAL_EXIT; } -void AtomBrowserMainParts::PostEarlyInitialization() { +void ElectronBrowserMainParts::PostEarlyInitialization() { // A workaround was previously needed because there was no ThreadTaskRunner // set. If this check is failing we may need to re-add that workaround DCHECK(base::ThreadTaskRunnerHandle::IsSet()); @@ -354,7 +359,7 @@ void AtomBrowserMainParts::PostEarlyInitialization() { fake_browser_process_->PostEarlyInitialization(); } -int AtomBrowserMainParts::PreCreateThreads() { +int ElectronBrowserMainParts::PreCreateThreads() { #if defined(USE_AURA) display::Screen* screen = views::CreateDesktopScreen(); display::Screen::SetScreenInstance(screen); @@ -368,7 +373,7 @@ int AtomBrowserMainParts::PreCreateThreads() { // Initialize the app locale. fake_browser_process_->SetApplicationLocale( - AtomBrowserClient::Get()->GetApplicationLocale()); + ElectronBrowserClient::Get()->GetApplicationLocale()); // Force MediaCaptureDevicesDispatcher to be created on UI thread. MediaCaptureDevicesDispatcher::GetInstance(); @@ -385,7 +390,13 @@ int AtomBrowserMainParts::PreCreateThreads() { return 0; } -void AtomBrowserMainParts::PostDestroyThreads() { +void ElectronBrowserMainParts::PostCreateThreads() { + base::PostTask( + FROM_HERE, {content::BrowserThread::IO}, + base::BindOnce(&tracing::TracingSamplerProfiler::CreateOnChildThread)); +} + +void ElectronBrowserMainParts::PostDestroyThreads() { #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) extensions_browser_client_.reset(); extensions::ExtensionsBrowserClient::Set(nullptr); @@ -397,7 +408,7 @@ void AtomBrowserMainParts::PostDestroyThreads() { fake_browser_process_->PostDestroyThreads(); } -void AtomBrowserMainParts::ToolkitInitialized() { +void ElectronBrowserMainParts::ToolkitInitialized() { ui::MaterialDesignController::Initialize(); #if defined(USE_AURA) && defined(USE_X11) @@ -424,24 +435,29 @@ void AtomBrowserMainParts::ToolkitInitialized() { #endif } -void AtomBrowserMainParts::PreMainMessageLoopRun() { +void ElectronBrowserMainParts::PreMainMessageLoopRun() { // Run user's main script before most things get initialized, so we can have // a chance to setup everything. node_bindings_->PrepareMessageLoop(); node_bindings_->RunMessageLoop(); #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - extensions_client_ = std::make_unique(); + extensions_client_ = std::make_unique(); extensions::ExtensionsClient::Set(extensions_client_.get()); // BrowserContextKeyedAPIServiceFactories require an ExtensionsBrowserClient. - extensions_browser_client_ = std::make_unique(); + extensions_browser_client_ = + std::make_unique(); extensions::ExtensionsBrowserClient::Set(extensions_browser_client_.get()); extensions::EnsureBrowserContextKeyedServiceFactoriesBuilt(); extensions::electron::EnsureBrowserContextKeyedServiceFactoriesBuilt(); #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + SpellcheckServiceFactory::GetInstance(); +#endif + // url::Add*Scheme are not threadsafe, this helps prevent data races. url::LockSchemeRegistries(); @@ -455,7 +471,7 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() { base::Unretained(js_env_->isolate()))); content::WebUIControllerFactory::RegisterFactory( - AtomWebUIControllerFactory::GetInstance()); + ElectronWebUIControllerFactory::GetInstance()); // --remote-debugging-port auto* command_line = base::CommandLine::ForCurrentProcess(); @@ -463,7 +479,7 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() { DevToolsManagerDelegate::StartHttpHandler(); #if !defined(OS_MACOSX) - // The corresponding call in macOS is in AtomApplicationDelegate. + // The corresponding call in macOS is in ElectronApplicationDelegate. Browser::Get()->WillFinishLaunching(); Browser::Get()->DidFinishLaunching(base::DictionaryValue()); #endif @@ -472,18 +488,18 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() { Browser::Get()->PreMainMessageLoopRun(); } -bool AtomBrowserMainParts::MainMessageLoopRun(int* result_code) { +bool ElectronBrowserMainParts::MainMessageLoopRun(int* result_code) { js_env_->OnMessageLoopCreated(); exit_code_ = result_code; return content::BrowserMainParts::MainMessageLoopRun(result_code); } -void AtomBrowserMainParts::PreDefaultMainMessageLoopRun( +void ElectronBrowserMainParts::PreDefaultMainMessageLoopRun( base::OnceClosure quit_closure) { Browser::Get()->SetMainMessageLoopQuitClosure(std::move(quit_closure)); } -void AtomBrowserMainParts::PostMainMessageLoopStart() { +void ElectronBrowserMainParts::PostMainMessageLoopStart() { #if defined(USE_X11) // Installs the X11 error handlers for the browser process after the // main message loop has started. This will allow us to exit cleanly @@ -498,7 +514,7 @@ void AtomBrowserMainParts::PostMainMessageLoopStart() { #endif } -void AtomBrowserMainParts::PostMainMessageLoopRun() { +void ElectronBrowserMainParts::PostMainMessageLoopRun() { #if defined(USE_X11) // Unset the X11 error handlers. The X11 error handlers log the errors using a // |PostTask()| on the message-loop. But since the message-loop is in the @@ -506,9 +522,6 @@ void AtomBrowserMainParts::PostMainMessageLoopRun() { ui::SetX11ErrorHandlers(X11EmptyErrorHandler, X11EmptyIOErrorHandler); #endif - node_debugger_->Stop(); - js_env_->OnMessageLoopDestroying(); - #if defined(OS_MACOSX) FreeAppDelegate(); #endif @@ -525,16 +538,22 @@ void AtomBrowserMainParts::PostMainMessageLoopRun() { ++iter; } + // Destroy node platform after all destructors_ are executed, as they may + // invoke Node/V8 APIs inside them. + node_debugger_->Stop(); + node_env_.reset(); + js_env_->OnMessageLoopDestroying(); + fake_browser_process_->PostMainMessageLoopRun(); } #if !defined(OS_MACOSX) -void AtomBrowserMainParts::PreMainMessageLoopStart() { +void ElectronBrowserMainParts::PreMainMessageLoopStart() { PreMainMessageLoopStartCommon(); } #endif -void AtomBrowserMainParts::PreMainMessageLoopStartCommon() { +void ElectronBrowserMainParts::PreMainMessageLoopStartCommon() { #if defined(OS_MACOSX) InitializeMainNib(); RegisterURLHandler(); @@ -543,21 +562,18 @@ void AtomBrowserMainParts::PreMainMessageLoopStartCommon() { } device::mojom::GeolocationControl* -AtomBrowserMainParts::GetGeolocationControl() { +ElectronBrowserMainParts::GetGeolocationControl() { if (geolocation_control_) return geolocation_control_.get(); - auto request = mojo::MakeRequest(&geolocation_control_); - if (!content::ServiceManagerConnection::GetForProcess()) - return geolocation_control_.get(); - - service_manager::Connector* connector = - content::ServiceManagerConnection::GetForProcess()->GetConnector(); - connector->BindInterface(device::mojom::kServiceName, std::move(request)); + auto receiver = geolocation_control_.BindNewPipeAndPassReceiver(); + service_manager::Connector* connector = content::GetSystemConnector(); + if (connector) + connector->Connect(device::mojom::kServiceName, std::move(receiver)); return geolocation_control_.get(); } -IconManager* AtomBrowserMainParts::GetIconManager() { +IconManager* ElectronBrowserMainParts::GetIconManager() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!icon_manager_.get()) icon_manager_ = std::make_unique(); diff --git a/shell/browser/atom_browser_main_parts.h b/shell/browser/electron_browser_main_parts.h similarity index 81% rename from shell/browser/atom_browser_main_parts.h rename to shell/browser/electron_browser_main_parts.h index f79498d3d9f36..6a72ea90c08c7 100644 --- a/shell/browser/atom_browser_main_parts.h +++ b/shell/browser/electron_browser_main_parts.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_BROWSER_MAIN_PARTS_H_ -#define SHELL_BROWSER_ATOM_BROWSER_MAIN_PARTS_H_ +#ifndef SHELL_BROWSER_ELECTRON_BROWSER_MAIN_PARTS_H_ +#define SHELL_BROWSER_ELECTRON_BROWSER_MAIN_PARTS_H_ #include #include @@ -16,6 +16,7 @@ #include "content/public/browser/browser_main_parts.h" #include "content/public/common/main_function_params.h" #include "electron/buildflags/buildflags.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/mojom/geolocation_control.mojom.h" #include "ui/views/layout/layout_provider.h" @@ -30,7 +31,7 @@ class WMState; namespace electron { -class AtomBrowserContext; +class ElectronBrowserContext; class Browser; class ElectronBindings; class JavascriptEnvironment; @@ -40,8 +41,8 @@ class NodeEnvironment; class BridgeTaskRunner; #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) -class AtomExtensionsClient; -class AtomExtensionsBrowserClient; +class ElectronExtensionsClient; +class ElectronExtensionsBrowserClient; #endif #if defined(TOOLKIT_VIEWS) @@ -52,12 +53,12 @@ class ViewsDelegate; class ViewsDelegateMac; #endif -class AtomBrowserMainParts : public content::BrowserMainParts { +class ElectronBrowserMainParts : public content::BrowserMainParts { public: - explicit AtomBrowserMainParts(const content::MainFunctionParams& params); - ~AtomBrowserMainParts() override; + explicit ElectronBrowserMainParts(const content::MainFunctionParams& params); + ~ElectronBrowserMainParts() override; - static AtomBrowserMainParts* Get(); + static ElectronBrowserMainParts* Get(); // Sets the exit code, will fail if the message loop is not ready. bool SetExitCode(int code); @@ -92,6 +93,7 @@ class AtomBrowserMainParts : public content::BrowserMainParts { void PostMainMessageLoopStart() override; void PostMainMessageLoopRun() override; void PreMainMessageLoopStart() override; + void PostCreateThreads() override; void PostDestroyThreads() override; private: @@ -137,8 +139,8 @@ class AtomBrowserMainParts : public content::BrowserMainParts { std::unique_ptr field_trial_list_; #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - std::unique_ptr extensions_client_; - std::unique_ptr extensions_browser_client_; + std::unique_ptr extensions_client_; + std::unique_ptr extensions_browser_client_; #endif base::RepeatingTimer gc_timer_; @@ -146,13 +148,13 @@ class AtomBrowserMainParts : public content::BrowserMainParts { // List of callbacks should be executed before destroying JS env. std::list destructors_; - device::mojom::GeolocationControlPtr geolocation_control_; + mojo::Remote geolocation_control_; - static AtomBrowserMainParts* self_; + static ElectronBrowserMainParts* self_; - DISALLOW_COPY_AND_ASSIGN(AtomBrowserMainParts); + DISALLOW_COPY_AND_ASSIGN(ElectronBrowserMainParts); }; } // namespace electron -#endif // SHELL_BROWSER_ATOM_BROWSER_MAIN_PARTS_H_ +#endif // SHELL_BROWSER_ELECTRON_BROWSER_MAIN_PARTS_H_ diff --git a/shell/browser/atom_browser_main_parts_mac.mm b/shell/browser/electron_browser_main_parts_mac.mm similarity index 79% rename from shell/browser/atom_browser_main_parts_mac.mm rename to shell/browser/electron_browser_main_parts_mac.mm index 523b87c17475f..3b6f398d3884c 100644 --- a/shell/browser/atom_browser_main_parts_mac.mm +++ b/shell/browser/electron_browser_main_parts_mac.mm @@ -2,21 +2,22 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_browser_main_parts.h" +#include "shell/browser/electron_browser_main_parts.h" #include "base/mac/bundle_locations.h" #include "base/mac/foundation_util.h" #include "base/path_service.h" -#include "shell/browser/atom_paths.h" -#import "shell/browser/mac/atom_application.h" -#include "shell/browser/mac/atom_application_delegate.h" +#include "shell/browser/electron_paths.h" +#import "shell/browser/mac/electron_application.h" +#include "shell/browser/mac/electron_application_delegate.h" #include "ui/base/l10n/l10n_util_mac.h" namespace electron { -void AtomBrowserMainParts::PreMainMessageLoopStart() { +void ElectronBrowserMainParts::PreMainMessageLoopStart() { // Set our own application delegate. - AtomApplicationDelegate* delegate = [[AtomApplicationDelegate alloc] init]; + ElectronApplicationDelegate* delegate = + [[ElectronApplicationDelegate alloc] init]; [NSApp setDelegate:delegate]; PreMainMessageLoopStartCommon(); @@ -28,17 +29,17 @@ forKey:@"NSTreatUnknownArgumentsAsOpen"]; } -void AtomBrowserMainParts::FreeAppDelegate() { +void ElectronBrowserMainParts::FreeAppDelegate() { [[NSApp delegate] release]; [NSApp setDelegate:nil]; } -void AtomBrowserMainParts::RegisterURLHandler() { +void ElectronBrowserMainParts::RegisterURLHandler() { [[AtomApplication sharedApplication] registerURLHandler]; } // Replicates NSApplicationMain, but doesn't start a run loop. -void AtomBrowserMainParts::InitializeMainNib() { +void ElectronBrowserMainParts::InitializeMainNib() { auto infoDictionary = base::mac::OuterBundle().infoDictionary; auto principalClass = diff --git a/shell/browser/atom_browser_main_parts_posix.cc b/shell/browser/electron_browser_main_parts_posix.cc similarity index 97% rename from shell/browser/atom_browser_main_parts_posix.cc rename to shell/browser/electron_browser_main_parts_posix.cc index b3b4716deb5f7..4bd8f002d3e04 100644 --- a/shell/browser/atom_browser_main_parts_posix.cc +++ b/shell/browser/electron_browser_main_parts_posix.cc @@ -4,7 +4,7 @@ // Most code came from: chrome/browser/chrome_browser_main_posix.cc. -#include "shell/browser/atom_browser_main_parts.h" +#include "shell/browser/electron_browser_main_parts.h" #include #include @@ -164,7 +164,7 @@ void ShutdownDetector::ThreadMain() { } // namespace -void AtomBrowserMainParts::HandleSIGCHLD() { +void ElectronBrowserMainParts::HandleSIGCHLD() { // We need to accept SIGCHLD, even though our handler is a no-op because // otherwise we cannot wait on children. (According to POSIX 2001.) struct sigaction action; @@ -173,7 +173,7 @@ void AtomBrowserMainParts::HandleSIGCHLD() { CHECK_EQ(sigaction(SIGCHLD, &action, nullptr), 0); } -void AtomBrowserMainParts::HandleShutdownSignals() { +void ElectronBrowserMainParts::HandleShutdownSignals() { int pipefd[2]; int ret = pipe(pipefd); if (ret < 0) { diff --git a/shell/browser/atom_download_manager_delegate.cc b/shell/browser/electron_download_manager_delegate.cc similarity index 85% rename from shell/browser/atom_download_manager_delegate.cc rename to shell/browser/electron_download_manager_delegate.cc index 1ca768e03a276..dd07fc47aa9e4 100644 --- a/shell/browser/atom_download_manager_delegate.cc +++ b/shell/browser/electron_download_manager_delegate.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_download_manager_delegate.h" +#include "shell/browser/electron_download_manager_delegate.h" #include #include @@ -18,8 +18,8 @@ #include "content/public/browser/download_item_utils.h" #include "content/public/browser/download_manager.h" #include "net/base/filename_util.h" -#include "shell/browser/api/atom_api_download_item.h" -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/api/electron_api_download_item.h" +#include "shell/browser/electron_browser_context.h" #include "shell/browser/native_window.h" #include "shell/browser/ui/file_dialog.h" #include "shell/browser/web_contents_preferences.h" @@ -49,11 +49,11 @@ base::FilePath CreateDownloadPath(const GURL& url, } // namespace -AtomDownloadManagerDelegate::AtomDownloadManagerDelegate( +ElectronDownloadManagerDelegate::ElectronDownloadManagerDelegate( content::DownloadManager* manager) : download_manager_(manager), weak_ptr_factory_(this) {} -AtomDownloadManagerDelegate::~AtomDownloadManagerDelegate() { +ElectronDownloadManagerDelegate::~ElectronDownloadManagerDelegate() { if (download_manager_) { DCHECK_EQ(static_cast(this), download_manager_->GetDelegate()); @@ -62,8 +62,9 @@ AtomDownloadManagerDelegate::~AtomDownloadManagerDelegate() { } } -void AtomDownloadManagerDelegate::GetItemSavePath(download::DownloadItem* item, - base::FilePath* path) { +void ElectronDownloadManagerDelegate::GetItemSavePath( + download::DownloadItem* item, + base::FilePath* path) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Locker locker(isolate); v8::HandleScope handle_scope(isolate); @@ -73,7 +74,7 @@ void AtomDownloadManagerDelegate::GetItemSavePath(download::DownloadItem* item, *path = download->GetSavePath(); } -void AtomDownloadManagerDelegate::GetItemSaveDialogOptions( +void ElectronDownloadManagerDelegate::GetItemSaveDialogOptions( download::DownloadItem* item, file_dialog::DialogSettings* options) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); @@ -85,7 +86,7 @@ void AtomDownloadManagerDelegate::GetItemSaveDialogOptions( *options = download->GetSaveDialogOptions(); } -void AtomDownloadManagerDelegate::OnDownloadPathGenerated( +void ElectronDownloadManagerDelegate::OnDownloadPathGenerated( uint32_t download_id, const content::DownloadTargetCallback& callback, const base::FilePath& default_path) { @@ -125,9 +126,9 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated( v8::Isolate* isolate = v8::Isolate::GetCurrent(); electron::util::Promise dialog_promise(isolate); - auto dialog_callback = - base::BindOnce(&AtomDownloadManagerDelegate::OnDownloadSaveDialogDone, - base::Unretained(this), download_id, callback); + auto dialog_callback = base::BindOnce( + &ElectronDownloadManagerDelegate::OnDownloadSaveDialogDone, + base::Unretained(this), download_id, callback); ignore_result(dialog_promise.Then(std::move(dialog_callback))); file_dialog::ShowSaveDialog(settings, std::move(dialog_promise)); @@ -138,7 +139,7 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated( } } -void AtomDownloadManagerDelegate::OnDownloadSaveDialogDone( +void ElectronDownloadManagerDelegate::OnDownloadSaveDialogDone( uint32_t download_id, const content::DownloadTargetCallback& download_callback, gin_helper::Dictionary result) { @@ -156,8 +157,9 @@ void AtomDownloadManagerDelegate::OnDownloadSaveDialogDone( if (!canceled) { if (result.Get("filePath", &path)) { // Remember the last selected download directory. - AtomBrowserContext* browser_context = static_cast( - download_manager_->GetBrowserContext()); + ElectronBrowserContext* browser_context = + static_cast( + download_manager_->GetBrowserContext()); browser_context->prefs()->SetFilePath(prefs::kDownloadDefaultDirectory, path.DirName()); @@ -182,12 +184,12 @@ void AtomDownloadManagerDelegate::OnDownloadSaveDialogDone( interrupt_reason); } -void AtomDownloadManagerDelegate::Shutdown() { +void ElectronDownloadManagerDelegate::Shutdown() { weak_ptr_factory_.InvalidateWeakPtrs(); download_manager_ = nullptr; } -bool AtomDownloadManagerDelegate::DetermineDownloadTarget( +bool ElectronDownloadManagerDelegate::DetermineDownloadTarget( download::DownloadItem* download, const content::DownloadTargetCallback& callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -212,8 +214,9 @@ bool AtomDownloadManagerDelegate::DetermineDownloadTarget( return true; } - AtomBrowserContext* browser_context = - static_cast(download_manager_->GetBrowserContext()); + ElectronBrowserContext* browser_context = + static_cast( + download_manager_->GetBrowserContext()); base::FilePath default_download_path = browser_context->prefs()->GetFilePath(prefs::kDownloadDefaultDirectory); @@ -225,19 +228,19 @@ bool AtomDownloadManagerDelegate::DetermineDownloadTarget( download->GetContentDisposition(), download->GetSuggestedFilename(), download->GetMimeType(), default_download_path), - base::BindOnce(&AtomDownloadManagerDelegate::OnDownloadPathGenerated, + base::BindOnce(&ElectronDownloadManagerDelegate::OnDownloadPathGenerated, weak_ptr_factory_.GetWeakPtr(), download->GetId(), callback)); return true; } -bool AtomDownloadManagerDelegate::ShouldOpenDownload( +bool ElectronDownloadManagerDelegate::ShouldOpenDownload( download::DownloadItem* download, const content::DownloadOpenDelayedCallback& callback) { return true; } -void AtomDownloadManagerDelegate::GetNextId( +void ElectronDownloadManagerDelegate::GetNextId( const content::DownloadIdCallback& callback) { static uint32_t next_id = download::DownloadItem::kInvalidId + 1; callback.Run(next_id++); diff --git a/shell/browser/atom_download_manager_delegate.h b/shell/browser/electron_download_manager_delegate.h similarity index 75% rename from shell/browser/atom_download_manager_delegate.h rename to shell/browser/electron_download_manager_delegate.h index 2cbf725ad907a..986e2d88e6d96 100644 --- a/shell/browser/atom_download_manager_delegate.h +++ b/shell/browser/electron_download_manager_delegate.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_DOWNLOAD_MANAGER_DELEGATE_H_ -#define SHELL_BROWSER_ATOM_DOWNLOAD_MANAGER_DELEGATE_H_ +#ifndef SHELL_BROWSER_ELECTRON_DOWNLOAD_MANAGER_DELEGATE_H_ +#define SHELL_BROWSER_ELECTRON_DOWNLOAD_MANAGER_DELEGATE_H_ #include @@ -18,13 +18,14 @@ class DownloadManager; namespace electron { -class AtomDownloadManagerDelegate : public content::DownloadManagerDelegate { +class ElectronDownloadManagerDelegate + : public content::DownloadManagerDelegate { public: using CreateDownloadPathCallback = base::Callback; - explicit AtomDownloadManagerDelegate(content::DownloadManager* manager); - ~AtomDownloadManagerDelegate() override; + explicit ElectronDownloadManagerDelegate(content::DownloadManager* manager); + ~ElectronDownloadManagerDelegate() override; // content::DownloadManagerDelegate: void Shutdown() override; @@ -52,11 +53,11 @@ class AtomDownloadManagerDelegate : public content::DownloadManagerDelegate { gin_helper::Dictionary result); content::DownloadManager* download_manager_; - base::WeakPtrFactory weak_ptr_factory_; + base::WeakPtrFactory weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(AtomDownloadManagerDelegate); + DISALLOW_COPY_AND_ASSIGN(ElectronDownloadManagerDelegate); }; } // namespace electron -#endif // SHELL_BROWSER_ATOM_DOWNLOAD_MANAGER_DELEGATE_H_ +#endif // SHELL_BROWSER_ELECTRON_DOWNLOAD_MANAGER_DELEGATE_H_ diff --git a/shell/browser/atom_gpu_client.cc b/shell/browser/electron_gpu_client.cc similarity index 75% rename from shell/browser/atom_gpu_client.cc rename to shell/browser/electron_gpu_client.cc index f5e04fc449934..c7457849a710a 100644 --- a/shell/browser/atom_gpu_client.cc +++ b/shell/browser/electron_gpu_client.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_gpu_client.h" +#include "shell/browser/electron_gpu_client.h" #include "base/environment.h" @@ -12,9 +12,9 @@ namespace electron { -AtomGpuClient::AtomGpuClient() = default; +ElectronGpuClient::ElectronGpuClient() = default; -void AtomGpuClient::PreCreateMessageLoop() { +void ElectronGpuClient::PreCreateMessageLoop() { #if defined(OS_WIN) auto env = base::Environment::Create(); if (env->HasVar("ELECTRON_DEFAULT_ERROR_MODE")) diff --git a/shell/browser/atom_gpu_client.h b/shell/browser/electron_gpu_client.h similarity index 55% rename from shell/browser/atom_gpu_client.h rename to shell/browser/electron_gpu_client.h index fc64f2a8492a2..bbedf298b41c6 100644 --- a/shell/browser/atom_gpu_client.h +++ b/shell/browser/electron_gpu_client.h @@ -2,24 +2,24 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_GPU_CLIENT_H_ -#define SHELL_BROWSER_ATOM_GPU_CLIENT_H_ +#ifndef SHELL_BROWSER_ELECTRON_GPU_CLIENT_H_ +#define SHELL_BROWSER_ELECTRON_GPU_CLIENT_H_ #include "content/public/gpu/content_gpu_client.h" namespace electron { -class AtomGpuClient : public content::ContentGpuClient { +class ElectronGpuClient : public content::ContentGpuClient { public: - AtomGpuClient(); + ElectronGpuClient(); // content::ContentGpuClient: void PreCreateMessageLoop() override; private: - DISALLOW_COPY_AND_ASSIGN(AtomGpuClient); + DISALLOW_COPY_AND_ASSIGN(ElectronGpuClient); }; } // namespace electron -#endif // SHELL_BROWSER_ATOM_GPU_CLIENT_H_ +#endif // SHELL_BROWSER_ELECTRON_GPU_CLIENT_H_ diff --git a/shell/browser/atom_javascript_dialog_manager.cc b/shell/browser/electron_javascript_dialog_manager.cc similarity index 82% rename from shell/browser/atom_javascript_dialog_manager.cc rename to shell/browser/electron_javascript_dialog_manager.cc index a425537f61b2e..1aa1247d1b713 100644 --- a/shell/browser/atom_javascript_dialog_manager.cc +++ b/shell/browser/electron_javascript_dialog_manager.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_javascript_dialog_manager.h" +#include "shell/browser/electron_javascript_dialog_manager.h" #include #include @@ -10,7 +10,7 @@ #include "base/bind.h" #include "base/strings/utf_string_conversions.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/native_window.h" #include "shell/browser/ui/message_box.h" #include "shell/browser/web_contents_preferences.h" @@ -27,12 +27,12 @@ constexpr int kUserWantsNoMoreDialogs = -1; } // namespace -AtomJavaScriptDialogManager::AtomJavaScriptDialogManager( +ElectronJavaScriptDialogManager::ElectronJavaScriptDialogManager( api::WebContents* api_web_contents) : api_web_contents_(api_web_contents) {} -AtomJavaScriptDialogManager::~AtomJavaScriptDialogManager() = default; +ElectronJavaScriptDialogManager::~ElectronJavaScriptDialogManager() = default; -void AtomJavaScriptDialogManager::RunJavaScriptDialog( +void ElectronJavaScriptDialogManager::RunJavaScriptDialog( content::WebContents* web_contents, content::RenderFrameHost* rfh, JavaScriptDialogType dialog_type, @@ -61,6 +61,12 @@ void AtomJavaScriptDialogManager::RunJavaScriptDialog( return; } + auto* web_preferences = WebContentsPreferences::From(web_contents); + + if (web_preferences && web_preferences->IsEnabled("disableDialogs")) { + return std::move(callback).Run(false, base::string16()); + } + // No default button int default_id = -1; int cancel_id = 0; @@ -75,7 +81,6 @@ void AtomJavaScriptDialogManager::RunJavaScriptDialog( origin_counts_[origin]++; - auto* web_preferences = WebContentsPreferences::From(web_contents); std::string checkbox; if (origin_counts_[origin] > 1 && web_preferences && web_preferences->IsEnabled("safeDialogs") && @@ -93,6 +98,7 @@ void AtomJavaScriptDialogManager::RunJavaScriptDialog( electron::MessageBoxSettings settings; settings.parent_window = window; + settings.checkbox_label = checkbox; settings.buttons = buttons; settings.default_id = default_id; settings.cancel_id = cancel_id; @@ -100,12 +106,12 @@ void AtomJavaScriptDialogManager::RunJavaScriptDialog( electron::ShowMessageBox( settings, - base::BindOnce(&AtomJavaScriptDialogManager::OnMessageBoxCallback, + base::BindOnce(&ElectronJavaScriptDialogManager::OnMessageBoxCallback, base::Unretained(this), base::Passed(std::move(callback)), origin)); } -void AtomJavaScriptDialogManager::RunBeforeUnloadDialog( +void ElectronJavaScriptDialogManager::RunBeforeUnloadDialog( content::WebContents* web_contents, content::RenderFrameHost* rfh, bool is_reload, @@ -115,11 +121,11 @@ void AtomJavaScriptDialogManager::RunBeforeUnloadDialog( return; } -void AtomJavaScriptDialogManager::CancelDialogs( +void ElectronJavaScriptDialogManager::CancelDialogs( content::WebContents* web_contents, bool reset_state) {} -void AtomJavaScriptDialogManager::OnMessageBoxCallback( +void ElectronJavaScriptDialogManager::OnMessageBoxCallback( DialogClosedCallback callback, const std::string& origin, int code, diff --git a/shell/browser/atom_javascript_dialog_manager.h b/shell/browser/electron_javascript_dialog_manager.h similarity index 78% rename from shell/browser/atom_javascript_dialog_manager.h rename to shell/browser/electron_javascript_dialog_manager.h index 330490a6a0fd9..69a865c47f769 100644 --- a/shell/browser/atom_javascript_dialog_manager.h +++ b/shell/browser/electron_javascript_dialog_manager.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_JAVASCRIPT_DIALOG_MANAGER_H_ -#define SHELL_BROWSER_ATOM_JAVASCRIPT_DIALOG_MANAGER_H_ +#ifndef SHELL_BROWSER_ELECTRON_JAVASCRIPT_DIALOG_MANAGER_H_ +#define SHELL_BROWSER_ELECTRON_JAVASCRIPT_DIALOG_MANAGER_H_ #include #include @@ -16,10 +16,11 @@ namespace api { class WebContents; } -class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager { +class ElectronJavaScriptDialogManager + : public content::JavaScriptDialogManager { public: - explicit AtomJavaScriptDialogManager(api::WebContents* api_web_contents); - ~AtomJavaScriptDialogManager() override; + explicit ElectronJavaScriptDialogManager(api::WebContents* api_web_contents); + ~ElectronJavaScriptDialogManager() override; // content::JavaScriptDialogManager implementations. void RunJavaScriptDialog(content::WebContents* web_contents, @@ -48,4 +49,4 @@ class AtomJavaScriptDialogManager : public content::JavaScriptDialogManager { } // namespace electron -#endif // SHELL_BROWSER_ATOM_JAVASCRIPT_DIALOG_MANAGER_H_ +#endif // SHELL_BROWSER_ELECTRON_JAVASCRIPT_DIALOG_MANAGER_H_ diff --git a/shell/browser/atom_navigation_throttle.cc b/shell/browser/electron_navigation_throttle.cc similarity index 69% rename from shell/browser/atom_navigation_throttle.cc rename to shell/browser/electron_navigation_throttle.cc index f4f6aac852a22..5c0ef054421c3 100644 --- a/shell/browser/atom_navigation_throttle.cc +++ b/shell/browser/electron_navigation_throttle.cc @@ -2,25 +2,25 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_navigation_throttle.h" +#include "shell/browser/electron_navigation_throttle.h" #include "content/public/browser/navigation_handle.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" namespace electron { -AtomNavigationThrottle::AtomNavigationThrottle( +ElectronNavigationThrottle::ElectronNavigationThrottle( content::NavigationHandle* navigation_handle) : content::NavigationThrottle(navigation_handle) {} -AtomNavigationThrottle::~AtomNavigationThrottle() = default; +ElectronNavigationThrottle::~ElectronNavigationThrottle() = default; -const char* AtomNavigationThrottle::GetNameForLogging() { - return "AtomNavigationThrottle"; +const char* ElectronNavigationThrottle::GetNameForLogging() { + return "ElectronNavigationThrottle"; } content::NavigationThrottle::ThrottleCheckResult -AtomNavigationThrottle::WillRedirectRequest() { +ElectronNavigationThrottle::WillRedirectRequest() { auto* handle = navigation_handle(); auto* contents = handle->GetWebContents(); if (!contents) { diff --git a/shell/browser/electron_navigation_throttle.h b/shell/browser/electron_navigation_throttle.h new file mode 100644 index 0000000000000..b7f96205ea5c8 --- /dev/null +++ b/shell/browser/electron_navigation_throttle.h @@ -0,0 +1,28 @@ +// Copyright (c) 2018 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_BROWSER_ELECTRON_NAVIGATION_THROTTLE_H_ +#define SHELL_BROWSER_ELECTRON_NAVIGATION_THROTTLE_H_ + +#include "content/public/browser/navigation_throttle.h" + +namespace electron { + +class ElectronNavigationThrottle : public content::NavigationThrottle { + public: + explicit ElectronNavigationThrottle(content::NavigationHandle* handle); + ~ElectronNavigationThrottle() override; + + ElectronNavigationThrottle::ThrottleCheckResult WillRedirectRequest() + override; + + const char* GetNameForLogging() override; + + private: + DISALLOW_COPY_AND_ASSIGN(ElectronNavigationThrottle); +}; + +} // namespace electron + +#endif // SHELL_BROWSER_ELECTRON_NAVIGATION_THROTTLE_H_ diff --git a/shell/browser/atom_paths.h b/shell/browser/electron_paths.h similarity index 90% rename from shell/browser/atom_paths.h rename to shell/browser/electron_paths.h index d534b8991ac71..070cdb74d6a6f 100644 --- a/shell/browser/atom_paths.h +++ b/shell/browser/electron_paths.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_PATHS_H_ -#define SHELL_BROWSER_ATOM_PATHS_H_ +#ifndef SHELL_BROWSER_ELECTRON_PATHS_H_ +#define SHELL_BROWSER_ELECTRON_PATHS_H_ #include "base/base_paths.h" @@ -47,4 +47,4 @@ static_assert(PATH_START < PATH_END, "invalid PATH boundaries"); } // namespace electron -#endif // SHELL_BROWSER_ATOM_PATHS_H_ +#endif // SHELL_BROWSER_ELECTRON_PATHS_H_ diff --git a/shell/browser/atom_permission_manager.cc b/shell/browser/electron_permission_manager.cc similarity index 84% rename from shell/browser/atom_permission_manager.cc rename to shell/browser/electron_permission_manager.cc index 3c8793b7e2db6..c06828be6c36e 100644 --- a/shell/browser/atom_permission_manager.cc +++ b/shell/browser/electron_permission_manager.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_permission_manager.h" +#include "shell/browser/electron_permission_manager.h" #include #include @@ -15,8 +15,8 @@ #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" -#include "shell/browser/atom_browser_client.h" -#include "shell/browser/atom_browser_main_parts.h" +#include "shell/browser/electron_browser_client.h" +#include "shell/browser/electron_browser_main_parts.h" #include "shell/browser/web_contents_preferences.h" namespace electron { @@ -25,7 +25,7 @@ namespace { bool WebContentsDestroyed(int process_id) { content::WebContents* web_contents = - static_cast(AtomBrowserClient::Get()) + static_cast(ElectronBrowserClient::Get()) ->GetWebContentsFromProcessID(process_id); if (!web_contents) return true; @@ -33,14 +33,14 @@ bool WebContentsDestroyed(int process_id) { } void PermissionRequestResponseCallbackWrapper( - AtomPermissionManager::StatusCallback callback, + ElectronPermissionManager::StatusCallback callback, const std::vector& vector) { std::move(callback).Run(vector[0]); } } // namespace -class AtomPermissionManager::PendingRequest { +class ElectronPermissionManager::PendingRequest { public: PendingRequest(content::RenderFrameHost* render_frame_host, const std::vector& permissions, @@ -61,7 +61,7 @@ class AtomPermissionManager::PendingRequest { content::ChildProcessSecurityPolicy::GetInstance() ->GrantSendMidiSysExMessage(render_process_id_); } else if (permission == content::PermissionType::GEOLOCATION) { - AtomBrowserMainParts::Get() + ElectronBrowserMainParts::Get() ->GetGeolocationControl() ->UserDidOptIntoLocationServices(); } @@ -89,11 +89,11 @@ class AtomPermissionManager::PendingRequest { size_t remaining_results_; }; -AtomPermissionManager::AtomPermissionManager() = default; +ElectronPermissionManager::ElectronPermissionManager() = default; -AtomPermissionManager::~AtomPermissionManager() = default; +ElectronPermissionManager::~ElectronPermissionManager() = default; -void AtomPermissionManager::SetPermissionRequestHandler( +void ElectronPermissionManager::SetPermissionRequestHandler( const RequestHandler& handler) { if (handler.is_null() && !pending_requests_.IsEmpty()) { for (PendingRequestsMap::iterator iter(&pending_requests_); !iter.IsAtEnd(); @@ -107,12 +107,12 @@ void AtomPermissionManager::SetPermissionRequestHandler( request_handler_ = handler; } -void AtomPermissionManager::SetPermissionCheckHandler( +void ElectronPermissionManager::SetPermissionCheckHandler( const CheckHandler& handler) { check_handler_ = handler; } -int AtomPermissionManager::RequestPermission( +int ElectronPermissionManager::RequestPermission( content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, @@ -123,7 +123,7 @@ int AtomPermissionManager::RequestPermission( std::move(response_callback)); } -int AtomPermissionManager::RequestPermissionWithDetails( +int ElectronPermissionManager::RequestPermissionWithDetails( content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, @@ -137,7 +137,7 @@ int AtomPermissionManager::RequestPermissionWithDetails( std::move(response_callback))); } -int AtomPermissionManager::RequestPermissions( +int ElectronPermissionManager::RequestPermissions( const std::vector& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, @@ -148,7 +148,7 @@ int AtomPermissionManager::RequestPermissions( std::move(response_callback)); } -int AtomPermissionManager::RequestPermissionsWithDetails( +int ElectronPermissionManager::RequestPermissionsWithDetails( const std::vector& permissions, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, @@ -168,7 +168,7 @@ int AtomPermissionManager::RequestPermissionsWithDetails( ->GrantSendMidiSysExMessage( render_frame_host->GetProcess()->GetID()); } else if (permission == content::PermissionType::GEOLOCATION) { - AtomBrowserMainParts::Get() + ElectronBrowserMainParts::Get() ->GetGeolocationControl() ->UserDidOptIntoLocationServices(); } @@ -186,7 +186,7 @@ int AtomPermissionManager::RequestPermissionsWithDetails( for (size_t i = 0; i < permissions.size(); ++i) { auto permission = permissions[i]; const auto callback = - base::BindRepeating(&AtomPermissionManager::OnPermissionResponse, + base::BindRepeating(&ElectronPermissionManager::OnPermissionResponse, base::Unretained(this), request_id, i); auto mutable_details = details == nullptr ? base::DictionaryValue() : details->Clone(); @@ -200,7 +200,7 @@ int AtomPermissionManager::RequestPermissionsWithDetails( return request_id; } -void AtomPermissionManager::OnPermissionResponse( +void ElectronPermissionManager::OnPermissionResponse( int request_id, int permission_id, blink::mojom::PermissionStatus status) { @@ -215,18 +215,19 @@ void AtomPermissionManager::OnPermissionResponse( } } -void AtomPermissionManager::ResetPermission(content::PermissionType permission, - const GURL& requesting_origin, - const GURL& embedding_origin) {} +void ElectronPermissionManager::ResetPermission( + content::PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) {} -blink::mojom::PermissionStatus AtomPermissionManager::GetPermissionStatus( +blink::mojom::PermissionStatus ElectronPermissionManager::GetPermissionStatus( content::PermissionType permission, const GURL& requesting_origin, const GURL& embedding_origin) { return blink::mojom::PermissionStatus::GRANTED; } -int AtomPermissionManager::SubscribePermissionStatusChange( +int ElectronPermissionManager::SubscribePermissionStatusChange( content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, @@ -234,10 +235,10 @@ int AtomPermissionManager::SubscribePermissionStatusChange( return -1; } -void AtomPermissionManager::UnsubscribePermissionStatusChange( +void ElectronPermissionManager::UnsubscribePermissionStatusChange( int subscription_id) {} -bool AtomPermissionManager::CheckPermissionWithDetails( +bool ElectronPermissionManager::CheckPermissionWithDetails( content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin, @@ -258,7 +259,7 @@ bool AtomPermissionManager::CheckPermissionWithDetails( } blink::mojom::PermissionStatus -AtomPermissionManager::GetPermissionStatusForFrame( +ElectronPermissionManager::GetPermissionStatusForFrame( content::PermissionType permission, content::RenderFrameHost* render_frame_host, const GURL& requesting_origin) { diff --git a/shell/browser/atom_permission_manager.h b/shell/browser/electron_permission_manager.h similarity index 92% rename from shell/browser/atom_permission_manager.h rename to shell/browser/electron_permission_manager.h index d1a4c6889c0dc..a347afd8375e7 100644 --- a/shell/browser/atom_permission_manager.h +++ b/shell/browser/electron_permission_manager.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_PERMISSION_MANAGER_H_ -#define SHELL_BROWSER_ATOM_PERMISSION_MANAGER_H_ +#ifndef SHELL_BROWSER_ELECTRON_PERMISSION_MANAGER_H_ +#define SHELL_BROWSER_ELECTRON_PERMISSION_MANAGER_H_ #include #include @@ -20,10 +20,10 @@ class WebContents; namespace electron { -class AtomPermissionManager : public content::PermissionControllerDelegate { +class ElectronPermissionManager : public content::PermissionControllerDelegate { public: - AtomPermissionManager(); - ~AtomPermissionManager() override; + ElectronPermissionManager(); + ~ElectronPermissionManager() override; using StatusCallback = base::OnceCallback; @@ -107,9 +107,9 @@ class AtomPermissionManager : public content::PermissionControllerDelegate { PendingRequestsMap pending_requests_; - DISALLOW_COPY_AND_ASSIGN(AtomPermissionManager); + DISALLOW_COPY_AND_ASSIGN(ElectronPermissionManager); }; } // namespace electron -#endif // SHELL_BROWSER_ATOM_PERMISSION_MANAGER_H_ +#endif // SHELL_BROWSER_ELECTRON_PERMISSION_MANAGER_H_ diff --git a/shell/browser/atom_quota_permission_context.cc b/shell/browser/electron_quota_permission_context.cc similarity index 56% rename from shell/browser/atom_quota_permission_context.cc rename to shell/browser/electron_quota_permission_context.cc index d0841cb20c038..d1a7b29179d8f 100644 --- a/shell/browser/atom_quota_permission_context.cc +++ b/shell/browser/electron_quota_permission_context.cc @@ -2,15 +2,15 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_quota_permission_context.h" +#include "shell/browser/electron_quota_permission_context.h" namespace electron { -AtomQuotaPermissionContext::AtomQuotaPermissionContext() = default; +ElectronQuotaPermissionContext::ElectronQuotaPermissionContext() = default; -AtomQuotaPermissionContext::~AtomQuotaPermissionContext() = default; +ElectronQuotaPermissionContext::~ElectronQuotaPermissionContext() = default; -void AtomQuotaPermissionContext::RequestQuotaPermission( +void ElectronQuotaPermissionContext::RequestQuotaPermission( const content::StorageQuotaParams& params, int render_process_id, const PermissionCallback& callback) { diff --git a/shell/browser/atom_quota_permission_context.h b/shell/browser/electron_quota_permission_context.h similarity index 61% rename from shell/browser/atom_quota_permission_context.h rename to shell/browser/electron_quota_permission_context.h index ba8f0bc066daf..a7cfac003a00c 100644 --- a/shell/browser/atom_quota_permission_context.h +++ b/shell/browser/electron_quota_permission_context.h @@ -2,19 +2,19 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_QUOTA_PERMISSION_CONTEXT_H_ -#define SHELL_BROWSER_ATOM_QUOTA_PERMISSION_CONTEXT_H_ +#ifndef SHELL_BROWSER_ELECTRON_QUOTA_PERMISSION_CONTEXT_H_ +#define SHELL_BROWSER_ELECTRON_QUOTA_PERMISSION_CONTEXT_H_ #include "content/public/browser/quota_permission_context.h" #include "content/public/common/storage_quota_params.h" namespace electron { -class AtomQuotaPermissionContext : public content::QuotaPermissionContext { +class ElectronQuotaPermissionContext : public content::QuotaPermissionContext { public: typedef content::QuotaPermissionContext::QuotaPermissionResponse response; - AtomQuotaPermissionContext(); + ElectronQuotaPermissionContext(); // content::QuotaPermissionContext: void RequestQuotaPermission(const content::StorageQuotaParams& params, @@ -22,11 +22,11 @@ class AtomQuotaPermissionContext : public content::QuotaPermissionContext { const PermissionCallback& callback) override; private: - ~AtomQuotaPermissionContext() override; + ~ElectronQuotaPermissionContext() override; - DISALLOW_COPY_AND_ASSIGN(AtomQuotaPermissionContext); + DISALLOW_COPY_AND_ASSIGN(ElectronQuotaPermissionContext); }; } // namespace electron -#endif // SHELL_BROWSER_ATOM_QUOTA_PERMISSION_CONTEXT_H_ +#endif // SHELL_BROWSER_ELECTRON_QUOTA_PERMISSION_CONTEXT_H_ diff --git a/shell/browser/electron_speech_recognition_manager_delegate.cc b/shell/browser/electron_speech_recognition_manager_delegate.cc new file mode 100644 index 0000000000000..5a6cfa86daab2 --- /dev/null +++ b/shell/browser/electron_speech_recognition_manager_delegate.cc @@ -0,0 +1,66 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/electron_speech_recognition_manager_delegate.h" + +#include +#include + +#include "base/callback.h" + +namespace electron { + +ElectronSpeechRecognitionManagerDelegate:: + ElectronSpeechRecognitionManagerDelegate() = default; + +ElectronSpeechRecognitionManagerDelegate:: + ~ElectronSpeechRecognitionManagerDelegate() = default; + +void ElectronSpeechRecognitionManagerDelegate::OnRecognitionStart( + int session_id) {} + +void ElectronSpeechRecognitionManagerDelegate::OnAudioStart(int session_id) {} + +void ElectronSpeechRecognitionManagerDelegate::OnEnvironmentEstimationComplete( + int session_id) {} + +void ElectronSpeechRecognitionManagerDelegate::OnSoundStart(int session_id) {} + +void ElectronSpeechRecognitionManagerDelegate::OnSoundEnd(int session_id) {} + +void ElectronSpeechRecognitionManagerDelegate::OnAudioEnd(int session_id) {} + +void ElectronSpeechRecognitionManagerDelegate::OnRecognitionEnd( + int session_id) {} + +void ElectronSpeechRecognitionManagerDelegate::OnRecognitionResults( + int session_id, + const std::vector& results) {} + +void ElectronSpeechRecognitionManagerDelegate::OnRecognitionError( + int session_id, + const blink::mojom::SpeechRecognitionError& error) {} + +void ElectronSpeechRecognitionManagerDelegate::OnAudioLevelsChange( + int session_id, + float volume, + float noise_volume) {} + +void ElectronSpeechRecognitionManagerDelegate::CheckRecognitionIsAllowed( + int session_id, + base::OnceCallback callback) { + std::move(callback).Run(true, true); +} + +content::SpeechRecognitionEventListener* +ElectronSpeechRecognitionManagerDelegate::GetEventListener() { + return this; +} + +bool ElectronSpeechRecognitionManagerDelegate::FilterProfanities( + int render_process_id) { + return false; +} + +} // namespace electron diff --git a/shell/browser/atom_speech_recognition_manager_delegate.h b/shell/browser/electron_speech_recognition_manager_delegate.h similarity index 79% rename from shell/browser/atom_speech_recognition_manager_delegate.h rename to shell/browser/electron_speech_recognition_manager_delegate.h index 6b22b3ae21505..a78ba22dae610 100644 --- a/shell/browser/atom_speech_recognition_manager_delegate.h +++ b/shell/browser/electron_speech_recognition_manager_delegate.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_ -#define SHELL_BROWSER_ATOM_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_ +#ifndef SHELL_BROWSER_ELECTRON_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_ +#define SHELL_BROWSER_ELECTRON_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_ #include #include @@ -14,12 +14,12 @@ namespace electron { -class AtomSpeechRecognitionManagerDelegate +class ElectronSpeechRecognitionManagerDelegate : public content::SpeechRecognitionManagerDelegate, public content::SpeechRecognitionEventListener { public: - AtomSpeechRecognitionManagerDelegate(); - ~AtomSpeechRecognitionManagerDelegate() override; + ElectronSpeechRecognitionManagerDelegate(); + ~ElectronSpeechRecognitionManagerDelegate() override; // content::SpeechRecognitionEventListener: void OnRecognitionStart(int session_id) override; @@ -48,9 +48,9 @@ class AtomSpeechRecognitionManagerDelegate bool FilterProfanities(int render_process_id) override; private: - DISALLOW_COPY_AND_ASSIGN(AtomSpeechRecognitionManagerDelegate); + DISALLOW_COPY_AND_ASSIGN(ElectronSpeechRecognitionManagerDelegate); }; } // namespace electron -#endif // SHELL_BROWSER_ATOM_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_ +#endif // SHELL_BROWSER_ELECTRON_SPEECH_RECOGNITION_MANAGER_DELEGATE_H_ diff --git a/shell/browser/atom_web_ui_controller_factory.cc b/shell/browser/electron_web_ui_controller_factory.cc similarity index 74% rename from shell/browser/atom_web_ui_controller_factory.cc rename to shell/browser/electron_web_ui_controller_factory.cc index bcc475e601e80..d253f064fde0b 100644 --- a/shell/browser/atom_web_ui_controller_factory.cc +++ b/shell/browser/electron_web_ui_controller_factory.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/atom_web_ui_controller_factory.h" +#include "shell/browser/electron_web_ui_controller_factory.h" #include @@ -13,7 +13,7 @@ #include "base/strings/string_util.h" #include "net/base/escape.h" #include "shell/browser/ui/webui/pdf_viewer_ui.h" -#include "shell/common/atom_constants.h" +#include "shell/common/electron_constants.h" #endif // BUILDFLAG(ENABLE_PDF_VIEWER) #include "content/public/browser/web_contents.h" @@ -28,44 +28,45 @@ const char kChromeUIDevToolsBundledHost[] = "devtools"; } // namespace // static -AtomWebUIControllerFactory* AtomWebUIControllerFactory::GetInstance() { - return base::Singleton::get(); +ElectronWebUIControllerFactory* ElectronWebUIControllerFactory::GetInstance() { + return base::Singleton::get(); } -AtomWebUIControllerFactory::AtomWebUIControllerFactory() = default; +ElectronWebUIControllerFactory::ElectronWebUIControllerFactory() = default; -AtomWebUIControllerFactory::~AtomWebUIControllerFactory() = default; +ElectronWebUIControllerFactory::~ElectronWebUIControllerFactory() = default; -content::WebUI::TypeID AtomWebUIControllerFactory::GetWebUIType( +content::WebUI::TypeID ElectronWebUIControllerFactory::GetWebUIType( content::BrowserContext* browser_context, const GURL& url) { #if BUILDFLAG(ENABLE_PDF_VIEWER) if (url.host() == kPdfViewerUIHost) { - return const_cast(this); + return const_cast(this); } #endif // BUILDFLAG(ENABLE_PDF_VIEWER) if (url.host() == kChromeUIDevToolsBundledHost) { - return const_cast(this); + return const_cast(this); } return content::WebUI::kNoWebUI; } -bool AtomWebUIControllerFactory::UseWebUIForURL( +bool ElectronWebUIControllerFactory::UseWebUIForURL( content::BrowserContext* browser_context, const GURL& url) { return GetWebUIType(browser_context, url) != content::WebUI::kNoWebUI; } -bool AtomWebUIControllerFactory::UseWebUIBindingsForURL( +bool ElectronWebUIControllerFactory::UseWebUIBindingsForURL( content::BrowserContext* browser_context, const GURL& url) { return UseWebUIForURL(browser_context, url); } std::unique_ptr -AtomWebUIControllerFactory::CreateWebUIControllerForURL(content::WebUI* web_ui, - const GURL& url) { +ElectronWebUIControllerFactory::CreateWebUIControllerForURL( + content::WebUI* web_ui, + const GURL& url) { #if BUILDFLAG(ENABLE_PDF_VIEWER) if (url.host() == kPdfViewerUIHost) { base::StringPairs toplevel_params; diff --git a/shell/browser/atom_web_ui_controller_factory.h b/shell/browser/electron_web_ui_controller_factory.h similarity index 64% rename from shell/browser/atom_web_ui_controller_factory.h rename to shell/browser/electron_web_ui_controller_factory.h index 6a238595d38d4..287ec5280cfab 100644 --- a/shell/browser/atom_web_ui_controller_factory.h +++ b/shell/browser/electron_web_ui_controller_factory.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_ATOM_WEB_UI_CONTROLLER_FACTORY_H_ -#define SHELL_BROWSER_ATOM_WEB_UI_CONTROLLER_FACTORY_H_ +#ifndef SHELL_BROWSER_ELECTRON_WEB_UI_CONTROLLER_FACTORY_H_ +#define SHELL_BROWSER_ELECTRON_WEB_UI_CONTROLLER_FACTORY_H_ #include @@ -14,12 +14,12 @@ namespace electron { -class AtomWebUIControllerFactory : public content::WebUIControllerFactory { +class ElectronWebUIControllerFactory : public content::WebUIControllerFactory { public: - static AtomWebUIControllerFactory* GetInstance(); + static ElectronWebUIControllerFactory* GetInstance(); - AtomWebUIControllerFactory(); - ~AtomWebUIControllerFactory() override; + ElectronWebUIControllerFactory(); + ~ElectronWebUIControllerFactory() override; // content::WebUIControllerFactory: content::WebUI::TypeID GetWebUIType(content::BrowserContext* browser_context, @@ -33,11 +33,11 @@ class AtomWebUIControllerFactory : public content::WebUIControllerFactory { const GURL& url) override; private: - friend struct base::DefaultSingletonTraits; + friend struct base::DefaultSingletonTraits; - DISALLOW_COPY_AND_ASSIGN(AtomWebUIControllerFactory); + DISALLOW_COPY_AND_ASSIGN(ElectronWebUIControllerFactory); }; } // namespace electron -#endif // SHELL_BROWSER_ATOM_WEB_UI_CONTROLLER_FACTORY_H_ +#endif // SHELL_BROWSER_ELECTRON_WEB_UI_CONTROLLER_FACTORY_H_ diff --git a/shell/browser/extensions/api/runtime/atom_runtime_api_delegate.cc b/shell/browser/extensions/api/runtime/atom_runtime_api_delegate.cc deleted file mode 100644 index 37216dbbe04fa..0000000000000 --- a/shell/browser/extensions/api/runtime/atom_runtime_api_delegate.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2014 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 "shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h" - -#include - -#include "build/build_config.h" -#include "extensions/common/api/runtime.h" -#include "shell/browser/extensions/atom_extension_system.h" - -using extensions::api::runtime::PlatformInfo; - -namespace extensions { - -AtomRuntimeAPIDelegate::AtomRuntimeAPIDelegate( - content::BrowserContext* browser_context) - : browser_context_(browser_context) { - DCHECK(browser_context_); -} - -AtomRuntimeAPIDelegate::~AtomRuntimeAPIDelegate() = default; - -void AtomRuntimeAPIDelegate::AddUpdateObserver(UpdateObserver* observer) {} - -void AtomRuntimeAPIDelegate::RemoveUpdateObserver(UpdateObserver* observer) {} - -void AtomRuntimeAPIDelegate::ReloadExtension(const std::string& extension_id) { - static_cast(ExtensionSystem::Get(browser_context_)) - ->ReloadExtension(extension_id); -} - -bool AtomRuntimeAPIDelegate::CheckForUpdates( - const std::string& extension_id, - const UpdateCheckCallback& callback) { - return false; -} - -void AtomRuntimeAPIDelegate::OpenURL(const GURL& uninstall_url) {} - -bool AtomRuntimeAPIDelegate::GetPlatformInfo(PlatformInfo* info) { - // TODO(nornagon): put useful information here. -#if defined(OS_LINUX) - info->os = api::runtime::PLATFORM_OS_LINUX; -#endif - return true; -} // namespace extensions - -bool AtomRuntimeAPIDelegate::RestartDevice(std::string* error_message) { - *error_message = "Restart is not supported in Electron"; - return false; -} - -} // namespace extensions diff --git a/shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc b/shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc new file mode 100644 index 0000000000000..b375ac69b1be4 --- /dev/null +++ b/shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc @@ -0,0 +1,57 @@ +// Copyright 2014 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 "shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h" + +#include + +#include "build/build_config.h" +#include "extensions/common/api/runtime.h" +#include "shell/browser/extensions/electron_extension_system.h" + +using extensions::api::runtime::PlatformInfo; + +namespace extensions { + +ElectronRuntimeAPIDelegate::ElectronRuntimeAPIDelegate( + content::BrowserContext* browser_context) + : browser_context_(browser_context) { + DCHECK(browser_context_); +} + +ElectronRuntimeAPIDelegate::~ElectronRuntimeAPIDelegate() = default; + +void ElectronRuntimeAPIDelegate::AddUpdateObserver(UpdateObserver* observer) {} + +void ElectronRuntimeAPIDelegate::RemoveUpdateObserver( + UpdateObserver* observer) {} + +void ElectronRuntimeAPIDelegate::ReloadExtension( + const std::string& extension_id) { + static_cast(ExtensionSystem::Get(browser_context_)) + ->ReloadExtension(extension_id); +} + +bool ElectronRuntimeAPIDelegate::CheckForUpdates( + const std::string& extension_id, + const UpdateCheckCallback& callback) { + return false; +} + +void ElectronRuntimeAPIDelegate::OpenURL(const GURL& uninstall_url) {} + +bool ElectronRuntimeAPIDelegate::GetPlatformInfo(PlatformInfo* info) { + // TODO(nornagon): put useful information here. +#if defined(OS_LINUX) + info->os = api::runtime::PLATFORM_OS_LINUX; +#endif + return true; +} // namespace extensions + +bool ElectronRuntimeAPIDelegate::RestartDevice(std::string* error_message) { + *error_message = "Restart is not supported in Electron"; + return false; +} + +} // namespace extensions diff --git a/shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h b/shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h similarity index 67% rename from shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h rename to shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h index d49c468577c76..0491c0ebf3aee 100644 --- a/shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h +++ b/shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_EXTENSIONS_API_RUNTIME_ATOM_RUNTIME_API_DELEGATE_H_ -#define SHELL_BROWSER_EXTENSIONS_API_RUNTIME_ATOM_RUNTIME_API_DELEGATE_H_ +#ifndef SHELL_BROWSER_EXTENSIONS_API_RUNTIME_ELECTRON_RUNTIME_API_DELEGATE_H_ +#define SHELL_BROWSER_EXTENSIONS_API_RUNTIME_ELECTRON_RUNTIME_API_DELEGATE_H_ #include @@ -16,10 +16,10 @@ class BrowserContext; namespace extensions { -class AtomRuntimeAPIDelegate : public RuntimeAPIDelegate { +class ElectronRuntimeAPIDelegate : public RuntimeAPIDelegate { public: - explicit AtomRuntimeAPIDelegate(content::BrowserContext* browser_context); - ~AtomRuntimeAPIDelegate() override; + explicit ElectronRuntimeAPIDelegate(content::BrowserContext* browser_context); + ~ElectronRuntimeAPIDelegate() override; // RuntimeAPIDelegate implementation. void AddUpdateObserver(UpdateObserver* observer) override; @@ -34,9 +34,9 @@ class AtomRuntimeAPIDelegate : public RuntimeAPIDelegate { private: content::BrowserContext* browser_context_; - DISALLOW_COPY_AND_ASSIGN(AtomRuntimeAPIDelegate); + DISALLOW_COPY_AND_ASSIGN(ElectronRuntimeAPIDelegate); }; } // namespace extensions -#endif // SHELL_BROWSER_EXTENSIONS_API_RUNTIME_ATOM_RUNTIME_API_DELEGATE_H_ +#endif // SHELL_BROWSER_EXTENSIONS_API_RUNTIME_ELECTRON_RUNTIME_API_DELEGATE_H_ diff --git a/shell/browser/extensions/atom_display_info_provider.h b/shell/browser/extensions/atom_display_info_provider.h deleted file mode 100644 index 22458339bd798..0000000000000 --- a/shell/browser/extensions/atom_display_info_provider.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2014 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 SHELL_BROWSER_EXTENSIONS_ATOM_DISPLAY_INFO_PROVIDER_H_ -#define SHELL_BROWSER_EXTENSIONS_ATOM_DISPLAY_INFO_PROVIDER_H_ - -#include "base/macros.h" -#include "extensions/browser/api/system_display/display_info_provider.h" - -namespace extensions { - -class AtomDisplayInfoProvider : public DisplayInfoProvider { - public: - AtomDisplayInfoProvider(); - - private: - DISALLOW_COPY_AND_ASSIGN(AtomDisplayInfoProvider); -}; - -} // namespace extensions - -#endif // SHELL_BROWSER_EXTENSIONS_ATOM_DISPLAY_INFO_PROVIDER_H_ diff --git a/shell/browser/extensions/atom_browser_context_keyed_service_factories.cc b/shell/browser/extensions/electron_browser_context_keyed_service_factories.cc similarity index 75% rename from shell/browser/extensions/atom_browser_context_keyed_service_factories.cc rename to shell/browser/extensions/electron_browser_context_keyed_service_factories.cc index 8101a63cf5b5e..eb0a91fc91503 100644 --- a/shell/browser/extensions/atom_browser_context_keyed_service_factories.cc +++ b/shell/browser/extensions/electron_browser_context_keyed_service_factories.cc @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/browser/extensions/atom_browser_context_keyed_service_factories.h" +#include "shell/browser/extensions/electron_browser_context_keyed_service_factories.h" #include "extensions/browser/updater/update_service_factory.h" // #include "extensions/shell/browser/api/identity/identity_api.h" -#include "shell/browser/extensions/atom_extension_system_factory.h" +#include "shell/browser/extensions/electron_extension_system_factory.h" namespace extensions { namespace electron { @@ -18,7 +18,7 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() { // extensions embedders (and namely chrome.) UpdateServiceFactory::GetInstance(); - AtomExtensionSystemFactory::GetInstance(); + ElectronExtensionSystemFactory::GetInstance(); } } // namespace electron diff --git a/shell/browser/extensions/atom_browser_context_keyed_service_factories.h b/shell/browser/extensions/electron_browser_context_keyed_service_factories.h similarity index 62% rename from shell/browser/extensions/atom_browser_context_keyed_service_factories.h rename to shell/browser/extensions/electron_browser_context_keyed_service_factories.h index 536b75c86f0b6..3ac39a587aa2f 100644 --- a/shell/browser/extensions/atom_browser_context_keyed_service_factories.h +++ b/shell/browser/extensions/electron_browser_context_keyed_service_factories.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_ -#define SHELL_BROWSER_EXTENSIONS_ATOM_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_ +#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_ +#define SHELL_BROWSER_EXTENSIONS_ELECTRON_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_ namespace extensions { namespace electron { @@ -15,4 +15,4 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt(); } // namespace electron } // namespace extensions -#endif // SHELL_BROWSER_EXTENSIONS_ATOM_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_ +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_ diff --git a/shell/browser/extensions/atom_display_info_provider.cc b/shell/browser/extensions/electron_display_info_provider.cc similarity index 61% rename from shell/browser/extensions/atom_display_info_provider.cc rename to shell/browser/extensions/electron_display_info_provider.cc index 92567b5680178..4e93c281ab0cd 100644 --- a/shell/browser/extensions/atom_display_info_provider.cc +++ b/shell/browser/extensions/electron_display_info_provider.cc @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/browser/extensions/atom_display_info_provider.h" +#include "shell/browser/extensions/electron_display_info_provider.h" namespace extensions { -AtomDisplayInfoProvider::AtomDisplayInfoProvider() = default; +ElectronDisplayInfoProvider::ElectronDisplayInfoProvider() = default; } // namespace extensions diff --git a/shell/browser/extensions/electron_display_info_provider.h b/shell/browser/extensions/electron_display_info_provider.h new file mode 100644 index 0000000000000..931dab17a29f0 --- /dev/null +++ b/shell/browser/extensions/electron_display_info_provider.h @@ -0,0 +1,23 @@ +// Copyright 2014 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 SHELL_BROWSER_EXTENSIONS_ELECTRON_DISPLAY_INFO_PROVIDER_H_ +#define SHELL_BROWSER_EXTENSIONS_ELECTRON_DISPLAY_INFO_PROVIDER_H_ + +#include "base/macros.h" +#include "extensions/browser/api/system_display/display_info_provider.h" + +namespace extensions { + +class ElectronDisplayInfoProvider : public DisplayInfoProvider { + public: + ElectronDisplayInfoProvider(); + + private: + DISALLOW_COPY_AND_ASSIGN(ElectronDisplayInfoProvider); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_DISPLAY_INFO_PROVIDER_H_ diff --git a/shell/browser/extensions/atom_extension_host_delegate.cc b/shell/browser/extensions/electron_extension_host_delegate.cc similarity index 62% rename from shell/browser/extensions/atom_extension_host_delegate.cc rename to shell/browser/extensions/electron_extension_host_delegate.cc index 2342eb662db44..91e27343c0fb2 100644 --- a/shell/browser/extensions/atom_extension_host_delegate.cc +++ b/shell/browser/extensions/electron_extension_host_delegate.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/browser/extensions/atom_extension_host_delegate.h" +#include "shell/browser/extensions/electron_extension_host_delegate.h" #include #include @@ -11,32 +11,31 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "extensions/browser/media_capture_util.h" -#include "extensions/browser/serial_extension_host_queue.h" -#include "shell/browser/extensions/atom_extension_web_contents_observer.h" +#include "shell/browser/extensions/electron_extension_web_contents_observer.h" namespace extensions { -AtomExtensionHostDelegate::AtomExtensionHostDelegate() {} +ElectronExtensionHostDelegate::ElectronExtensionHostDelegate() {} -AtomExtensionHostDelegate::~AtomExtensionHostDelegate() {} +ElectronExtensionHostDelegate::~ElectronExtensionHostDelegate() {} -void AtomExtensionHostDelegate::OnExtensionHostCreated( +void ElectronExtensionHostDelegate::OnExtensionHostCreated( content::WebContents* web_contents) { - AtomExtensionWebContentsObserver::CreateForWebContents(web_contents); + ElectronExtensionWebContentsObserver::CreateForWebContents(web_contents); } -void AtomExtensionHostDelegate::OnRenderViewCreatedForBackgroundPage( +void ElectronExtensionHostDelegate::OnRenderViewCreatedForBackgroundPage( ExtensionHost* host) {} content::JavaScriptDialogManager* -AtomExtensionHostDelegate::GetJavaScriptDialogManager() { +ElectronExtensionHostDelegate::GetJavaScriptDialogManager() { // TODO(jamescook): Create a JavaScriptDialogManager or reuse the one from // content_shell. NOTREACHED(); return nullptr; } -void AtomExtensionHostDelegate::CreateTab( +void ElectronExtensionHostDelegate::CreateTab( std::unique_ptr web_contents, const std::string& extension_id, WindowOpenDisposition disposition, @@ -46,7 +45,7 @@ void AtomExtensionHostDelegate::CreateTab( NOTREACHED(); } -void AtomExtensionHostDelegate::ProcessMediaAccessRequest( +void ElectronExtensionHostDelegate::ProcessMediaAccessRequest( content::WebContents* web_contents, const content::MediaStreamRequest& request, content::MediaResponseCallback callback, @@ -56,7 +55,7 @@ void AtomExtensionHostDelegate::ProcessMediaAccessRequest( std::move(callback), extension); } -bool AtomExtensionHostDelegate::CheckMediaAccessPermission( +bool ElectronExtensionHostDelegate::CheckMediaAccessPermission( content::RenderFrameHost* render_frame_host, const GURL& security_origin, blink::mojom::MediaStreamType type, @@ -65,15 +64,8 @@ bool AtomExtensionHostDelegate::CheckMediaAccessPermission( return true; } -static base::LazyInstance::DestructorAtExit g_queue = - LAZY_INSTANCE_INITIALIZER; - -ExtensionHostQueue* AtomExtensionHostDelegate::GetExtensionHostQueue() const { - return g_queue.Pointer(); -} - content::PictureInPictureResult -AtomExtensionHostDelegate::EnterPictureInPicture( +ElectronExtensionHostDelegate::EnterPictureInPicture( content::WebContents* web_contents, const viz::SurfaceId& surface_id, const gfx::Size& natural_size) { @@ -81,7 +73,7 @@ AtomExtensionHostDelegate::EnterPictureInPicture( return content::PictureInPictureResult(); } -void AtomExtensionHostDelegate::ExitPictureInPicture() { +void ElectronExtensionHostDelegate::ExitPictureInPicture() { NOTREACHED(); } diff --git a/shell/browser/extensions/atom_extension_host_delegate.h b/shell/browser/extensions/electron_extension_host_delegate.h similarity index 79% rename from shell/browser/extensions/atom_extension_host_delegate.h rename to shell/browser/extensions/electron_extension_host_delegate.h index 689f757ef3d07..7334603c39b2f 100644 --- a/shell/browser/extensions/atom_extension_host_delegate.h +++ b/shell/browser/extensions/electron_extension_host_delegate.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_HOST_DELEGATE_H_ -#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_HOST_DELEGATE_H_ +#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_HOST_DELEGATE_H_ +#define SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_HOST_DELEGATE_H_ #include #include @@ -14,10 +14,10 @@ namespace extensions { // A minimal ExtensionHostDelegate. -class AtomExtensionHostDelegate : public ExtensionHostDelegate { +class ElectronExtensionHostDelegate : public ExtensionHostDelegate { public: - AtomExtensionHostDelegate(); - ~AtomExtensionHostDelegate() override; + ElectronExtensionHostDelegate(); + ~ElectronExtensionHostDelegate() override; // ExtensionHostDelegate implementation. void OnExtensionHostCreated(content::WebContents* web_contents) override; @@ -36,7 +36,6 @@ class AtomExtensionHostDelegate : public ExtensionHostDelegate { const GURL& security_origin, blink::mojom::MediaStreamType type, const Extension* extension) override; - ExtensionHostQueue* GetExtensionHostQueue() const override; content::PictureInPictureResult EnterPictureInPicture( content::WebContents* web_contents, const viz::SurfaceId& surface_id, @@ -44,9 +43,9 @@ class AtomExtensionHostDelegate : public ExtensionHostDelegate { void ExitPictureInPicture() override; private: - DISALLOW_COPY_AND_ASSIGN(AtomExtensionHostDelegate); + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionHostDelegate); }; } // namespace extensions -#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_HOST_DELEGATE_H_ +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_HOST_DELEGATE_H_ diff --git a/shell/browser/extensions/atom_extension_loader.cc b/shell/browser/extensions/electron_extension_loader.cc similarity index 82% rename from shell/browser/extensions/atom_extension_loader.cc rename to shell/browser/extensions/electron_extension_loader.cc index 9bacce1639fbe..8d2fba921d11b 100644 --- a/shell/browser/extensions/atom_extension_loader.cc +++ b/shell/browser/extensions/electron_extension_loader.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/browser/extensions/atom_extension_loader.h" +#include "shell/browser/extensions/electron_extension_loader.h" #include "base/auto_reset.h" #include "base/bind.h" @@ -57,15 +57,15 @@ scoped_refptr LoadUnpacked( } // namespace -AtomExtensionLoader::AtomExtensionLoader( +ElectronExtensionLoader::ElectronExtensionLoader( content::BrowserContext* browser_context) : browser_context_(browser_context), extension_registrar_(browser_context, this), weak_factory_(this) {} -AtomExtensionLoader::~AtomExtensionLoader() = default; +ElectronExtensionLoader::~ElectronExtensionLoader() = default; -const Extension* AtomExtensionLoader::LoadExtension( +const Extension* ElectronExtensionLoader::LoadExtension( const base::FilePath& extension_dir) { // TODO(nornagon): load extensions asynchronously on // GetExtensionFileTaskRunner() @@ -77,7 +77,7 @@ const Extension* AtomExtensionLoader::LoadExtension( return extension.get(); } -void AtomExtensionLoader::ReloadExtension(ExtensionId extension_id) { +void ElectronExtensionLoader::ReloadExtension(ExtensionId extension_id) { const Extension* extension = ExtensionRegistry::Get(browser_context_) ->GetInstalledExtension(extension_id); // We shouldn't be trying to reload extensions that haven't been added. @@ -93,7 +93,7 @@ void AtomExtensionLoader::ReloadExtension(ExtensionId extension_id) { return; } -void AtomExtensionLoader::FinishExtensionReload( +void ElectronExtensionLoader::FinishExtensionReload( const ExtensionId old_extension_id, scoped_refptr extension) { if (extension) { @@ -101,8 +101,8 @@ void AtomExtensionLoader::FinishExtensionReload( } } -void AtomExtensionLoader::PreAddExtension(const Extension* extension, - const Extension* old_extension) { +void ElectronExtensionLoader::PreAddExtension(const Extension* extension, + const Extension* old_extension) { if (old_extension) return; @@ -122,13 +122,13 @@ void AtomExtensionLoader::PreAddExtension(const Extension* extension, } } -void AtomExtensionLoader::PostActivateExtension( +void ElectronExtensionLoader::PostActivateExtension( scoped_refptr extension) {} -void AtomExtensionLoader::PostDeactivateExtension( +void ElectronExtensionLoader::PostDeactivateExtension( scoped_refptr extension) {} -void AtomExtensionLoader::LoadExtensionForReload( +void ElectronExtensionLoader::LoadExtensionForReload( const ExtensionId& extension_id, const base::FilePath& path, LoadErrorBehavior load_error_behavior) { @@ -137,21 +137,21 @@ void AtomExtensionLoader::LoadExtensionForReload( base::PostTaskAndReplyWithResult( GetExtensionFileTaskRunner().get(), FROM_HERE, base::BindOnce(&LoadUnpacked, path), - base::BindOnce(&AtomExtensionLoader::FinishExtensionReload, + base::BindOnce(&ElectronExtensionLoader::FinishExtensionReload, weak_factory_.GetWeakPtr(), extension_id)); did_schedule_reload_ = true; } -bool AtomExtensionLoader::CanEnableExtension(const Extension* extension) { +bool ElectronExtensionLoader::CanEnableExtension(const Extension* extension) { return true; } -bool AtomExtensionLoader::CanDisableExtension(const Extension* extension) { +bool ElectronExtensionLoader::CanDisableExtension(const Extension* extension) { // Extensions cannot be disabled by the user. return false; } -bool AtomExtensionLoader::ShouldBlockExtension(const Extension* extension) { +bool ElectronExtensionLoader::ShouldBlockExtension(const Extension* extension) { return false; } diff --git a/shell/browser/extensions/atom_extension_loader.h b/shell/browser/extensions/electron_extension_loader.h similarity index 84% rename from shell/browser/extensions/atom_extension_loader.h rename to shell/browser/extensions/electron_extension_loader.h index 9b0f431389d0e..cf8173b25e016 100644 --- a/shell/browser/extensions/atom_extension_loader.h +++ b/shell/browser/extensions/electron_extension_loader.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_LOADER_H_ -#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_LOADER_H_ +#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_LOADER_H_ +#define SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_LOADER_H_ #include #include @@ -27,10 +27,10 @@ namespace extensions { class Extension; // Handles extension loading and reloading using ExtensionRegistrar. -class AtomExtensionLoader : public ExtensionRegistrar::Delegate { +class ElectronExtensionLoader : public ExtensionRegistrar::Delegate { public: - explicit AtomExtensionLoader(content::BrowserContext* browser_context); - ~AtomExtensionLoader() override; + explicit ElectronExtensionLoader(content::BrowserContext* browser_context); + ~ElectronExtensionLoader() override; // Loads an unpacked extension from a directory synchronously. Returns the // extension on success, or nullptr otherwise. @@ -76,11 +76,11 @@ class AtomExtensionLoader : public ExtensionRegistrar::Delegate { // LoadExtensionForReload(). bool did_schedule_reload_ = false; - base::WeakPtrFactory weak_factory_; + base::WeakPtrFactory weak_factory_; - DISALLOW_COPY_AND_ASSIGN(AtomExtensionLoader); + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionLoader); }; } // namespace extensions -#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_LOADER_H_ +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_LOADER_H_ diff --git a/shell/browser/extensions/atom_extension_system.cc b/shell/browser/extensions/electron_extension_system.cc similarity index 66% rename from shell/browser/extensions/atom_extension_system.cc rename to shell/browser/extensions/electron_extension_system.cc index 7d30806fee88b..86534214d6977 100644 --- a/shell/browser/extensions/atom_extension_system.cc +++ b/shell/browser/extensions/electron_extension_system.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/browser/extensions/atom_extension_system.h" +#include "shell/browser/extensions/electron_extension_system.h" #include #include @@ -29,31 +29,33 @@ #include "extensions/browser/value_store/value_store_factory_impl.h" #include "extensions/common/constants.h" #include "extensions/common/file_util.h" -#include "shell/browser/extensions/atom_extension_loader.h" +#include "shell/browser/extensions/electron_extension_loader.h" using content::BrowserContext; using content::BrowserThread; namespace extensions { -AtomExtensionSystem::AtomExtensionSystem(BrowserContext* browser_context) +ElectronExtensionSystem::ElectronExtensionSystem( + BrowserContext* browser_context) : browser_context_(browser_context), store_factory_(new ValueStoreFactoryImpl(browser_context->GetPath())), weak_factory_(this) {} -AtomExtensionSystem::~AtomExtensionSystem() = default; +ElectronExtensionSystem::~ElectronExtensionSystem() = default; -const Extension* AtomExtensionSystem::LoadExtension( +const Extension* ElectronExtensionSystem::LoadExtension( const base::FilePath& extension_dir) { return extension_loader_->LoadExtension(extension_dir); } -const Extension* AtomExtensionSystem::LoadApp(const base::FilePath& app_dir) { +const Extension* ElectronExtensionSystem::LoadApp( + const base::FilePath& app_dir) { CHECK(false); // Should never call LoadApp return nullptr; } -void AtomExtensionSystem::FinishInitialization() { +void ElectronExtensionSystem::FinishInitialization() { // Inform the rest of the extensions system to start. ready_.Signal(); content::NotificationService::current()->Notify( @@ -62,15 +64,15 @@ void AtomExtensionSystem::FinishInitialization() { content::NotificationService::NoDetails()); } -void AtomExtensionSystem::ReloadExtension(const ExtensionId& extension_id) { +void ElectronExtensionSystem::ReloadExtension(const ExtensionId& extension_id) { extension_loader_->ReloadExtension(extension_id); } -void AtomExtensionSystem::Shutdown() { +void ElectronExtensionSystem::Shutdown() { extension_loader_.reset(); } -void AtomExtensionSystem::InitForRegularProfile(bool extensions_enabled) { +void ElectronExtensionSystem::InitForRegularProfile(bool extensions_enabled) { service_worker_manager_ = std::make_unique(browser_context_); runtime_data_ = @@ -79,56 +81,57 @@ void AtomExtensionSystem::InitForRegularProfile(bool extensions_enabled) { shared_user_script_master_ = std::make_unique(browser_context_); app_sorting_ = std::make_unique(); - extension_loader_ = std::make_unique(browser_context_); + extension_loader_ = + std::make_unique(browser_context_); } -ExtensionService* AtomExtensionSystem::extension_service() { +ExtensionService* ElectronExtensionSystem::extension_service() { return nullptr; } -RuntimeData* AtomExtensionSystem::runtime_data() { +RuntimeData* ElectronExtensionSystem::runtime_data() { return runtime_data_.get(); } -ManagementPolicy* AtomExtensionSystem::management_policy() { +ManagementPolicy* ElectronExtensionSystem::management_policy() { return nullptr; } -ServiceWorkerManager* AtomExtensionSystem::service_worker_manager() { +ServiceWorkerManager* ElectronExtensionSystem::service_worker_manager() { return service_worker_manager_.get(); } -SharedUserScriptMaster* AtomExtensionSystem::shared_user_script_master() { +SharedUserScriptMaster* ElectronExtensionSystem::shared_user_script_master() { return new SharedUserScriptMaster(browser_context_); } -StateStore* AtomExtensionSystem::state_store() { +StateStore* ElectronExtensionSystem::state_store() { return nullptr; } -StateStore* AtomExtensionSystem::rules_store() { +StateStore* ElectronExtensionSystem::rules_store() { return nullptr; } -scoped_refptr AtomExtensionSystem::store_factory() { +scoped_refptr ElectronExtensionSystem::store_factory() { return store_factory_; } -InfoMap* AtomExtensionSystem::info_map() { +InfoMap* ElectronExtensionSystem::info_map() { if (!info_map_.get()) info_map_ = new InfoMap; return info_map_.get(); } -QuotaService* AtomExtensionSystem::quota_service() { +QuotaService* ElectronExtensionSystem::quota_service() { return quota_service_.get(); } -AppSorting* AtomExtensionSystem::app_sorting() { +AppSorting* ElectronExtensionSystem::app_sorting() { return app_sorting_.get(); } -void AtomExtensionSystem::RegisterExtensionWithRequestContexts( +void ElectronExtensionSystem::RegisterExtensionWithRequestContexts( const Extension* extension, const base::Closure& callback) { base::PostTaskAndReply( @@ -138,24 +141,24 @@ void AtomExtensionSystem::RegisterExtensionWithRequestContexts( callback); } -void AtomExtensionSystem::UnregisterExtensionWithRequestContexts( +void ElectronExtensionSystem::UnregisterExtensionWithRequestContexts( const std::string& extension_id, const UnloadedExtensionReason reason) {} -const base::OneShotEvent& AtomExtensionSystem::ready() const { +const base::OneShotEvent& ElectronExtensionSystem::ready() const { return ready_; } -ContentVerifier* AtomExtensionSystem::content_verifier() { +ContentVerifier* ElectronExtensionSystem::content_verifier() { return nullptr; } -std::unique_ptr AtomExtensionSystem::GetDependentExtensions( +std::unique_ptr ElectronExtensionSystem::GetDependentExtensions( const Extension* extension) { return std::make_unique(); } -void AtomExtensionSystem::InstallUpdate( +void ElectronExtensionSystem::InstallUpdate( const std::string& extension_id, const std::string& public_key, const base::FilePath& temp_dir, @@ -165,14 +168,14 @@ void AtomExtensionSystem::InstallUpdate( base::DeleteFile(temp_dir, true /* recursive */); } -bool AtomExtensionSystem::FinishDelayedInstallationIfReady( +bool ElectronExtensionSystem::FinishDelayedInstallationIfReady( const std::string& extension_id, bool install_immediately) { NOTREACHED(); return false; } -void AtomExtensionSystem::OnExtensionRegisteredWithRequestContexts( +void ElectronExtensionSystem::OnExtensionRegisteredWithRequestContexts( scoped_refptr extension) { ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_); registry->AddReady(extension); diff --git a/shell/browser/extensions/atom_extension_system.h b/shell/browser/extensions/electron_extension_system.h similarity index 86% rename from shell/browser/extensions/atom_extension_system.h rename to shell/browser/extensions/electron_extension_system.h index ed78e18d182c3..6323a1d74dc57 100644 --- a/shell/browser/extensions/atom_extension_system.h +++ b/shell/browser/extensions/electron_extension_system.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_H_ -#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_H_ +#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_SYSTEM_H_ +#define SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_SYSTEM_H_ #include #include @@ -26,16 +26,16 @@ class BrowserContext; namespace extensions { -class AtomExtensionLoader; +class ElectronExtensionLoader; class ValueStoreFactory; // A simplified version of ExtensionSystem for app_shell. Allows // app_shell to skip initialization of services it doesn't need. -class AtomExtensionSystem : public ExtensionSystem { +class ElectronExtensionSystem : public ExtensionSystem { public: using InstallUpdateCallback = ExtensionSystem::InstallUpdateCallback; - explicit AtomExtensionSystem(content::BrowserContext* browser_context); - ~AtomExtensionSystem() override; + explicit ElectronExtensionSystem(content::BrowserContext* browser_context); + ~ElectronExtensionSystem() override; // Loads an unpacked extension from a directory. Returns the extension on // success, or nullptr otherwise. @@ -101,18 +101,18 @@ class AtomExtensionSystem : public ExtensionSystem { std::unique_ptr shared_user_script_master_; std::unique_ptr app_sorting_; - std::unique_ptr extension_loader_; + std::unique_ptr extension_loader_; scoped_refptr store_factory_; // Signaled when the extension system has completed its startup tasks. base::OneShotEvent ready_; - base::WeakPtrFactory weak_factory_; + base::WeakPtrFactory weak_factory_; - DISALLOW_COPY_AND_ASSIGN(AtomExtensionSystem); + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionSystem); }; } // namespace extensions -#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_H_ +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_SYSTEM_H_ diff --git a/shell/browser/extensions/atom_extension_system_factory.cc b/shell/browser/extensions/electron_extension_system_factory.cc similarity index 51% rename from shell/browser/extensions/atom_extension_system_factory.cc rename to shell/browser/extensions/electron_extension_system_factory.cc index cdc7d1ec8d653..b284f8f21b442 100644 --- a/shell/browser/extensions/atom_extension_system_factory.cc +++ b/shell/browser/extensions/electron_extension_system_factory.cc @@ -2,49 +2,50 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/browser/extensions/atom_extension_system_factory.h" +#include "shell/browser/extensions/electron_extension_system_factory.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "extensions/browser/extension_prefs_factory.h" #include "extensions/browser/extension_registry_factory.h" -#include "shell/browser/extensions/atom_extension_system.h" +#include "shell/browser/extensions/electron_extension_system.h" using content::BrowserContext; namespace extensions { -ExtensionSystem* AtomExtensionSystemFactory::GetForBrowserContext( +ExtensionSystem* ElectronExtensionSystemFactory::GetForBrowserContext( BrowserContext* context) { - return static_cast( + return static_cast( GetInstance()->GetServiceForBrowserContext(context, true)); } // static -AtomExtensionSystemFactory* AtomExtensionSystemFactory::GetInstance() { - return base::Singleton::get(); +ElectronExtensionSystemFactory* ElectronExtensionSystemFactory::GetInstance() { + return base::Singleton::get(); } -AtomExtensionSystemFactory::AtomExtensionSystemFactory() - : ExtensionSystemProvider("AtomExtensionSystem", +ElectronExtensionSystemFactory::ElectronExtensionSystemFactory() + : ExtensionSystemProvider("ElectronExtensionSystem", BrowserContextDependencyManager::GetInstance()) { DependsOn(ExtensionPrefsFactory::GetInstance()); DependsOn(ExtensionRegistryFactory::GetInstance()); } -AtomExtensionSystemFactory::~AtomExtensionSystemFactory() {} +ElectronExtensionSystemFactory::~ElectronExtensionSystemFactory() {} -KeyedService* AtomExtensionSystemFactory::BuildServiceInstanceFor( +KeyedService* ElectronExtensionSystemFactory::BuildServiceInstanceFor( BrowserContext* context) const { - return new AtomExtensionSystem(context); + return new ElectronExtensionSystem(context); } -BrowserContext* AtomExtensionSystemFactory::GetBrowserContextToUse( +BrowserContext* ElectronExtensionSystemFactory::GetBrowserContextToUse( BrowserContext* context) const { // Use a separate instance for incognito. return context; } -bool AtomExtensionSystemFactory::ServiceIsCreatedWithBrowserContext() const { +bool ElectronExtensionSystemFactory::ServiceIsCreatedWithBrowserContext() + const { return true; } diff --git a/shell/browser/extensions/atom_extension_system_factory.h b/shell/browser/extensions/electron_extension_system_factory.h similarity index 57% rename from shell/browser/extensions/atom_extension_system_factory.h rename to shell/browser/extensions/electron_extension_system_factory.h index 6ec20f45c3015..e6e761c144233 100644 --- a/shell/browser/extensions/atom_extension_system_factory.h +++ b/shell/browser/extensions/electron_extension_system_factory.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_FACTORY_H_ -#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_FACTORY_H_ +#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_SYSTEM_FACTORY_H_ +#define SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_SYSTEM_FACTORY_H_ #include "base/macros.h" #include "base/memory/singleton.h" @@ -11,20 +11,20 @@ namespace extensions { -// A factory that provides AtomExtensionSystem. -class AtomExtensionSystemFactory : public ExtensionSystemProvider { +// A factory that provides ElectronExtensionSystem. +class ElectronExtensionSystemFactory : public ExtensionSystemProvider { public: // ExtensionSystemProvider implementation: ExtensionSystem* GetForBrowserContext( content::BrowserContext* context) override; - static AtomExtensionSystemFactory* GetInstance(); + static ElectronExtensionSystemFactory* GetInstance(); private: - friend struct base::DefaultSingletonTraits; + friend struct base::DefaultSingletonTraits; - AtomExtensionSystemFactory(); - ~AtomExtensionSystemFactory() override; + ElectronExtensionSystemFactory(); + ~ElectronExtensionSystemFactory() override; // BrowserContextKeyedServiceFactory implementation: KeyedService* BuildServiceInstanceFor( @@ -33,9 +33,9 @@ class AtomExtensionSystemFactory : public ExtensionSystemProvider { content::BrowserContext* context) const override; bool ServiceIsCreatedWithBrowserContext() const override; - DISALLOW_COPY_AND_ASSIGN(AtomExtensionSystemFactory); + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionSystemFactory); }; } // namespace extensions -#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_FACTORY_H_ +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_SYSTEM_FACTORY_H_ diff --git a/shell/browser/extensions/atom_extension_web_contents_observer.cc b/shell/browser/extensions/electron_extension_web_contents_observer.cc similarity index 51% rename from shell/browser/extensions/atom_extension_web_contents_observer.cc rename to shell/browser/extensions/electron_extension_web_contents_observer.cc index 4beb9030375e5..4d7a2cebd5c70 100644 --- a/shell/browser/extensions/atom_extension_web_contents_observer.cc +++ b/shell/browser/extensions/electron_extension_web_contents_observer.cc @@ -2,25 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/browser/extensions/atom_extension_web_contents_observer.h" +#include "shell/browser/extensions/electron_extension_web_contents_observer.h" namespace extensions { -AtomExtensionWebContentsObserver::AtomExtensionWebContentsObserver( +ElectronExtensionWebContentsObserver::ElectronExtensionWebContentsObserver( content::WebContents* web_contents) : ExtensionWebContentsObserver(web_contents) {} -AtomExtensionWebContentsObserver::~AtomExtensionWebContentsObserver() {} +ElectronExtensionWebContentsObserver::~ElectronExtensionWebContentsObserver() {} -void AtomExtensionWebContentsObserver::CreateForWebContents( +void ElectronExtensionWebContentsObserver::CreateForWebContents( content::WebContents* web_contents) { content::WebContentsUserData< - AtomExtensionWebContentsObserver>::CreateForWebContents(web_contents); + ElectronExtensionWebContentsObserver>::CreateForWebContents(web_contents); // Initialize this instance if necessary. FromWebContents(web_contents)->Initialize(); } -WEB_CONTENTS_USER_DATA_KEY_IMPL(AtomExtensionWebContentsObserver) +WEB_CONTENTS_USER_DATA_KEY_IMPL(ElectronExtensionWebContentsObserver) } // namespace extensions diff --git a/shell/browser/extensions/atom_extension_web_contents_observer.h b/shell/browser/extensions/electron_extension_web_contents_observer.h similarity index 51% rename from shell/browser/extensions/atom_extension_web_contents_observer.h rename to shell/browser/extensions/electron_extension_web_contents_observer.h index 75c4fc72853b7..577c3c3b19291 100644 --- a/shell/browser/extensions/atom_extension_web_contents_observer.h +++ b/shell/browser/extensions/electron_extension_web_contents_observer.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_WEB_CONTENTS_OBSERVER_H_ -#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_WEB_CONTENTS_OBSERVER_H_ +#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_WEB_CONTENTS_OBSERVER_H_ +#define SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_WEB_CONTENTS_OBSERVER_H_ #include "base/macros.h" #include "content/public/browser/web_contents_user_data.h" @@ -12,26 +12,29 @@ namespace extensions { // The app_shell version of ExtensionWebContentsObserver. -class AtomExtensionWebContentsObserver +class ElectronExtensionWebContentsObserver : public ExtensionWebContentsObserver, - public content::WebContentsUserData { + public content::WebContentsUserData< + ElectronExtensionWebContentsObserver> { public: - ~AtomExtensionWebContentsObserver() override; + ~ElectronExtensionWebContentsObserver() override; // Creates and initializes an instance of this class for the given // |web_contents|, if it doesn't already exist. static void CreateForWebContents(content::WebContents* web_contents); private: - friend class content::WebContentsUserData; + friend class content::WebContentsUserData< + ElectronExtensionWebContentsObserver>; - explicit AtomExtensionWebContentsObserver(content::WebContents* web_contents); + explicit ElectronExtensionWebContentsObserver( + content::WebContents* web_contents); WEB_CONTENTS_USER_DATA_KEY_DECL(); - DISALLOW_COPY_AND_ASSIGN(AtomExtensionWebContentsObserver); + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionWebContentsObserver); }; } // namespace extensions -#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_WEB_CONTENTS_OBSERVER_H_ +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_WEB_CONTENTS_OBSERVER_H_ diff --git a/shell/browser/extensions/atom_extensions_browser_client.cc b/shell/browser/extensions/electron_extensions_browser_client.cc similarity index 55% rename from shell/browser/extensions/atom_extensions_browser_client.cc rename to shell/browser/extensions/electron_extensions_browser_client.cc index da7bc02a3df9b..b67f702d00852 100644 --- a/shell/browser/extensions/atom_extensions_browser_client.cc +++ b/shell/browser/extensions/electron_extensions_browser_client.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/browser/extensions/atom_extensions_browser_client.h" +#include "shell/browser/extensions/electron_extensions_browser_client.h" #include @@ -25,26 +25,26 @@ #include "extensions/browser/updater/null_extension_cache.h" #include "extensions/browser/url_request_util.h" #include "extensions/common/features/feature_channel.h" -#include "shell/browser/atom_browser_client.h" -#include "shell/browser/atom_browser_context.h" #include "shell/browser/browser.h" -#include "shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h" -#include "shell/browser/extensions/atom_extension_host_delegate.h" -#include "shell/browser/extensions/atom_extension_system_factory.h" -#include "shell/browser/extensions/atom_extension_web_contents_observer.h" +#include "shell/browser/electron_browser_client.h" +#include "shell/browser/electron_browser_context.h" +#include "shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h" +#include "shell/browser/extensions/electron_extension_host_delegate.h" +#include "shell/browser/extensions/electron_extension_system_factory.h" +#include "shell/browser/extensions/electron_extension_web_contents_observer.h" // #include "shell/browser/extensions/atom_extensions_api_client.h" // #include "shell/browser/extensions/atom_extensions_browser_api_provider.h" #include "services/network/public/mojom/url_loader.mojom.h" -#include "shell/browser/extensions/atom_navigation_ui_data.h" +#include "shell/browser/extensions/electron_navigation_ui_data.h" using content::BrowserContext; using content::BrowserThread; namespace electron { -AtomExtensionsBrowserClient::AtomExtensionsBrowserClient() +ElectronExtensionsBrowserClient::ElectronExtensionsBrowserClient() : api_client_(new extensions::ExtensionsAPIClient), - // : api_client_(new extensions::AtomExtensionsAPIClient), + // : api_client_(new extensions::ElectronExtensionsAPIClient), extension_cache_(new extensions::NullExtensionCache()) { // app_shell does not have a concept of channel yet, so leave UNKNOWN to // enable all channel-dependent extension APIs. @@ -52,23 +52,23 @@ AtomExtensionsBrowserClient::AtomExtensionsBrowserClient() AddAPIProvider( std::make_unique()); - // AddAPIProvider(std::make_unique()); + // AddAPIProvider(std::make_unique()); } -AtomExtensionsBrowserClient::~AtomExtensionsBrowserClient() {} +ElectronExtensionsBrowserClient::~ElectronExtensionsBrowserClient() {} -bool AtomExtensionsBrowserClient::IsShuttingDown() { +bool ElectronExtensionsBrowserClient::IsShuttingDown() { return electron::Browser::Get()->is_shutting_down(); } -bool AtomExtensionsBrowserClient::AreExtensionsDisabled( +bool ElectronExtensionsBrowserClient::AreExtensionsDisabled( const base::CommandLine& command_line, BrowserContext* context) { return false; } -bool AtomExtensionsBrowserClient::IsValidContext(BrowserContext* context) { - auto context_map = AtomBrowserContext::browser_context_map(); +bool ElectronExtensionsBrowserClient::IsValidContext(BrowserContext* context) { + auto context_map = ElectronBrowserContext::browser_context_map(); for (auto const& entry : context_map) { if (entry.second && entry.second.get() == context) return true; @@ -76,50 +76,50 @@ bool AtomExtensionsBrowserClient::IsValidContext(BrowserContext* context) { return false; } -bool AtomExtensionsBrowserClient::IsSameContext(BrowserContext* first, - BrowserContext* second) { +bool ElectronExtensionsBrowserClient::IsSameContext(BrowserContext* first, + BrowserContext* second) { return first == second; } -bool AtomExtensionsBrowserClient::HasOffTheRecordContext( +bool ElectronExtensionsBrowserClient::HasOffTheRecordContext( BrowserContext* context) { return false; } -BrowserContext* AtomExtensionsBrowserClient::GetOffTheRecordContext( +BrowserContext* ElectronExtensionsBrowserClient::GetOffTheRecordContext( BrowserContext* context) { // app_shell only supports a single context. return nullptr; } -BrowserContext* AtomExtensionsBrowserClient::GetOriginalContext( +BrowserContext* ElectronExtensionsBrowserClient::GetOriginalContext( BrowserContext* context) { DCHECK(context); if (context->IsOffTheRecord()) { - return AtomBrowserContext::From("", false).get(); + return ElectronBrowserContext::From("", false).get(); } else { return context; } } -bool AtomExtensionsBrowserClient::IsGuestSession( +bool ElectronExtensionsBrowserClient::IsGuestSession( BrowserContext* context) const { return false; } -bool AtomExtensionsBrowserClient::IsExtensionIncognitoEnabled( +bool ElectronExtensionsBrowserClient::IsExtensionIncognitoEnabled( const std::string& extension_id, content::BrowserContext* context) const { return false; } -bool AtomExtensionsBrowserClient::CanExtensionCrossIncognito( +bool ElectronExtensionsBrowserClient::CanExtensionCrossIncognito( const extensions::Extension* extension, content::BrowserContext* context) const { return false; } -base::FilePath AtomExtensionsBrowserClient::GetBundleResourcePath( +base::FilePath ElectronExtensionsBrowserClient::GetBundleResourcePath( const network::ResourceRequest& request, const base::FilePath& extension_resources_path, int* resource_id) const { @@ -127,18 +127,18 @@ base::FilePath AtomExtensionsBrowserClient::GetBundleResourcePath( return base::FilePath(); } -void AtomExtensionsBrowserClient::LoadResourceFromResourceBundle( +void ElectronExtensionsBrowserClient::LoadResourceFromResourceBundle( const network::ResourceRequest& request, - network::mojom::URLLoaderRequest loader, + mojo::PendingReceiver loader, const base::FilePath& resource_relative_path, int resource_id, const std::string& content_security_policy, - network::mojom::URLLoaderClientPtr client, + mojo::PendingRemote client, bool send_cors_header) { NOTREACHED() << "Load resources from bundles not supported."; } -bool AtomExtensionsBrowserClient::AllowCrossRendererResourceLoad( +bool ElectronExtensionsBrowserClient::AllowCrossRendererResourceLoad( const GURL& url, content::ResourceType resource_type, ui::PageTransition page_transition, @@ -158,60 +158,62 @@ bool AtomExtensionsBrowserClient::AllowCrossRendererResourceLoad( return false; } -PrefService* AtomExtensionsBrowserClient::GetPrefServiceForContext( +PrefService* ElectronExtensionsBrowserClient::GetPrefServiceForContext( BrowserContext* context) { - return static_cast(context)->prefs(); + return static_cast(context)->prefs(); } -void AtomExtensionsBrowserClient::GetEarlyExtensionPrefsObservers( +void ElectronExtensionsBrowserClient::GetEarlyExtensionPrefsObservers( content::BrowserContext* context, std::vector* observers) const {} extensions::ProcessManagerDelegate* -AtomExtensionsBrowserClient::GetProcessManagerDelegate() const { +ElectronExtensionsBrowserClient::GetProcessManagerDelegate() const { return NULL; } -std::unique_ptr AtomExtensionsBrowserClient:: +std::unique_ptr +ElectronExtensionsBrowserClient:: CreateExtensionHostDelegate() { // TODO(samuelmaddock): - return base::WrapUnique(new extensions::AtomExtensionHostDelegate); + return base::WrapUnique(new extensions::ElectronExtensionHostDelegate); } -bool AtomExtensionsBrowserClient::DidVersionUpdate(BrowserContext* context) { +bool ElectronExtensionsBrowserClient::DidVersionUpdate( + BrowserContext* context) { // TODO(jamescook): We might want to tell extensions when app_shell updates. return false; } -void AtomExtensionsBrowserClient::PermitExternalProtocolHandler() {} +void ElectronExtensionsBrowserClient::PermitExternalProtocolHandler() {} -bool AtomExtensionsBrowserClient::IsInDemoMode() { +bool ElectronExtensionsBrowserClient::IsInDemoMode() { return false; } -bool AtomExtensionsBrowserClient::IsScreensaverInDemoMode( +bool ElectronExtensionsBrowserClient::IsScreensaverInDemoMode( const std::string& app_id) { return false; } -bool AtomExtensionsBrowserClient::IsRunningInForcedAppMode() { +bool ElectronExtensionsBrowserClient::IsRunningInForcedAppMode() { return false; } -bool AtomExtensionsBrowserClient::IsAppModeForcedForApp( +bool ElectronExtensionsBrowserClient::IsAppModeForcedForApp( const extensions::ExtensionId& extension_id) { return false; } -bool AtomExtensionsBrowserClient::IsLoggedInAsPublicAccount() { +bool ElectronExtensionsBrowserClient::IsLoggedInAsPublicAccount() { return false; } extensions::ExtensionSystemProvider* -AtomExtensionsBrowserClient::GetExtensionSystemFactory() { - return extensions::AtomExtensionSystemFactory::GetInstance(); +ElectronExtensionsBrowserClient::GetExtensionSystemFactory() { + return extensions::ElectronExtensionSystemFactory::GetInstance(); } -void AtomExtensionsBrowserClient::RegisterExtensionInterfaces( +void ElectronExtensionsBrowserClient::RegisterExtensionInterfaces( service_manager::BinderRegistryWithArgs* registry, content::RenderFrameHost* render_frame_host, @@ -220,32 +222,33 @@ void AtomExtensionsBrowserClient::RegisterExtensionInterfaces( } std::unique_ptr -AtomExtensionsBrowserClient::CreateRuntimeAPIDelegate( +ElectronExtensionsBrowserClient::CreateRuntimeAPIDelegate( content::BrowserContext* context) const { - return std::make_unique(context); + return std::make_unique(context); } const extensions::ComponentExtensionResourceManager* -AtomExtensionsBrowserClient::GetComponentExtensionResourceManager() { +ElectronExtensionsBrowserClient::GetComponentExtensionResourceManager() { return NULL; } -void AtomExtensionsBrowserClient::BroadcastEventToRenderers( +void ElectronExtensionsBrowserClient::BroadcastEventToRenderers( extensions::events::HistogramValue histogram_value, const std::string& event_name, std::unique_ptr args) { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { base::PostTask( FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&AtomExtensionsBrowserClient::BroadcastEventToRenderers, - base::Unretained(this), histogram_value, event_name, - std::move(args))); + base::BindOnce( + &ElectronExtensionsBrowserClient::BroadcastEventToRenderers, + base::Unretained(this), histogram_value, event_name, + std::move(args))); return; } std::unique_ptr event( new extensions::Event(histogram_value, event_name, std::move(args))); - auto context_map = AtomBrowserContext::browser_context_map(); + auto context_map = ElectronBrowserContext::browser_context_map(); for (auto const& entry : context_map) { if (entry.second) { extensions::EventRouter::Get(entry.second.get()) @@ -254,46 +257,52 @@ void AtomExtensionsBrowserClient::BroadcastEventToRenderers( } } -extensions::ExtensionCache* AtomExtensionsBrowserClient::GetExtensionCache() { +extensions::ExtensionCache* +ElectronExtensionsBrowserClient::GetExtensionCache() { return extension_cache_.get(); } -bool AtomExtensionsBrowserClient::IsBackgroundUpdateAllowed() { +bool ElectronExtensionsBrowserClient::IsBackgroundUpdateAllowed() { return true; } -bool AtomExtensionsBrowserClient::IsMinBrowserVersionSupported( +bool ElectronExtensionsBrowserClient::IsMinBrowserVersionSupported( const std::string& min_version) { return true; } -void AtomExtensionsBrowserClient::SetAPIClientForTest( +void ElectronExtensionsBrowserClient::SetAPIClientForTest( extensions::ExtensionsAPIClient* api_client) { api_client_.reset(api_client); } extensions::ExtensionWebContentsObserver* -AtomExtensionsBrowserClient::GetExtensionWebContentsObserver( +ElectronExtensionsBrowserClient::GetExtensionWebContentsObserver( content::WebContents* web_contents) { - return extensions::AtomExtensionWebContentsObserver::FromWebContents( + return extensions::ElectronExtensionWebContentsObserver::FromWebContents( web_contents); } -extensions::KioskDelegate* AtomExtensionsBrowserClient::GetKioskDelegate() { +extensions::KioskDelegate* ElectronExtensionsBrowserClient::GetKioskDelegate() { return nullptr; } -bool AtomExtensionsBrowserClient::IsLockScreenContext( +bool ElectronExtensionsBrowserClient::IsLockScreenContext( content::BrowserContext* context) { return false; } -std::string AtomExtensionsBrowserClient::GetApplicationLocale() { - return AtomBrowserClient::Get()->GetApplicationLocale(); +std::string ElectronExtensionsBrowserClient::GetApplicationLocale() { + return ElectronBrowserClient::Get()->GetApplicationLocale(); } -std::string AtomExtensionsBrowserClient::GetUserAgent() const { - return AtomBrowserClient::Get()->GetUserAgent(); +std::string ElectronExtensionsBrowserClient::GetUserAgent() const { + return ElectronBrowserClient::Get()->GetUserAgent(); } +void ElectronExtensionsBrowserClient::RegisterBrowserInterfaceBindersForFrame( + service_manager::BinderMapWithContext* map, + content::RenderFrameHost* render_frame_host, + const extensions::Extension* extension) const {} + } // namespace electron diff --git a/shell/browser/extensions/atom_extensions_browser_client.h b/shell/browser/extensions/electron_extensions_browser_client.h similarity index 85% rename from shell/browser/extensions/atom_extensions_browser_client.h rename to shell/browser/extensions/electron_extensions_browser_client.h index a21988d9b999f..3b83089cdc219 100644 --- a/shell/browser/extensions/atom_extensions_browser_client.h +++ b/shell/browser/extensions/electron_extensions_browser_client.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_CLIENT_H_ -#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_CLIENT_H_ +#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSIONS_BROWSER_CLIENT_H_ +#define SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSIONS_BROWSER_CLIENT_H_ #include #include @@ -13,6 +13,7 @@ #include "base/macros.h" #include "build/build_config.h" #include "extensions/browser/extensions_browser_client.h" +#include "mojo/public/cpp/bindings/pending_remote.h" class PrefService; @@ -29,10 +30,11 @@ namespace electron { // with no related incognito context. // Must be initialized via InitWithBrowserContext() once the BrowserContext is // created. -class AtomExtensionsBrowserClient : public extensions::ExtensionsBrowserClient { +class ElectronExtensionsBrowserClient + : public extensions::ExtensionsBrowserClient { public: - AtomExtensionsBrowserClient(); - ~AtomExtensionsBrowserClient() override; + ElectronExtensionsBrowserClient(); + ~ElectronExtensionsBrowserClient() override; // ExtensionsBrowserClient overrides: bool IsShuttingDown() override; @@ -59,11 +61,11 @@ class AtomExtensionsBrowserClient : public extensions::ExtensionsBrowserClient { int* resource_id) const override; void LoadResourceFromResourceBundle( const network::ResourceRequest& request, - network::mojom::URLLoaderRequest loader, + mojo::PendingReceiver loader, const base::FilePath& resource_relative_path, int resource_id, const std::string& content_security_policy, - network::mojom::URLLoaderClientPtr client, + mojo::PendingRemote client, bool send_cors_header) override; bool AllowCrossRendererResourceLoad( const GURL& url, @@ -115,6 +117,10 @@ class AtomExtensionsBrowserClient : public extensions::ExtensionsBrowserClient { bool IsLockScreenContext(content::BrowserContext* context) override; std::string GetApplicationLocale() override; std::string GetUserAgent() const override; + void RegisterBrowserInterfaceBindersForFrame( + service_manager::BinderMapWithContext* map, + content::RenderFrameHost* render_frame_host, + const extensions::Extension* extension) const override; // |context| is the single BrowserContext used for IsValidContext(). // |pref_service| is used for GetPrefServiceForContext(). @@ -131,9 +137,9 @@ class AtomExtensionsBrowserClient : public extensions::ExtensionsBrowserClient { // The extension cache used for download and installation. std::unique_ptr extension_cache_; - DISALLOW_COPY_AND_ASSIGN(AtomExtensionsBrowserClient); + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionsBrowserClient); }; } // namespace electron -#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_CLIENT_H_ +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSIONS_BROWSER_CLIENT_H_ diff --git a/shell/browser/extensions/atom_navigation_ui_data.cc b/shell/browser/extensions/electron_navigation_ui_data.cc similarity index 62% rename from shell/browser/extensions/atom_navigation_ui_data.cc rename to shell/browser/extensions/electron_navigation_ui_data.cc index ab1b8afd22203..eac7e275c67be 100644 --- a/shell/browser/extensions/atom_navigation_ui_data.cc +++ b/shell/browser/extensions/electron_navigation_ui_data.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/browser/extensions/atom_navigation_ui_data.h" +#include "shell/browser/extensions/electron_navigation_ui_data.h" #include @@ -11,20 +11,20 @@ namespace extensions { -AtomNavigationUIData::AtomNavigationUIData() {} +ElectronNavigationUIData::ElectronNavigationUIData() {} -AtomNavigationUIData::AtomNavigationUIData( +ElectronNavigationUIData::ElectronNavigationUIData( content::NavigationHandle* navigation_handle) { extension_data_ = std::make_unique( navigation_handle, extension_misc::kUnknownTabId, extension_misc::kUnknownWindowId); } -AtomNavigationUIData::~AtomNavigationUIData() {} +ElectronNavigationUIData::~ElectronNavigationUIData() {} -std::unique_ptr AtomNavigationUIData::Clone() { - std::unique_ptr copy = - std::make_unique(); +std::unique_ptr ElectronNavigationUIData::Clone() { + std::unique_ptr copy = + std::make_unique(); if (extension_data_) copy->SetExtensionNavigationUIData(extension_data_->DeepCopy()); @@ -32,7 +32,7 @@ std::unique_ptr AtomNavigationUIData::Clone() { return std::move(copy); } -void AtomNavigationUIData::SetExtensionNavigationUIData( +void ElectronNavigationUIData::SetExtensionNavigationUIData( std::unique_ptr extension_data) { extension_data_ = std::move(extension_data); } diff --git a/shell/browser/extensions/atom_navigation_ui_data.h b/shell/browser/extensions/electron_navigation_ui_data.h similarity index 73% rename from shell/browser/extensions/atom_navigation_ui_data.h rename to shell/browser/extensions/electron_navigation_ui_data.h index f757ee5e8d501..e3680eb4315b9 100644 --- a/shell/browser/extensions/atom_navigation_ui_data.h +++ b/shell/browser/extensions/electron_navigation_ui_data.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_NAVIGATION_UI_DATA_H_ -#define SHELL_BROWSER_EXTENSIONS_ATOM_NAVIGATION_UI_DATA_H_ +#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_NAVIGATION_UI_DATA_H_ +#define SHELL_BROWSER_EXTENSIONS_ELECTRON_NAVIGATION_UI_DATA_H_ #include @@ -18,11 +18,12 @@ namespace extensions { // beginning of each navigation. The class is instantiated on the UI thread, // then a copy created using Clone is passed to the content::ResourceRequestInfo // on the IO thread. -class AtomNavigationUIData : public content::NavigationUIData { +class ElectronNavigationUIData : public content::NavigationUIData { public: - AtomNavigationUIData(); - explicit AtomNavigationUIData(content::NavigationHandle* navigation_handle); - ~AtomNavigationUIData() override; + ElectronNavigationUIData(); + explicit ElectronNavigationUIData( + content::NavigationHandle* navigation_handle); + ~ElectronNavigationUIData() override; // Creates a new ChromeNavigationUIData that is a deep copy of the original. // Any changes to the original after the clone is created will not be @@ -40,9 +41,9 @@ class AtomNavigationUIData : public content::NavigationUIData { // Manages the lifetime of optional ExtensionNavigationUIData information. std::unique_ptr extension_data_; - DISALLOW_COPY_AND_ASSIGN(AtomNavigationUIData); + DISALLOW_COPY_AND_ASSIGN(ElectronNavigationUIData); }; } // namespace extensions -#endif // SHELL_BROWSER_EXTENSIONS_ATOM_NAVIGATION_UI_DATA_H_ +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_NAVIGATION_UI_DATA_H_ diff --git a/shell/browser/feature_list.cc b/shell/browser/feature_list.cc index b19596b7b429b..685142821698d 100644 --- a/shell/browser/feature_list.cc +++ b/shell/browser/feature_list.cc @@ -27,6 +27,13 @@ void InitializeFeatureList() { // when node integration is enabled. disable_features += std::string(",") + features::kSpareRendererForSitePerProcess.name; + + // https://www.polymer-project.org/blog/2018-10-02-webcomponents-v0-deprecations + // https://chromium-review.googlesource.com/c/chromium/src/+/1869562 + // Any website which uses older WebComponents will fail in without this + // enabled, since Electron does not support origin trials. + enable_features += std::string(",") + "WebComponentsV0Enabled"; + #if !BUILDFLAG(ENABLE_PICTURE_IN_PICTURE) disable_features += std::string(",") + media::kPictureInPicture.name; #endif diff --git a/shell/browser/javascript_environment.h b/shell/browser/javascript_environment.h index 67a21a0386650..b3556f83783c0 100644 --- a/shell/browser/javascript_environment.h +++ b/shell/browser/javascript_environment.h @@ -9,7 +9,7 @@ #include "base/macros.h" #include "gin/public/isolate_holder.h" -#include "uv.h" // NOLINT(build/include) +#include "uv.h" // NOLINT(build/include_directory) namespace node { class Environment; diff --git a/shell/browser/lib/bluetooth_chooser.h b/shell/browser/lib/bluetooth_chooser.h index d6f0f7c82f4bd..d74684ff3ba2c 100644 --- a/shell/browser/lib/bluetooth_chooser.h +++ b/shell/browser/lib/bluetooth_chooser.h @@ -10,7 +10,7 @@ #include #include "content/public/browser/bluetooth_chooser.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" namespace electron { diff --git a/shell/browser/linux/unity_service.cc b/shell/browser/linux/unity_service.cc new file mode 100644 index 0000000000000..78cca845fc868 --- /dev/null +++ b/shell/browser/linux/unity_service.cc @@ -0,0 +1,143 @@ +// Copyright 2013 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 "shell/browser/linux/unity_service.h" + +#include +#include + +#include +#include + +#include "base/environment.h" +#include "base/nix/xdg_util.h" + +// Unity data typedefs. +typedef struct _UnityInspector UnityInspector; +typedef UnityInspector* (*unity_inspector_get_default_func)(void); +typedef gboolean (*unity_inspector_get_unity_running_func)( + UnityInspector* self); + +typedef struct _UnityLauncherEntry UnityLauncherEntry; +typedef UnityLauncherEntry* (*unity_launcher_entry_get_for_desktop_id_func)( + const gchar* desktop_id); +typedef void (*unity_launcher_entry_set_count_func)(UnityLauncherEntry* self, + gint64 value); +typedef void (*unity_launcher_entry_set_count_visible_func)( + UnityLauncherEntry* self, + gboolean value); +typedef void (*unity_launcher_entry_set_progress_func)(UnityLauncherEntry* self, + gdouble value); +typedef void (*unity_launcher_entry_set_progress_visible_func)( + UnityLauncherEntry* self, + gboolean value); + +namespace { + +bool attempted_load = false; + +// Unity has a singleton object that we can ask whether the unity is running. +UnityInspector* inspector = nullptr; + +// A link to the desktop entry in the panel. +UnityLauncherEntry* chrome_entry = nullptr; + +// Retrieved functions from libunity. +unity_inspector_get_unity_running_func get_unity_running = nullptr; +unity_launcher_entry_set_count_func entry_set_count = nullptr; +unity_launcher_entry_set_count_visible_func entry_set_count_visible = nullptr; +unity_launcher_entry_set_progress_func entry_set_progress = nullptr; +unity_launcher_entry_set_progress_visible_func entry_set_progress_visible = + nullptr; + +void EnsureLibUnityLoaded() { + using base::nix::GetDesktopEnvironment; + + if (attempted_load) + return; + attempted_load = true; + + std::unique_ptr env(base::Environment::Create()); + base::nix::DesktopEnvironment desktop_env = GetDesktopEnvironment(env.get()); + + // The "icon-tasks" KDE task manager also honors Unity Launcher API. + if (desktop_env != base::nix::DESKTOP_ENVIRONMENT_UNITY && + desktop_env != base::nix::DESKTOP_ENVIRONMENT_KDE4 && + desktop_env != base::nix::DESKTOP_ENVIRONMENT_KDE5) + return; + + // Ubuntu still hasn't given us a nice libunity.so symlink. + void* unity_lib = dlopen("libunity.so.4", RTLD_LAZY); + if (!unity_lib) + unity_lib = dlopen("libunity.so.6", RTLD_LAZY); + if (!unity_lib) + unity_lib = dlopen("libunity.so.9", RTLD_LAZY); + if (!unity_lib) + return; + + unity_inspector_get_default_func inspector_get_default = + reinterpret_cast( + dlsym(unity_lib, "unity_inspector_get_default")); + if (inspector_get_default) { + inspector = inspector_get_default(); + + get_unity_running = + reinterpret_cast( + dlsym(unity_lib, "unity_inspector_get_unity_running")); + } + + unity_launcher_entry_get_for_desktop_id_func entry_get_for_desktop_id = + reinterpret_cast( + dlsym(unity_lib, "unity_launcher_entry_get_for_desktop_id")); + if (entry_get_for_desktop_id) { + std::string desktop_id = getenv("CHROME_DESKTOP"); + chrome_entry = entry_get_for_desktop_id(desktop_id.c_str()); + + entry_set_count = reinterpret_cast( + dlsym(unity_lib, "unity_launcher_entry_set_count")); + + entry_set_count_visible = + reinterpret_cast( + dlsym(unity_lib, "unity_launcher_entry_set_count_visible")); + + entry_set_progress = + reinterpret_cast( + dlsym(unity_lib, "unity_launcher_entry_set_progress")); + + entry_set_progress_visible = + reinterpret_cast( + dlsym(unity_lib, "unity_launcher_entry_set_progress_visible")); + } +} + +} // namespace + +namespace unity { + +bool IsRunning() { + EnsureLibUnityLoaded(); + if (inspector && get_unity_running) + return get_unity_running(inspector); + + return false; +} + +void SetDownloadCount(int count) { + EnsureLibUnityLoaded(); + if (chrome_entry && entry_set_count && entry_set_count_visible) { + entry_set_count(chrome_entry, count); + entry_set_count_visible(chrome_entry, count != 0); + } +} + +void SetProgressFraction(float percentage) { + EnsureLibUnityLoaded(); + if (chrome_entry && entry_set_progress && entry_set_progress_visible) { + entry_set_progress(chrome_entry, percentage); + entry_set_progress_visible(chrome_entry, + percentage > 0.0 && percentage < 1.0); + } +} + +} // namespace unity diff --git a/shell/browser/linux/unity_service.h b/shell/browser/linux/unity_service.h new file mode 100644 index 0000000000000..9df6619742a84 --- /dev/null +++ b/shell/browser/linux/unity_service.h @@ -0,0 +1,23 @@ +// Copyright 2013 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 SHELL_BROWSER_LINUX_UNITY_SERVICE_H_ +#define SHELL_BROWSER_LINUX_UNITY_SERVICE_H_ + +namespace unity { + +// Returns whether unity is currently running. +bool IsRunning(); + +// If unity is running, sets the download counter in the dock icon. Any value +// other than 0 displays the badge. +void SetDownloadCount(int count); + +// If unity is running, sets the download progress bar in the dock icon. Any +// value between 0.0 and 1.0 (exclusive) shows the progress bar. +void SetProgressFraction(float percentage); + +} // namespace unity + +#endif // SHELL_BROWSER_LINUX_UNITY_SERVICE_H_ diff --git a/shell/browser/login_handler.cc b/shell/browser/login_handler.cc index 45c3ca8b09718..afe477922c3c9 100644 --- a/shell/browser/login_handler.cc +++ b/shell/browser/login_handler.cc @@ -5,100 +5,91 @@ #include "shell/browser/login_handler.h" #include +#include #include - -#include "base/task/post_task.h" -#include "base/values.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/web_contents.h" -#include "net/base/auth.h" -#include "shell/browser/browser.h" +#include + +#include "base/callback.h" +#include "base/strings/string16.h" +#include "native_mate/arguments.h" +#include "native_mate/dictionary.h" +#include "shell/browser/api/electron_api_web_contents.h" +#include "shell/common/native_mate_converters/callback_converter_deprecated.h" +#include "shell/common/native_mate_converters/gurl_converter.h" #include "shell/common/native_mate_converters/net_converter.h" +#include "shell/common/native_mate_converters/value_converter.h" using content::BrowserThread; namespace electron { -LoginHandler::LoginHandler(net::URLRequest* request, - const net::AuthChallengeInfo& auth_info, - // net::NetworkDelegate::AuthCallback callback, - net::AuthCredentials* credentials) - : credentials_(credentials), - auth_info_(std::make_unique(auth_info)), - // auth_callback_(std::move(callback)), - weak_factory_(this) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - std::unique_ptr request_details( - new base::DictionaryValue); - FillRequestDetails(request_details.get(), request); - - // TODO(deepak1556): fix with network service - // tracking issue: #19602 - CHECK(false) << "fix with network service"; - // web_contents_getter_ = - // resource_request_info->GetWebContentsGetterForRequest(); - - base::PostTask( - FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&Browser::RequestLogin, base::Unretained(Browser::Get()), - base::RetainedRef(this), std::move(request_details))); -} - -LoginHandler::~LoginHandler() = default; - -void LoginHandler::Login(const base::string16& username, - const base::string16& password) { +LoginHandler::LoginHandler( + const net::AuthChallengeInfo& auth_info, + content::WebContents* web_contents, + bool is_main_frame, + const GURL& url, + scoped_refptr response_headers, + bool first_auth_attempt, + LoginAuthRequiredCallback auth_required_callback) + + : WebContentsObserver(web_contents), + auth_required_callback_(std::move(auth_required_callback)) { DCHECK_CURRENTLY_ON(BrowserThread::UI); base::PostTask( - FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&LoginHandler::DoLogin, weak_factory_.GetWeakPtr(), - username, password)); + FROM_HERE, {base::CurrentThread()}, + base::BindOnce(&LoginHandler::EmitEvent, weak_factory_.GetWeakPtr(), + auth_info, is_main_frame, url, response_headers, + first_auth_attempt)); } -void LoginHandler::CancelAuth() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); +void LoginHandler::EmitEvent( + net::AuthChallengeInfo auth_info, + bool is_main_frame, + const GURL& url, + scoped_refptr response_headers, + bool first_auth_attempt) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + auto api_web_contents = api::WebContents::From(isolate, web_contents()); + if (api_web_contents.IsEmpty()) { + std::move(auth_required_callback_).Run(base::nullopt); + return; + } - base::PostTask( - FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&LoginHandler::DoCancelAuth, weak_factory_.GetWeakPtr())); -} + v8::HandleScope scope(isolate); -void LoginHandler::NotifyRequestDestroyed() { - // auth_callback_.Reset(); - credentials_ = nullptr; - weak_factory_.InvalidateWeakPtrs(); -} + auto details = mate::Dictionary::CreateEmpty(isolate); + details.Set("url", url); -content::WebContents* LoginHandler::GetWebContents() const { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - // TODO(deepak1556): fix with network service - // tracking issue: #19602 - CHECK(false) << "fix with network service"; - return web_contents_getter_.Run(); -} + // These parameters aren't documented, and I'm not sure that they're useful, + // but we might as well stick 'em on the details object. If it turns out they + // are useful, we can add them to the docs :) + details.Set("isMainFrame", is_main_frame); + details.Set("firstAuthAttempt", first_auth_attempt); + details.Set("responseHeaders", response_headers.get()); -void LoginHandler::DoCancelAuth() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - /* - if (!auth_callback_.is_null()) - std::move(auth_callback_) - .Run(net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_CANCEL_AUTH); - */ + bool default_prevented = + api_web_contents->Emit("login", std::move(details), auth_info, + base::BindRepeating(&LoginHandler::CallbackFromJS, + weak_factory_.GetWeakPtr())); + if (!default_prevented && auth_required_callback_) { + std::move(auth_required_callback_).Run(base::nullopt); + } } -void LoginHandler::DoLogin(const base::string16& username, - const base::string16& password) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - /* - if (!auth_callback_.is_null()) { - credentials_->Set(username, password); - std::move(auth_callback_) - .Run(net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_SET_AUTH); +LoginHandler::~LoginHandler() = default; + +void LoginHandler::CallbackFromJS(mate::Arguments* args) { + if (auth_required_callback_) { + base::string16 username, password; + if (!args->GetNext(&username) || !args->GetNext(&password)) { + std::move(auth_required_callback_).Run(base::nullopt); + return; + } + std::move(auth_required_callback_) + .Run(net::AuthCredentials(username, password)); } - */ } } // namespace electron diff --git a/shell/browser/login_handler.h b/shell/browser/login_handler.h index 64b735cb8dd13..46ca0c7ba8ac3 100644 --- a/shell/browser/login_handler.h +++ b/shell/browser/login_handler.h @@ -5,68 +5,45 @@ #ifndef SHELL_BROWSER_LOGIN_HANDLER_H_ #define SHELL_BROWSER_LOGIN_HANDLER_H_ -#include - -#include "base/callback.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/sequenced_task_runner_helpers.h" -#include "base/strings/string16.h" -#include "content/public/browser/web_contents.h" -#include "net/base/network_delegate.h" +#include "base/values.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/browser/login_delegate.h" +#include "content/public/browser/web_contents_observer.h" namespace content { class WebContents; } +namespace mate { +class Arguments; +} + namespace electron { -// Handles the HTTP basic auth, must be created on IO thread. -class LoginHandler : public base::RefCountedThreadSafe { +// Handles HTTP basic auth. +class LoginHandler : public content::LoginDelegate, + public content::WebContentsObserver { public: - LoginHandler(net::URLRequest* request, - const net::AuthChallengeInfo& auth_info, - // net::NetworkDelegate::AuthCallback callback, - net::AuthCredentials* credentials); - - // The auth is cancelled, must be called on UI thread. - void CancelAuth(); - - // The URLRequest associated with the auth is destroyed. - void NotifyRequestDestroyed(); - - // Login with |username| and |password|, must be called on UI thread. - void Login(const base::string16& username, const base::string16& password); - - // Returns the WebContents associated with the request, must be called on UI - // thread. - content::WebContents* GetWebContents() const; - - const net::AuthChallengeInfo* auth_info() const { return auth_info_.get(); } + LoginHandler(const net::AuthChallengeInfo& auth_info, + content::WebContents* web_contents, + bool is_main_frame, + const GURL& url, + scoped_refptr response_headers, + bool first_auth_attempt, + LoginAuthRequiredCallback auth_required_callback); + ~LoginHandler() override; private: - friend class base::RefCountedThreadSafe; - friend class base::DeleteHelper; - - ~LoginHandler(); - - // Must be called on IO thread. - void DoCancelAuth(); - void DoLogin(const base::string16& username, const base::string16& password); - - // Credentials to be used for the auth. - net::AuthCredentials* credentials_; - - // Who/where/what asked for the authentication. - std::unique_ptr auth_info_; - - // WebContents associated with the login request. - content::WebContents::Getter web_contents_getter_; + void EmitEvent(net::AuthChallengeInfo auth_info, + bool is_main_frame, + const GURL& url, + scoped_refptr response_headers, + bool first_auth_attempt); + void CallbackFromJS(mate::Arguments* args); - // Called with preferred value of net::NetworkDelegate::AuthRequiredResponse. - // net::NetworkDelegate::AuthCallback auth_callback_; + LoginAuthRequiredCallback auth_required_callback_; - base::WeakPtrFactory weak_factory_; + base::WeakPtrFactory weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(LoginHandler); }; diff --git a/shell/browser/mac/atom_application.h b/shell/browser/mac/atom_application.h deleted file mode 100644 index 0058b8e852af9..0000000000000 --- a/shell/browser/mac/atom_application.h +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "base/callback.h" -#include "base/mac/scoped_nsobject.h" -#include "base/mac/scoped_sending_event.h" - -#import -#import - -// Forward Declare Appearance APIs -@interface NSApplication (MojaveSDK) -@property(copy, readonly) - NSAppearance* effectiveAppearance API_AVAILABLE(macosx(10.14)); -@property(copy, readonly) NSAppearance* appearance API_AVAILABLE(macosx(10.14)); -- (void)setAppearance:(NSAppearance*)appearance API_AVAILABLE(macosx(10.14)); -@end - -// forward declare Access APIs -typedef NSString* AVMediaType NS_EXTENSIBLE_STRING_ENUM; - -AVF_EXPORT AVMediaType const AVMediaTypeVideo; -AVF_EXPORT AVMediaType const AVMediaTypeAudio; - -typedef NS_ENUM(NSInteger, AVAuthorizationStatusMac) { - AVAuthorizationStatusNotDeterminedMac = 0, - AVAuthorizationStatusRestrictedMac = 1, - AVAuthorizationStatusDeniedMac = 2, - AVAuthorizationStatusAuthorizedMac = 3, -}; - -@interface AVCaptureDevice (MojaveSDK) -+ (void)requestAccessForMediaType:(AVMediaType)mediaType - completionHandler:(void (^)(BOOL granted))handler - API_AVAILABLE(macosx(10.14)); -+ (AVAuthorizationStatusMac)authorizationStatusForMediaType: - (AVMediaType)mediaType API_AVAILABLE(macosx(10.14)); -@end - -@interface NSColor (MojaveSDK) -@property(class, strong, readonly) - NSColor* controlAccentColor API_AVAILABLE(macosx(10.14)); - -// macOS system colors -@property(class, strong, readonly) - NSColor* systemBlueColor API_AVAILABLE(macosx(10.10)); -@property(class, strong, readonly) - NSColor* systemBrownColor API_AVAILABLE(macosx(10.10)); -@property(class, strong, readonly) - NSColor* systemGrayColor API_AVAILABLE(macosx(10.10)); -@property(class, strong, readonly) - NSColor* systemGreenColor API_AVAILABLE(macosx(10.10)); -@property(class, strong, readonly) - NSColor* systemOrangeColor API_AVAILABLE(macosx(10.10)); -@property(class, strong, readonly) - NSColor* systemPinkColor API_AVAILABLE(macosx(10.10)); -@property(class, strong, readonly) - NSColor* systemPurpleColor API_AVAILABLE(macosx(10.10)); -@property(class, strong, readonly) - NSColor* systemRedColor API_AVAILABLE(macosx(10.10)); -@property(class, strong, readonly) - NSColor* systemYellowColor API_AVAILABLE(macosx(10.10)); - -// misc dynamic colors declarations -@property(class, strong, readonly) - NSColor* linkColor API_AVAILABLE(macosx(10.10)); -@property(class, strong, readonly) - NSColor* placeholderTextColor API_AVAILABLE(macosx(10.10)); -@property(class, strong, readonly) - NSColor* findHighlightColor API_AVAILABLE(macosx(10.13)); -@property(class, strong, readonly) - NSColor* separatorColor API_AVAILABLE(macosx(10.14)); -@property(class, strong, readonly) - NSColor* selectedContentBackgroundColor API_AVAILABLE(macosx(10.14)); -@property(class, strong, readonly) - NSColor* unemphasizedSelectedContentBackgroundColor API_AVAILABLE( - macosx(10.14)); -@property(class, strong, readonly) - NSColor* unemphasizedSelectedTextBackgroundColor API_AVAILABLE(macosx(10.14) - ); -@property(class, strong, readonly) - NSColor* unemphasizedSelectedTextColor API_AVAILABLE(macosx(10.14)); -@end - -@interface AtomApplication : NSApplication { - @private - BOOL handlingSendEvent_; - base::scoped_nsobject currentActivity_; - NSCondition* handoffLock_; - BOOL updateReceived_; - base::Callback shouldShutdown_; -} - -+ (AtomApplication*)sharedApplication; - -- (void)setShutdownHandler:(base::Callback)handler; -- (void)registerURLHandler; - -// CrAppProtocol: -- (BOOL)isHandlingSendEvent; - -// CrAppControlProtocol: -- (void)setHandlingSendEvent:(BOOL)handlingSendEvent; - -- (NSUserActivity*)getCurrentActivity; -- (void)setCurrentActivity:(NSString*)type - withUserInfo:(NSDictionary*)userInfo - withWebpageURL:(NSURL*)webpageURL; -- (void)invalidateCurrentActivity; -- (void)resignCurrentActivity; -- (void)updateCurrentActivity:(NSString*)type - withUserInfo:(NSDictionary*)userInfo; - -@end diff --git a/shell/browser/mac/atom_application_delegate.h b/shell/browser/mac/atom_application_delegate.h deleted file mode 100644 index 1805f256b1803..0000000000000 --- a/shell/browser/mac/atom_application_delegate.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#import - -#import "shell/browser/ui/cocoa/atom_menu_controller.h" - -@interface AtomApplicationDelegate : NSObject { - @private - base::scoped_nsobject menu_controller_; -} - -// Sets the menu that will be returned in "applicationDockMenu:". -- (void)setApplicationDockMenu:(electron::AtomMenuModel*)model; - -@end diff --git a/shell/browser/mac/electron_application.h b/shell/browser/mac/electron_application.h new file mode 100644 index 0000000000000..a9d1817b4e6e6 --- /dev/null +++ b/shell/browser/mac/electron_application.h @@ -0,0 +1,43 @@ +// Copyright (c) 2013 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "base/callback.h" +#include "base/mac/scoped_nsobject.h" +#include "base/mac/scoped_sending_event.h" + +#import +#import + +@interface AtomApplication : NSApplication { + @private + BOOL handlingSendEvent_; + base::scoped_nsobject currentActivity_; + NSCondition* handoffLock_; + BOOL updateReceived_; + base::Callback shouldShutdown_; +} + ++ (AtomApplication*)sharedApplication; + +- (void)setShutdownHandler:(base::Callback)handler; +- (void)registerURLHandler; + +// CrAppProtocol: +- (BOOL)isHandlingSendEvent; + +// CrAppControlProtocol: +- (void)setHandlingSendEvent:(BOOL)handlingSendEvent; + +- (NSUserActivity*)getCurrentActivity; +- (void)setCurrentActivity:(NSString*)type + withUserInfo:(NSDictionary*)userInfo + withWebpageURL:(NSURL*)webpageURL; +- (void)invalidateCurrentActivity; +- (void)resignCurrentActivity; +- (void)updateCurrentActivity:(NSString*)type + withUserInfo:(NSDictionary*)userInfo; + +@end diff --git a/shell/browser/mac/atom_application.mm b/shell/browser/mac/electron_application.mm similarity index 98% rename from shell/browser/mac/atom_application.mm rename to shell/browser/mac/electron_application.mm index eece9854efd01..9a3f51088755d 100644 --- a/shell/browser/mac/atom_application.mm +++ b/shell/browser/mac/electron_application.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#import "shell/browser/mac/atom_application.h" +#import "shell/browser/mac/electron_application.h" #include #include @@ -15,8 +15,8 @@ #include "content/public/browser/native_event_processor_mac.h" #include "content/public/browser/native_event_processor_observer_mac.h" #include "shell/browser/browser.h" -#import "shell/browser/mac/atom_application_delegate.h" #include "shell/browser/mac/dict_util.h" +#import "shell/browser/mac/electron_application_delegate.h" namespace { diff --git a/shell/browser/mac/electron_application_delegate.h b/shell/browser/mac/electron_application_delegate.h new file mode 100644 index 0000000000000..43063ae2d874c --- /dev/null +++ b/shell/browser/mac/electron_application_delegate.h @@ -0,0 +1,17 @@ +// Copyright (c) 2013 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#import + +#import "shell/browser/ui/cocoa/electron_menu_controller.h" + +@interface ElectronApplicationDelegate : NSObject { + @private + base::scoped_nsobject menu_controller_; +} + +// Sets the menu that will be returned in "applicationDockMenu:". +- (void)setApplicationDockMenu:(electron::ElectronMenuModel*)model; + +@end diff --git a/shell/browser/mac/atom_application_delegate.mm b/shell/browser/mac/electron_application_delegate.mm similarity index 90% rename from shell/browser/mac/atom_application_delegate.mm rename to shell/browser/mac/electron_application_delegate.mm index 9cd911905ec6a..f5da4f4febfba 100644 --- a/shell/browser/mac/atom_application_delegate.mm +++ b/shell/browser/mac/electron_application_delegate.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#import "shell/browser/mac/atom_application_delegate.h" +#import "shell/browser/mac/electron_application_delegate.h" #include #include @@ -14,8 +14,8 @@ #include "base/strings/sys_string_conversions.h" #include "base/values.h" #include "shell/browser/browser.h" -#import "shell/browser/mac/atom_application.h" #include "shell/browser/mac/dict_util.h" +#import "shell/browser/mac/electron_application.h" #if BUILDFLAG(USE_ALLOCATOR_SHIM) // On macOS 10.12, the IME system attempts to allocate a 2^64 size buffer, @@ -33,18 +33,19 @@ - (void)_coreAttributesFromRange:(NSRange)range // not have fatal OOM occur while this method executes, but it is better // than crashing when using IME. base::allocator::SetCallNewHandlerOnMallocFailure(false); - g_swizzle_imk_input_session->GetOriginalImplementation()(self, _cmd, range, - attributes, block); + g_swizzle_imk_input_session->InvokeOriginal< + void, NSRange, long long, void (^)(void)>( // NOLINT(runtime/int) + self, _cmd, range, attributes, block); base::allocator::SetCallNewHandlerOnMallocFailure(true); } @end #endif // BUILDFLAG(USE_ALLOCATOR_SHIM) -@implementation AtomApplicationDelegate +@implementation ElectronApplicationDelegate -- (void)setApplicationDockMenu:(electron::AtomMenuModel*)model { - menu_controller_.reset([[AtomMenuController alloc] initWithModel:model - useDefaultAccelerator:NO]); +- (void)setApplicationDockMenu:(electron::ElectronMenuModel*)model { + menu_controller_.reset([[ElectronMenuController alloc] initWithModel:model + useDefaultAccelerator:NO]); } - (void)applicationWillFinishLaunching:(NSNotification*)notify { diff --git a/shell/browser/microtasks_runner.cc b/shell/browser/microtasks_runner.cc index c96645db19c13..9c20bc827eb92 100644 --- a/shell/browser/microtasks_runner.cc +++ b/shell/browser/microtasks_runner.cc @@ -10,7 +10,8 @@ namespace electron { MicrotasksRunner::MicrotasksRunner(v8::Isolate* isolate) : isolate_(isolate) {} -void MicrotasksRunner::WillProcessTask(const base::PendingTask& pending_task) {} +void MicrotasksRunner::WillProcessTask(const base::PendingTask& pending_task, + bool was_blocked_or_low_priority) {} void MicrotasksRunner::DidProcessTask(const base::PendingTask& pending_task) { v8::Isolate::Scope scope(isolate_); diff --git a/shell/browser/microtasks_runner.h b/shell/browser/microtasks_runner.h index 23bb115b8892e..c048c8653374a 100644 --- a/shell/browser/microtasks_runner.h +++ b/shell/browser/microtasks_runner.h @@ -24,7 +24,8 @@ class MicrotasksRunner : public base::TaskObserver { explicit MicrotasksRunner(v8::Isolate* isolate); // base::TaskObserver - void WillProcessTask(const base::PendingTask& pending_task) override; + void WillProcessTask(const base::PendingTask& pending_task, + bool was_blocked_or_low_priority) override; void DidProcessTask(const base::PendingTask& pending_task) override; private: diff --git a/shell/browser/native_browser_view.cc b/shell/browser/native_browser_view.cc index b80dbdd4f3fd5..ff948e4839da8 100644 --- a/shell/browser/native_browser_view.cc +++ b/shell/browser/native_browser_view.cc @@ -6,7 +6,7 @@ #include "shell/browser/native_browser_view.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/ui/inspectable_web_contents.h" namespace electron { diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index d707326159fff..1277fa74a453a 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -321,7 +321,7 @@ bool NativeWindow::IsDocumentEdited() { void NativeWindow::SetFocusable(bool focusable) {} -void NativeWindow::SetMenu(AtomMenuModel* menu) {} +void NativeWindow::SetMenu(ElectronMenuModel* menu) {} void NativeWindow::SetParentWindow(NativeWindow* parent) { parent_ = parent; diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 6f87855faab0b..1a19aa83928b5 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -44,7 +44,7 @@ class PersistentDictionary; namespace electron { -class AtomMenuModel; +class ElectronMenuModel; class NativeBrowserView; #if defined(OS_MACOSX) @@ -160,7 +160,7 @@ class NativeWindow : public base::SupportsUserData, virtual void SetIgnoreMouseEvents(bool ignore, bool forward) = 0; virtual void SetContentProtection(bool enable) = 0; virtual void SetFocusable(bool focusable); - virtual void SetMenu(AtomMenuModel* menu); + virtual void SetMenu(ElectronMenuModel* menu); virtual void SetParentWindow(NativeWindow* parent); virtual void AddBrowserView(NativeBrowserView* browser_view) = 0; virtual void RemoveBrowserView(NativeBrowserView* browser_view) = 0; @@ -194,6 +194,13 @@ class NativeWindow : public base::SupportsUserData, // Vibrancy API virtual void SetVibrancy(const std::string& type); + // Traffic Light API +#if defined(OS_MACOSX) + virtual void SetTrafficLightPosition(const gfx::Point& position) = 0; + virtual gfx::Point GetTrafficLightPosition() const = 0; + virtual void RepositionTrafficLights() = 0; +#endif + // Touchbar API virtual void SetTouchBar( const std::vector& items); diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index c570eb212ab0d..9fcc06a4b4e3d 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -14,19 +14,20 @@ #include "base/mac/scoped_nsobject.h" #include "shell/browser/native_window.h" +#include "ui/native_theme/native_theme_observer.h" #include "ui/views/controls/native/native_view_host.h" -@class AtomNSWindow; -@class AtomNSWindowDelegate; -@class AtomPreviewItem; -@class AtomTouchBar; +@class ElectronNSWindow; +@class ElectronNSWindowDelegate; +@class ElectronPreviewItem; +@class ElectronTouchBar; @class CustomWindowButtonView; namespace electron { class RootViewMac; -class NativeWindowMac : public NativeWindow { +class NativeWindowMac : public NativeWindow, public ui::NativeThemeObserver { public: NativeWindowMac(const mate::Dictionary& options, NativeWindow* parent); ~NativeWindowMac() override; @@ -149,6 +150,13 @@ class NativeWindowMac : public NativeWindow { void SetCollectionBehavior(bool on, NSUInteger flag); void SetWindowLevel(int level); + // Custom traffic light positioning + void RepositionTrafficLights() 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 TitleBarStyle { NORMAL, HIDDEN, @@ -157,11 +165,12 @@ class NativeWindowMac : public NativeWindow { }; TitleBarStyle title_bar_style() const { return title_bar_style_; } - AtomPreviewItem* preview_item() const { return preview_item_.get(); } - AtomTouchBar* touch_bar() const { return touch_bar_.get(); } + ElectronPreviewItem* preview_item() const { return preview_item_.get(); } + ElectronTouchBar* touch_bar() const { return touch_bar_.get(); } 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: @@ -175,11 +184,11 @@ class NativeWindowMac : public NativeWindow { void InternalSetParentWindow(NativeWindow* parent, bool attach); void SetForwardMouseMessages(bool forward); - AtomNSWindow* window_; // Weak ref, managed by widget_. + ElectronNSWindow* window_; // Weak ref, managed by widget_. - base::scoped_nsobject window_delegate_; - base::scoped_nsobject preview_item_; - base::scoped_nsobject touch_bar_; + base::scoped_nsobject window_delegate_; + base::scoped_nsobject preview_item_; + base::scoped_nsobject touch_bar_; base::scoped_nsobject buttons_view_; // Event monitor for scroll wheel event. @@ -198,6 +207,8 @@ class NativeWindowMac : public NativeWindow { bool zoom_to_page_width_ = false; bool fullscreen_window_title_ = false; bool resizable_ = true; + bool exiting_fullscreen_ = false; + gfx::Point traffic_light_position_; NSInteger attention_request_id_ = 0; // identifier from requestUserAttention diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index e449f1973ec43..863017201bed3 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -7,28 +7,34 @@ #include #include +#include #include +#include #include #include "base/mac/mac_util.h" #include "base/mac/scoped_cftyperef.h" #include "base/numerics/ranges.h" #include "base/strings/sys_string_conversions.h" +#include "base/task/post_task.h" #include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #include "content/public/browser/browser_accessibility_state.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/desktop_media_id.h" #include "native_mate/dictionary.h" #include "shell/browser/native_browser_view_mac.h" -#include "shell/browser/ui/cocoa/atom_native_widget_mac.h" -#include "shell/browser/ui/cocoa/atom_ns_window.h" -#include "shell/browser/ui/cocoa/atom_ns_window_delegate.h" -#include "shell/browser/ui/cocoa/atom_preview_item.h" -#include "shell/browser/ui/cocoa/atom_touch_bar.h" +#include "shell/browser/ui/cocoa/electron_native_widget_mac.h" +#include "shell/browser/ui/cocoa/electron_ns_window.h" +#include "shell/browser/ui/cocoa/electron_ns_window_delegate.h" +#include "shell/browser/ui/cocoa/electron_preview_item.h" +#include "shell/browser/ui/cocoa/electron_touch_bar.h" #include "shell/browser/ui/cocoa/root_view_mac.h" #include "shell/browser/ui/inspectable_web_contents.h" #include "shell/browser/ui/inspectable_web_contents_view.h" #include "shell/browser/window_list.h" #include "shell/common/deprecate_util.h" +#include "shell/common/gin_converters/gfx_converter.h" #include "shell/common/options_switches.h" #include "skia/ext/skia_utils_mac.h" #include "third_party/webrtc/modules/desktop_capture/mac/window_list_utils.h" @@ -206,10 +212,10 @@ - (void)setNeedsDisplayForButtons { @end -@interface AtomProgressBar : NSProgressIndicator +@interface ElectronProgressBar : NSProgressIndicator @end -@implementation AtomProgressBar +@implementation ElectronProgressBar - (void)drawRect:(NSRect)dirtyRect { if (self.style != NSProgressIndicatorBarStyle) @@ -278,7 +284,7 @@ bool IsFramelessWindow(NSView* view) { NSWindow* nswindow = [view window]; if (![nswindow respondsToSelector:@selector(shell)]) return false; - NativeWindow* window = [static_cast(nswindow) shell]; + NativeWindow* window = [static_cast(nswindow) shell]; return window && !window->has_frame(); } @@ -321,6 +327,8 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { NativeWindowMac::NativeWindowMac(const mate::Dictionary& options, NativeWindow* parent) : NativeWindow(options, parent), root_view_(new RootViewMac(this)) { + ui::NativeTheme::GetInstanceForNativeUi()->AddObserver(this); + int width = 800, height = 600; options.Get(options::kWidth, &width); options.Get(options::kHeight, &height); @@ -335,6 +343,11 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { options.Get(options::kZoomToPageWidth, &zoom_to_page_width_); options.Get(options::kFullscreenWindowTitle, &fullscreen_window_title_); options.Get(options::kSimpleFullScreen, &always_simple_fullscreen_); + v8::Local traffic_light_options; + if (options.Get(options::kTrafficLightPosition, &traffic_light_options)) { + gin::ConvertFromV8(options.isolate(), traffic_light_options, + &traffic_light_position_); + } bool minimizable = true; options.Get(options::kMinimizable, &minimizable); @@ -385,14 +398,14 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { params.bounds = bounds; params.delegate = this; params.type = views::Widget::InitParams::TYPE_WINDOW; - params.native_widget = new AtomNativeWidgetMac(this, styleMask, widget()); + params.native_widget = new ElectronNativeWidgetMac(this, styleMask, widget()); widget()->Init(std::move(params)); - window_ = static_cast( + window_ = static_cast( widget()->GetNativeWindow().GetNativeNSWindow()); [window_ setEnableLargerThanScreen:enable_larger_than_screen()]; - window_delegate_.reset([[AtomNSWindowDelegate alloc] initWithShell:this]); + window_delegate_.reset([[ElectronNSWindowDelegate alloc] initWithShell:this]); [window_ setDelegate:window_delegate_]; // Only use native parent window for non-modal windows. @@ -505,10 +518,68 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { } NativeWindowMac::~NativeWindowMac() { + ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this); if (wheel_event_monitor_) [NSEvent removeMonitor:wheel_event_monitor_]; } +void NativeWindowMac::RepositionTrafficLights() { + if (!traffic_light_position_.x() && !traffic_light_position_.y()) { + return; + } + if (IsFullscreen()) + return; + + NSWindow* window = window_; + NSButton* close = [window standardWindowButton:NSWindowCloseButton]; + NSButton* miniaturize = + [window standardWindowButton:NSWindowMiniaturizeButton]; + NSButton* zoom = [window standardWindowButton:NSWindowZoomButton]; + // Safety check just in case apple changes the view structure in a macOS + // update + DCHECK(close.superview); + DCHECK(close.superview.superview); + if (!close.superview || !close.superview.superview) + return; + NSView* titleBarContainerView = close.superview.superview; + + // Hide the container when exiting fullscreen, otherwise traffic light buttons + // jump + if (exiting_fullscreen_) { + [titleBarContainerView setHidden:YES]; + return; + } + + [titleBarContainerView setHidden:NO]; + CGFloat buttonHeight = [close frame].size.height; + CGFloat titleBarFrameHeight = buttonHeight + traffic_light_position_.y(); + CGRect titleBarRect = titleBarContainerView.frame; + CGFloat titleBarWidth = NSWidth(titleBarRect); + titleBarRect.size.height = titleBarFrameHeight; + titleBarRect.origin.y = window.frame.size.height - titleBarFrameHeight; + [titleBarContainerView setFrame:titleBarRect]; + + BOOL isRTL = [titleBarContainerView userInterfaceLayoutDirection] == + NSUserInterfaceLayoutDirectionRightToLeft; + NSArray* windowButtons = @[ close, miniaturize, zoom ]; + const CGFloat space_between = + [miniaturize frame].origin.x - [close frame].origin.x; + for (NSUInteger i = 0; i < windowButtons.count; i++) { + NSView* view = [windowButtons objectAtIndex:i]; + CGRect rect = [view frame]; + if (isRTL) { + CGFloat buttonWidth = NSWidth(rect); + // origin is always top-left, even in RTL + rect.origin.x = titleBarWidth - traffic_light_position_.x() + + (i * space_between) - buttonWidth; + } else { + rect.origin.x = traffic_light_position_.x() + (i * space_between); + } + rect.origin.y = (titleBarFrameHeight - rect.size.height) / 2; + [view setFrameOrigin:rect.origin]; + } +} + void NativeWindowMac::SetContentView(views::View* view) { views::View* root_view = GetContentsView(); if (content_view()) @@ -527,21 +598,18 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { } void NativeWindowMac::Close() { - // When this is a sheet showing, performClose won't work. - if (is_modal() && parent() && IsVisible()) { - [parent()->GetNativeWindow().GetNativeNSWindow() endSheet:window_]; - // Manually emit close event (not triggered from close fn) - NotifyWindowCloseButtonClicked(); - CloseImmediately(); - return; - } - if (!IsClosable()) { WindowList::WindowCloseCancelled(this); return; } [window_ performClose:nil]; + + // Closing a sheet doesn't trigger windowShouldClose, + // so we need to manually call it ourselves here. + if (is_modal() && parent() && IsVisible()) { + NotifyWindowCloseButtonClicked(); + } } void NativeWindowMac::CloseImmediately() { @@ -578,9 +646,9 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { void NativeWindowMac::Show() { if (is_modal() && parent()) { + NSWindow* window = parent()->GetNativeWindow().GetNativeNSWindow(); if ([window_ sheetParent] == nil) - [parent()->GetNativeWindow().GetNativeNSWindow() - beginSheet:window_ + [window beginSheet:window_ completionHandler:^(NSModalResponse){ }]; return; @@ -628,6 +696,16 @@ 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::RepositionTrafficLights, GetWeakPtr())); +} + bool NativeWindowMac::IsEnabled() { return [window_ attachedSheet] == nil; } @@ -817,7 +895,7 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { void NativeWindowMac::PreviewFile(const std::string& path, const std::string& display_name) { - preview_item_.reset([[AtomPreviewItem alloc] + preview_item_.reset([[ElectronPreviewItem alloc] initWithURL:[NSURL fileURLWithPath:base::SysUTF8ToNSString(path)] title:base::SysUTF8ToNSString(display_name)]); [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil]; @@ -949,6 +1027,9 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { void NativeWindowMac::SetTitle(const std::string& title) { [window_ setTitle:base::SysUTF8ToNSString(title)]; + if (title_bar_style_ == TitleBarStyle::HIDDEN) { + RepositionTrafficLights(); + } } std::string NativeWindowMac::GetTitle() { @@ -1238,7 +1319,7 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { NSRect frame = NSMakeRect(0.0f, 0.0f, dock_tile.size.width, 15.0); NSProgressIndicator* progress_indicator = - [[[AtomProgressBar alloc] initWithFrame:frame] autorelease]; + [[[ElectronProgressBar alloc] initWithFrame:frame] autorelease]; [progress_indicator setStyle:NSProgressIndicatorBarStyle]; [progress_indicator setIndeterminate:NO]; [progress_indicator setBezeled:YES]; @@ -1375,8 +1456,7 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { relativeTo:nil]; } - std::string dep_warn = - " has been deprecated and will be removed in a future version of macOS."; + std::string dep_warn = " has been deprecated and removed as of macOS 10.15."; node::Environment* env = node::Environment::GetCurrent(v8::Isolate::GetCurrent()); @@ -1399,61 +1479,44 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { } if (@available(macOS 10.11, *)) { - // TODO(codebytere): Use NSVisualEffectMaterial* constants directly once - // they are available in the minimum SDK version if (type == "selection") { - // NSVisualEffectMaterialSelection - vibrancyType = static_cast(4); + vibrancyType = NSVisualEffectMaterialSelection; } else if (type == "menu") { - // NSVisualEffectMaterialMenu - vibrancyType = static_cast(5); + vibrancyType = NSVisualEffectMaterialMenu; } else if (type == "popover") { - // NSVisualEffectMaterialPopover - vibrancyType = static_cast(6); + vibrancyType = NSVisualEffectMaterialPopover; } else if (type == "sidebar") { - // NSVisualEffectMaterialSidebar - vibrancyType = static_cast(7); + vibrancyType = NSVisualEffectMaterialSidebar; } else if (type == "medium-light") { - // NSVisualEffectMaterialMediumLight EmitDeprecationWarning( env, "NSVisualEffectMaterialMediumLight" + dep_warn, "electron"); - vibrancyType = static_cast(8); + vibrancyType = NSVisualEffectMaterialMediumLight; } else if (type == "ultra-dark") { - // NSVisualEffectMaterialUltraDark EmitDeprecationWarning(env, "NSVisualEffectMaterialUltraDark" + dep_warn, "electron"); - vibrancyType = static_cast(9); + vibrancyType = NSVisualEffectMaterialUltraDark; } } if (@available(macOS 10.14, *)) { if (type == "header") { - // NSVisualEffectMaterialHeaderView - vibrancyType = static_cast(10); + vibrancyType = NSVisualEffectMaterialHeaderView; } else if (type == "sheet") { - // NSVisualEffectMaterialSheet - vibrancyType = static_cast(11); + vibrancyType = NSVisualEffectMaterialSheet; } else if (type == "window") { - // NSVisualEffectMaterialWindowBackground - vibrancyType = static_cast(12); + vibrancyType = NSVisualEffectMaterialWindowBackground; } else if (type == "hud") { - // NSVisualEffectMaterialHUDWindow - vibrancyType = static_cast(13); + vibrancyType = NSVisualEffectMaterialHUDWindow; } else if (type == "fullscreen-ui") { - // NSVisualEffectMaterialFullScreenUI - vibrancyType = static_cast(16); + vibrancyType = NSVisualEffectMaterialFullScreenUI; } else if (type == "tooltip") { - // NSVisualEffectMaterialToolTip - vibrancyType = static_cast(17); + vibrancyType = NSVisualEffectMaterialToolTip; } else if (type == "content") { - // NSVisualEffectMaterialContentBackground - vibrancyType = static_cast(18); + vibrancyType = NSVisualEffectMaterialContentBackground; } else if (type == "under-window") { - // NSVisualEffectMaterialUnderWindowBackground - vibrancyType = static_cast(21); + vibrancyType = NSVisualEffectMaterialUnderWindowBackground; } else if (type == "under-page") { - // NSVisualEffectMaterialUnderPageBackground - vibrancyType = static_cast(22); + vibrancyType = NSVisualEffectMaterialUnderPageBackground; } } @@ -1461,10 +1524,19 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { [effect_view setMaterial:vibrancyType]; } +void NativeWindowMac::SetTrafficLightPosition(const gfx::Point& position) { + traffic_light_position_ = position; + RepositionTrafficLights(); +} + +gfx::Point NativeWindowMac::GetTrafficLightPosition() const { + return traffic_light_position_; +} + void NativeWindowMac::SetTouchBar( const std::vector& items) { if (@available(macOS 10.12.2, *)) { - touch_bar_.reset([[AtomTouchBar alloc] + touch_bar_.reset([[ElectronTouchBar alloc] initWithDelegate:window_delegate_.get() window:this settings:items]); diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index e3529ad46fada..00da49a7362a9 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -19,7 +19,7 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/desktop_media_id.h" #include "native_mate/dictionary.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/native_browser_view_views.h" #include "shell/browser/ui/inspectable_web_contents.h" #include "shell/browser/ui/inspectable_web_contents_view.h" @@ -27,7 +27,7 @@ #include "shell/browser/web_contents_preferences.h" #include "shell/browser/web_view_manager.h" #include "shell/browser/window_list.h" -#include "shell/common/atom_constants.h" +#include "shell/common/electron_constants.h" #include "shell/common/native_mate_converters/image_converter.h" #include "shell/common/options_switches.h" #include "ui/aura/window_tree_host.h" @@ -45,8 +45,8 @@ #if defined(USE_X11) #include "base/strings/string_util.h" -#include "chrome/browser/ui/libgtkui/unity_service.h" #include "shell/browser/browser.h" +#include "shell/browser/linux/unity_service.h" #include "shell/browser/ui/views/frameless_view.h" #include "shell/browser/ui/views/global_menu_bar_x11.h" #include "shell/browser/ui/views/native_frame_view.h" @@ -59,7 +59,7 @@ #include "ui/views/window/native_frame_view.h" #elif defined(OS_WIN) #include "shell/browser/ui/views/win_frame_view.h" -#include "shell/browser/ui/win/atom_desktop_native_widget_aura.h" +#include "shell/browser/ui/win/electron_desktop_native_widget_aura.h" #include "skia/ext/skia_utils_win.h" #include "ui/base/win/shell.h" #include "ui/display/screen.h" @@ -174,12 +174,12 @@ NativeWindowViews::NativeWindowViews(const mate::Dictionary& options, params.remove_standard_frame = !has_frame(); if (transparent()) - params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; + params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent; // The given window is most likely not rectangular since it uses // transparency and has no standard frame, don't show a shadow for it. if (transparent() && !has_frame()) - params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE; + params.shadow_type = views::Widget::InitParams::ShadowType::kNone; bool focusable; if (options.Get(options::kFocusable, &focusable) && !focusable) @@ -189,7 +189,7 @@ NativeWindowViews::NativeWindowViews(const mate::Dictionary& options, if (parent) params.parent = parent->GetNativeWindow(); - params.native_widget = new AtomDesktopNativeWidgetAura(this); + params.native_widget = new ElectronDesktopNativeWidgetAura(this); #elif defined(USE_X11) std::string name = Browser::Get()->GetName(); // Set WM_WINDOW_ROLE. @@ -471,8 +471,9 @@ void NativeWindowViews::SetEnabledInternal(bool enable) { #if defined(OS_WIN) ::EnableWindow(GetAcceleratedWidget(), enable); #elif defined(USE_X11) - views::DesktopWindowTreeHostX11* tree_host = - views::DesktopWindowTreeHostX11::GetHostForXID(GetAcceleratedWidget()); + views::DesktopWindowTreeHostLinux* tree_host = + views::DesktopWindowTreeHostLinux::GetHostForWidget( + GetAcceleratedWidget()); if (enable) { tree_host->RemoveEventRewriter(event_disabler_.get()); event_disabler_.reset(); @@ -975,6 +976,7 @@ void NativeWindowViews::SetContentProtection(bool enable) { } void NativeWindowViews::SetFocusable(bool focusable) { + widget()->widget_delegate()->SetCanActivate(focusable); #if defined(OS_WIN) LONG ex_style = ::GetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE); if (focusable) @@ -987,22 +989,24 @@ void NativeWindowViews::SetFocusable(bool focusable) { #endif } -void NativeWindowViews::SetMenu(AtomMenuModel* menu_model) { +void NativeWindowViews::SetMenu(ElectronMenuModel* menu_model) { #if defined(USE_X11) - if (menu_model == nullptr) { + // Remove global menu bar. + if (global_menu_bar_ && menu_model == nullptr) { global_menu_bar_.reset(); root_view_->UnregisterAcceleratorsWithFocusManager(); return; } - if (!global_menu_bar_ && ShouldUseGlobalMenuBar()) - global_menu_bar_ = std::make_unique(this); - // Use global application menu bar when possible. - if (global_menu_bar_ && global_menu_bar_->IsServerStarted()) { - root_view_->RegisterAcceleratorsWithFocusManager(menu_model); - global_menu_bar_->SetMenu(menu_model); - return; + if (ShouldUseGlobalMenuBar()) { + if (!global_menu_bar_) + global_menu_bar_ = std::make_unique(this); + if (global_menu_bar_->IsServerStarted()) { + root_view_->RegisterAcceleratorsWithFocusManager(menu_model); + global_menu_bar_->SetMenu(menu_model); + return; + } } #endif @@ -1265,8 +1269,8 @@ void NativeWindowViews::SetIcon(HICON window_icon, HICON app_icon) { } #elif defined(USE_X11) void NativeWindowViews::SetIcon(const gfx::ImageSkia& icon) { - auto* tree_host = static_cast( - views::DesktopWindowTreeHostX11::GetHostForXID(GetAcceleratedWidget())); + auto* tree_host = views::DesktopWindowTreeHostLinux::GetHostForWidget( + GetAcceleratedWidget()); tree_host->SetWindowIcons(icon, icon); } #endif diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index 8b8d973916e21..86406d12b0638 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -105,7 +105,7 @@ class NativeWindowViews : public NativeWindow, void SetIgnoreMouseEvents(bool ignore, bool forward) override; void SetContentProtection(bool enable) override; void SetFocusable(bool focusable) override; - void SetMenu(AtomMenuModel* menu_model) override; + void SetMenu(ElectronMenuModel* menu_model) override; void AddBrowserView(NativeBrowserView* browser_view) override; void RemoveBrowserView(NativeBrowserView* browser_view) override; void SetParentWindow(NativeWindow* parent) override; @@ -272,6 +272,9 @@ class NativeWindowViews : public NativeWindow, // Set to true if the window is always on top and behind the task bar. bool behind_task_bar_ = false; + + // Whether to block Chromium from handling window messages. + bool block_chromium_message_handler_ = false; #endif // Handles unhandled keyboard messages coming back from the renderer process. diff --git a/shell/browser/native_window_views_win.cc b/shell/browser/native_window_views_win.cc index 3575cbd2b303a..5003fbc3c70a6 100644 --- a/shell/browser/native_window_views_win.cc +++ b/shell/browser/native_window_views_win.cc @@ -9,7 +9,7 @@ #include "shell/browser/browser.h" #include "shell/browser/native_window_views.h" #include "shell/browser/ui/views/root_view.h" -#include "shell/common/atom_constants.h" +#include "shell/common/electron_constants.h" #include "ui/base/win/accessibility_misc_utils.h" #include "ui/display/display.h" #include "ui/display/win/screen_win.h" @@ -143,162 +143,28 @@ bool IsScreenReaderActive() { return screenReader && UiaClientsAreListening(); } -// We use "enum" instead of "enum class" because we need to do bitwise compare. -enum AppbarAutohideEdge { - TOP = 1 << 0, - LEFT = 1 << 1, - BOTTOM = 1 << 2, - RIGHT = 1 << 3, -}; - -// The thickness of an auto-hide taskbar in pixel. -constexpr int kAutoHideTaskbarThicknessPx = 2; - -// Code is copied from chrome_views_delegate_win.cc. -bool MonitorHasAutohideTaskbarForEdge(UINT edge, HMONITOR monitor) { - APPBARDATA taskbar_data = {sizeof(APPBARDATA), NULL, 0, edge}; - taskbar_data.hWnd = ::GetForegroundWindow(); - - // MSDN documents an ABM_GETAUTOHIDEBAREX, which supposedly takes a monitor - // rect and returns autohide bars on that monitor. This sounds like a good - // idea for multi-monitor systems. Unfortunately, it appears to not work at - // least some of the time (erroneously returning NULL) and there's almost no - // online documentation or other sample code using it that suggests ways to - // address this problem. We do the following:- - // 1. Use the ABM_GETAUTOHIDEBAR message. If it works, i.e. returns a valid - // window we are done. - // 2. If the ABM_GETAUTOHIDEBAR message does not work we query the auto hide - // state of the taskbar and then retrieve its position. That call returns - // the edge on which the taskbar is present. If it matches the edge we - // are looking for, we are done. - // NOTE: This call spins a nested run loop. - HWND taskbar = reinterpret_cast( - SHAppBarMessage(ABM_GETAUTOHIDEBAR, &taskbar_data)); - if (!::IsWindow(taskbar)) { - APPBARDATA taskbar_data = {sizeof(APPBARDATA), 0, 0, 0}; - unsigned int taskbar_state = SHAppBarMessage(ABM_GETSTATE, &taskbar_data); - if (!(taskbar_state & ABS_AUTOHIDE)) - return false; - - taskbar_data.hWnd = ::FindWindow(L"Shell_TrayWnd", NULL); - if (!::IsWindow(taskbar_data.hWnd)) - return false; - - SHAppBarMessage(ABM_GETTASKBARPOS, &taskbar_data); - if (taskbar_data.uEdge == edge) - taskbar = taskbar_data.hWnd; - } - - // There is a potential race condition here: - // 1. A maximized chrome window is fullscreened. - // 2. It is switched back to maximized. - // 3. In the process the window gets a WM_NCCACLSIZE message which calls us to - // get the autohide state. - // 4. The worker thread is invoked. It calls the API to get the autohide - // state. On Windows versions earlier than Windows 7, taskbars could - // easily be always on top or not. - // This meant that we only want to look for taskbars which have the topmost - // bit set. However this causes problems in cases where the window on the - // main thread is still in the process of switching away from fullscreen. - // In this case the taskbar might not yet have the topmost bit set. - // 5. The main thread resumes and does not leave space for the taskbar and - // hence it does not pop when hovered. - // - // To address point 4 above, it is best to not check for the WS_EX_TOPMOST - // window style on the taskbar, as starting from Windows 7, the topmost - // style is always set. We don't support XP and Vista anymore. - if (::IsWindow(taskbar)) { - if (MonitorFromWindow(taskbar, MONITOR_DEFAULTTONEAREST) == monitor) - return true; - // In some cases like when the autohide taskbar is on the left of the - // secondary monitor, the MonitorFromWindow call above fails to return the - // correct monitor the taskbar is on. We fallback to MonitorFromPoint for - // the cursor position in that case, which seems to work well. - POINT cursor_pos = {0}; - GetCursorPos(&cursor_pos); - if (MonitorFromPoint(cursor_pos, MONITOR_DEFAULTTONEAREST) == monitor) - return true; - } - return false; -} - -int GetAppbarAutohideEdges(HWND hwnd) { - HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL); - if (!monitor) - return 0; - - int edges = 0; - if (MonitorHasAutohideTaskbarForEdge(ABE_LEFT, monitor)) - edges |= AppbarAutohideEdge::LEFT; - if (MonitorHasAutohideTaskbarForEdge(ABE_TOP, monitor)) - edges |= AppbarAutohideEdge::TOP; - if (MonitorHasAutohideTaskbarForEdge(ABE_RIGHT, monitor)) - edges |= AppbarAutohideEdge::RIGHT; - if (MonitorHasAutohideTaskbarForEdge(ABE_BOTTOM, monitor)) - edges |= AppbarAutohideEdge::BOTTOM; - return edges; -} - -void TriggerNCCalcSize(HWND hwnd) { - RECT rcClient; - ::GetWindowRect(hwnd, &rcClient); - - ::SetWindowPos(hwnd, NULL, rcClient.left, rcClient.top, - rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, - SWP_FRAMECHANGED); -} - } // namespace std::set NativeWindowViews::forwarding_windows_; HHOOK NativeWindowViews::mouse_hook_ = NULL; void NativeWindowViews::Maximize() { - int autohide_edges = 0; - if (!has_frame()) - autohide_edges = GetAppbarAutohideEdges(GetAcceleratedWidget()); - // Only use Maximize() when: // 1. window has WS_THICKFRAME style; // 2. and window is not frameless when there is autohide taskbar. - if ((::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_THICKFRAME) && - (has_frame() || autohide_edges == 0)) { + if (::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_THICKFRAME) { if (IsVisible()) widget()->Maximize(); else widget()->native_widget_private()->Show(ui::SHOW_STATE_MAXIMIZED, gfx::Rect()); return; + } else { + restore_bounds_ = GetBounds(); + auto display = + display::Screen::GetScreen()->GetDisplayNearestPoint(GetPosition()); + SetBounds(display.work_area(), false); } - - gfx::Insets insets; - if (!has_frame()) { - // When taskbar is autohide, we need to leave some space so the window - // isn't treated as a "fullscreen app", which would cause the taskbars - // to disappear. - // - // This trick comes from hwnd_message_handler.cc. While Chromium already - // does this for normal window, somehow it is not applying the trick when - // using frameless window, and we have to do it ourselves. - float scale_factor = - display::win::ScreenWin::GetScaleFactorForHWND(GetAcceleratedWidget()); - int thickness = std::ceil(kAutoHideTaskbarThicknessPx / scale_factor); - if (autohide_edges & AppbarAutohideEdge::LEFT) - insets.set_left(-thickness); - if (autohide_edges & AppbarAutohideEdge::TOP) - insets.set_top(-thickness); - if (autohide_edges & AppbarAutohideEdge::RIGHT) - insets.set_right(thickness); - if (autohide_edges & AppbarAutohideEdge::BOTTOM) - insets.set_bottom(thickness); - } - - restore_bounds_ = GetBounds(); - auto display = - display::Screen::GetScreen()->GetDisplayNearestPoint(GetPosition()); - gfx::Rect bounds = display.work_area(); - bounds.Inset(insets); - SetBounds(bounds, false); } bool NativeWindowViews::ExecuteWindowsCommand(int command_id) { @@ -314,6 +180,14 @@ bool NativeWindowViews::PreHandleMSG(UINT message, LRESULT* result) { NotifyWindowMessage(message, w_param, l_param); + // See code below for why blocking Chromium from handling messages. + if (block_chromium_message_handler_) { + // Handle the message with default proc. + *result = DefWindowProc(GetAcceleratedWidget(), message, w_param, l_param); + // Tell Chromium to ignore this message. + return true; + } + switch (message) { // Screen readers send WM_GETOBJECT in order to get the accessibility // object, so take this opportunity to push Chromium into accessible @@ -345,74 +219,34 @@ bool NativeWindowViews::PreHandleMSG(UINT message, return false; } case WM_GETMINMAXINFO: { - // We need to handle GETMINMAXINFO ourselves because chromium tries to - // get the scale factor of the window during it's version of this handler - // based on the window position, which is invalid at this point. The - // previous method of calling SetWindowPlacement fixed the window - // position for the scale factor calculation but broke other things. - MINMAXINFO* info = reinterpret_cast(l_param); - - display::Display display = - display::Screen::GetScreen()->GetDisplayNearestPoint( - last_normal_placement_bounds_.origin()); - - gfx::Size min_size = gfx::ScaleToCeiledSize( - widget()->GetMinimumSize(), display.device_scale_factor()); - gfx::Size max_size = gfx::ScaleToCeiledSize( - widget()->GetMaximumSize(), display.device_scale_factor()); - - info->ptMinTrackSize.x = min_size.width(); - info->ptMinTrackSize.y = min_size.height(); - if (max_size.width() || max_size.height()) { - if (!max_size.width()) - max_size.set_width(GetSystemMetrics(SM_CXMAXTRACK)); - if (!max_size.height()) - max_size.set_height(GetSystemMetrics(SM_CYMAXTRACK)); - info->ptMaxTrackSize.x = max_size.width(); - info->ptMaxTrackSize.y = max_size.height(); - } - - *result = 1; - return true; - } - case WM_NCCALCSIZE: { - if (!has_frame() && w_param == TRUE) { - NCCALCSIZE_PARAMS* params = - reinterpret_cast(l_param); - RECT PROPOSED = params->rgrc[0]; - RECT BEFORE = params->rgrc[1]; - - // We need to call the default to have cascade and tile windows - // working - // (https://github.com/rossy/borderless-window/blob/master/borderless-window.c#L239), - // but we need to provide the proposed original value as suggested in - // https://blogs.msdn.microsoft.com/wpfsdk/2008/09/08/custom-window-chrome-in-wpf/ - DefWindowProcW(GetAcceleratedWidget(), WM_NCCALCSIZE, w_param, l_param); - - // When fullscreen the window has no border - int border = 0; - if (!IsFullscreen()) { - // When not fullscreen calculate the border size - border = GetSystemMetrics(SM_CXFRAME) + - GetSystemMetrics(SM_CXPADDEDBORDER); - if (!thick_frame_) { - border -= GetSystemMetrics(SM_CXBORDER); - } - } - - if (last_window_state_ == ui::SHOW_STATE_MAXIMIZED) { - // Position the top of the frame offset from where windows thinks by - // exactly the border amount. When fullscreen this is 0. - params->rgrc[0].top = PROPOSED.top + border; - } else { - params->rgrc[0] = PROPOSED; - params->rgrc[1] = BEFORE; - } + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); - return true; - } else { - return false; + // We do this to work around a Windows bug, where the minimized Window + // would report that the closest display to it is not the one that it was + // previously on (but the leftmost one instead). We restore the position + // of the window during the restore operation, this way chromium can + // use the proper display to calculate the scale factor to use. + if (!last_normal_placement_bounds_.IsEmpty() && + GetWindowPlacement(GetAcceleratedWidget(), &wp)) { + wp.rcNormalPosition = last_normal_placement_bounds_.ToRECT(); + + // When calling SetWindowPlacement, Chromium would do window messages + // handling. But since we are already in PreHandleMSG this would cause + // crash in Chromium under some cases. + // + // We work around the crash by prevent Chromium from handling window + // messages until the SetWindowPlacement call is done. + // + // See https://github.com/electron/electron/issues/21614 for more. + block_chromium_message_handler_ = true; + SetWindowPlacement(GetAcceleratedWidget(), &wp); + block_chromium_message_handler_ = false; + + last_normal_placement_bounds_ = gfx::Rect(); } + + return false; } case WM_COMMAND: // Handle thumbar button click message. @@ -478,11 +312,6 @@ void NativeWindowViews::HandleSizeEvent(WPARAM w_param, LPARAM l_param) { switch (w_param) { case SIZE_MAXIMIZED: { last_window_state_ = ui::SHOW_STATE_MAXIMIZED; - - if (!has_frame()) { - TriggerNCCalcSize(GetAcceleratedWidget()); - } - NotifyWindowMaximize(); break; } @@ -503,11 +332,6 @@ void NativeWindowViews::HandleSizeEvent(WPARAM w_param, LPARAM l_param) { case ui::SHOW_STATE_MAXIMIZED: last_window_state_ = ui::SHOW_STATE_NORMAL; NotifyWindowUnmaximize(); - - if (!has_frame()) { - TriggerNCCalcSize(GetAcceleratedWidget()); - } - break; case ui::SHOW_STATE_MINIMIZED: if (IsFullscreen()) { diff --git a/shell/browser/net/asar/asar_url_loader.cc b/shell/browser/net/asar/asar_url_loader.cc index 88f8825c4ec17..6196d5cfedc52 100644 --- a/shell/browser/net/asar/asar_url_loader.cc +++ b/shell/browser/net/asar/asar_url_loader.cc @@ -12,7 +12,8 @@ #include "base/strings/stringprintf.h" #include "base/task/post_task.h" #include "content/public/browser/file_url_loader.h" -#include "mojo/public/cpp/bindings/strong_binding.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/system/data_pipe_producer.h" #include "mojo/public/cpp/system/file_data_source.h" #include "net/base/filename_util.h" @@ -59,13 +60,13 @@ class AsarURLLoader : public network::mojom::URLLoader { static void CreateAndStart( const network::ResourceRequest& request, network::mojom::URLLoaderRequest loader, - network::mojom::URLLoaderClientPtrInfo client_info, + mojo::PendingRemote client, scoped_refptr extra_response_headers) { // Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr // bindings are alive - essentially until either the client gives up or all // file data has been sent to it. auto* asar_url_loader = new AsarURLLoader; - asar_url_loader->Start(request, std::move(loader), std::move(client_info), + asar_url_loader->Start(request, std::move(loader), std::move(client), std::move(extra_response_headers)); } @@ -79,26 +80,25 @@ class AsarURLLoader : public network::mojom::URLLoader { void ResumeReadingBodyFromNet() override {} private: - AsarURLLoader() : binding_(this) {} + AsarURLLoader() {} ~AsarURLLoader() override = default; void Start(const network::ResourceRequest& request, - network::mojom::URLLoaderRequest loader, - network::mojom::URLLoaderClientPtrInfo client_info, + mojo::PendingReceiver loader, + mojo::PendingRemote client, scoped_refptr extra_response_headers) { network::ResourceResponseHead head; head.request_start = base::TimeTicks::Now(); head.response_start = base::TimeTicks::Now(); head.headers = extra_response_headers; - binding_.Bind(std::move(loader)); - binding_.set_connection_error_handler(base::BindOnce( - &AsarURLLoader::OnConnectionError, base::Unretained(this))); - - client_.Bind(std::move(client_info)); base::FilePath path; if (!net::FileURLToFilePath(request.url, &path)) { - OnClientComplete(net::ERR_FAILED); + mojo::Remote client_remote( + std::move(client)); + client_remote->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_FAILED)); + MaybeDeleteSelf(); return; } @@ -106,12 +106,17 @@ class AsarURLLoader : public network::mojom::URLLoader { base::FilePath asar_path, relative_path; if (!GetAsarArchivePath(path, &asar_path, &relative_path)) { content::CreateFileURLLoader(request, std::move(loader), - std::move(client_), nullptr, false, + std::move(client), nullptr, false, extra_response_headers); MaybeDeleteSelf(); return; } + client_.Bind(std::move(client)); + receiver_.Bind(std::move(loader)); + receiver_.set_disconnect_handler(base::BindOnce( + &AsarURLLoader::OnConnectionError, base::Unretained(this))); + // Parse asar archive. std::shared_ptr archive = GetOrCreateAsarArchive(asar_path); Archive::FileInfo info; @@ -245,7 +250,7 @@ class AsarURLLoader : public network::mojom::URLLoader { } void OnConnectionError() { - binding_.Close(); + receiver_.reset(); MaybeDeleteSelf(); } @@ -256,7 +261,7 @@ class AsarURLLoader : public network::mojom::URLLoader { } void MaybeDeleteSelf() { - if (!binding_.is_bound() && !client_.is_bound()) + if (!receiver_.is_bound() && !client_.is_bound()) delete this; } @@ -279,8 +284,8 @@ class AsarURLLoader : public network::mojom::URLLoader { } std::unique_ptr data_producer_; - mojo::Binding binding_; - network::mojom::URLLoaderClientPtr client_; + mojo::Receiver receiver_{this}; + mojo::Remote client_; // In case of successful loads, this holds the total number of bytes written // to the response (this may be smaller than the total size of the file when @@ -297,15 +302,15 @@ class AsarURLLoader : public network::mojom::URLLoader { void CreateAsarURLLoader( const network::ResourceRequest& request, network::mojom::URLLoaderRequest loader, - network::mojom::URLLoaderClientPtr client, + mojo::PendingRemote client, scoped_refptr extra_response_headers) { auto task_runner = base::CreateSequencedTaskRunner( {base::ThreadPool(), base::MayBlock(), base::TaskPriority::USER_VISIBLE, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); task_runner->PostTask( - FROM_HERE, base::BindOnce(&AsarURLLoader::CreateAndStart, request, - std::move(loader), client.PassInterface(), - std::move(extra_response_headers))); + FROM_HERE, + base::BindOnce(&AsarURLLoader::CreateAndStart, request, std::move(loader), + std::move(client), std::move(extra_response_headers))); } } // namespace asar diff --git a/shell/browser/net/asar/asar_url_loader.h b/shell/browser/net/asar/asar_url_loader.h index be9e2b63167f8..e7c540cabe089 100644 --- a/shell/browser/net/asar/asar_url_loader.h +++ b/shell/browser/net/asar/asar_url_loader.h @@ -5,6 +5,7 @@ #ifndef SHELL_BROWSER_NET_ASAR_ASAR_URL_LOADER_H_ #define SHELL_BROWSER_NET_ASAR_ASAR_URL_LOADER_H_ +#include "mojo/public/cpp/bindings/pending_remote.h" #include "services/network/public/mojom/url_loader.mojom.h" namespace asar { @@ -12,7 +13,7 @@ namespace asar { void CreateAsarURLLoader( const network::ResourceRequest& request, network::mojom::URLLoaderRequest loader, - network::mojom::URLLoaderClientPtr client, + mojo::PendingRemote client, scoped_refptr extra_response_headers); } // namespace asar diff --git a/shell/browser/net/atom_url_loader_factory.h b/shell/browser/net/atom_url_loader_factory.h deleted file mode 100644 index 0038b2f3d1744..0000000000000 --- a/shell/browser/net/atom_url_loader_factory.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) 2019 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef SHELL_BROWSER_NET_ATOM_URL_LOADER_FACTORY_H_ -#define SHELL_BROWSER_NET_ATOM_URL_LOADER_FACTORY_H_ - -#include -#include -#include - -#include "mojo/public/cpp/bindings/binding_set.h" -#include "net/url_request/url_request_job_factory.h" -#include "services/network/public/cpp/resource_response.h" -#include "services/network/public/mojom/url_loader_factory.mojom.h" -#include "shell/common/gin_helper/dictionary.h" - -namespace electron { - -// Old Protocol API can only serve one type of response for one scheme. -enum class ProtocolType { - kBuffer, - kString, - kFile, - kHttp, - kStream, - kFree, // special type for returning arbitrary type of response. -}; - -using StartLoadingCallback = base::OnceCallback; -using ProtocolHandler = - base::Callback; - -// scheme => (type, handler). -using HandlersMap = - std::map>; - -// Implementation of URLLoaderFactory. -class AtomURLLoaderFactory : public network::mojom::URLLoaderFactory { - public: - AtomURLLoaderFactory(ProtocolType type, const ProtocolHandler& handler); - ~AtomURLLoaderFactory() override; - - // network::mojom::URLLoaderFactory: - void CreateLoaderAndStart(network::mojom::URLLoaderRequest loader, - int32_t routing_id, - int32_t request_id, - uint32_t options, - const network::ResourceRequest& request, - network::mojom::URLLoaderClientPtr client, - const net::MutableNetworkTrafficAnnotationTag& - traffic_annotation) override; - void Clone(network::mojom::URLLoaderFactoryRequest request) override; - - static void StartLoading( - network::mojom::URLLoaderRequest loader, - int32_t routing_id, - int32_t request_id, - uint32_t options, - const network::ResourceRequest& request, - network::mojom::URLLoaderClientPtr client, - const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, - network::mojom::URLLoaderFactory* proxy_factory, - ProtocolType type, - gin::Arguments* args); - - private: - static void StartLoadingBuffer(network::mojom::URLLoaderClientPtr client, - network::ResourceResponseHead head, - const gin_helper::Dictionary& dict); - static void StartLoadingString(network::mojom::URLLoaderClientPtr client, - network::ResourceResponseHead head, - const gin_helper::Dictionary& dict, - v8::Isolate* isolate, - v8::Local response); - static void StartLoadingFile(network::mojom::URLLoaderRequest loader, - network::ResourceRequest request, - network::mojom::URLLoaderClientPtr client, - network::ResourceResponseHead head, - const gin_helper::Dictionary& dict, - v8::Isolate* isolate, - v8::Local response); - static void StartLoadingHttp( - network::mojom::URLLoaderRequest loader, - const network::ResourceRequest& original_request, - network::mojom::URLLoaderClientPtr client, - const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, - const gin_helper::Dictionary& dict); - static void StartLoadingStream(network::mojom::URLLoaderRequest loader, - network::mojom::URLLoaderClientPtr client, - network::ResourceResponseHead head, - const gin_helper::Dictionary& dict); - - // Helper to send string as response. - static void SendContents(network::mojom::URLLoaderClientPtr client, - network::ResourceResponseHead head, - std::string data); - - // TODO(zcbenz): This comes from extensions/browser/extension_protocols.cc - // but I don't know what it actually does, find out the meanings of |Clone| - // and |bindings_| and add comments for them. - mojo::BindingSet bindings_; - - ProtocolType type_; - ProtocolHandler handler_; - - DISALLOW_COPY_AND_ASSIGN(AtomURLLoaderFactory); -}; - -} // namespace electron - -#endif // SHELL_BROWSER_NET_ATOM_URL_LOADER_FACTORY_H_ diff --git a/shell/browser/net/cookie_details.h b/shell/browser/net/cookie_details.h deleted file mode 100644 index 50a5b2969d259..0000000000000 --- a/shell/browser/net/cookie_details.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2017 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef SHELL_BROWSER_NET_COOKIE_DETAILS_H_ -#define SHELL_BROWSER_NET_COOKIE_DETAILS_H_ - -#include "base/macros.h" -#include "services/network/public/mojom/cookie_manager.mojom.h" - -namespace net { -class CanonicalCookie; -} - -namespace electron { - -struct CookieDetails { - public: - CookieDetails(const net::CanonicalCookie* cookie_copy, - bool is_removed, - network::mojom::CookieChangeCause cause) - : cookie(cookie_copy), removed(is_removed), cause(cause) {} - - const net::CanonicalCookie* cookie; - bool removed; - network::mojom::CookieChangeCause cause; -}; - -} // namespace electron - -#endif // SHELL_BROWSER_NET_COOKIE_DETAILS_H_ diff --git a/shell/browser/net/atom_url_loader_factory.cc b/shell/browser/net/electron_url_loader_factory.cc similarity index 76% rename from shell/browser/net/atom_url_loader_factory.cc rename to shell/browser/net/electron_url_loader_factory.cc index 7898c8ea5b553..7e43f0cf359ee 100644 --- a/shell/browser/net/atom_url_loader_factory.cc +++ b/shell/browser/net/electron_url_loader_factory.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/net/atom_url_loader_factory.h" +#include "shell/browser/net/electron_url_loader_factory.h" #include #include @@ -11,18 +11,19 @@ #include "base/guid.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" +#include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/system/data_pipe_producer.h" #include "mojo/public/cpp/system/string_data_source.h" #include "net/base/filename_util.h" #include "net/http/http_status_code.h" #include "services/network/public/cpp/url_loader_completion_status.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" -#include "shell/browser/api/atom_api_session.h" -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/api/electron_api_session.h" +#include "shell/browser/electron_browser_context.h" #include "shell/browser/net/asar/asar_url_loader.h" #include "shell/browser/net/node_stream_loader.h" #include "shell/browser/net/url_pipe_loader.h" -#include "shell/common/atom_constants.h" +#include "shell/common/electron_constants.h" #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_converters/gurl_converter.h" #include "shell/common/gin_converters/net_converter.h" @@ -140,7 +141,7 @@ network::ResourceResponseHead ToResponseHead( // Helper to write string to pipe. struct WriteData { - network::mojom::URLLoaderClientPtr client; + mojo::Remote client; std::string data; std::unique_ptr producer; }; @@ -160,41 +161,42 @@ void OnWrite(std::unique_ptr write_data, MojoResult result) { } // namespace -AtomURLLoaderFactory::AtomURLLoaderFactory(ProtocolType type, - const ProtocolHandler& handler) +ElectronURLLoaderFactory::ElectronURLLoaderFactory( + ProtocolType type, + const ProtocolHandler& handler) : type_(type), handler_(handler) {} -AtomURLLoaderFactory::~AtomURLLoaderFactory() = default; +ElectronURLLoaderFactory::~ElectronURLLoaderFactory() = default; -void AtomURLLoaderFactory::CreateLoaderAndStart( - network::mojom::URLLoaderRequest loader, +void ElectronURLLoaderFactory::CreateLoaderAndStart( + mojo::PendingReceiver loader, int32_t routing_id, int32_t request_id, uint32_t options, const network::ResourceRequest& request, - network::mojom::URLLoaderClientPtr client, + mojo::PendingRemote client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); handler_.Run( request, - base::BindOnce(&AtomURLLoaderFactory::StartLoading, std::move(loader), + base::BindOnce(&ElectronURLLoaderFactory::StartLoading, std::move(loader), routing_id, request_id, options, request, std::move(client), traffic_annotation, nullptr, type_)); } -void AtomURLLoaderFactory::Clone( - network::mojom::URLLoaderFactoryRequest request) { - bindings_.AddBinding(this, std::move(request)); +void ElectronURLLoaderFactory::Clone( + mojo::PendingReceiver receiver) { + receivers_.Add(this, std::move(receiver)); } // static -void AtomURLLoaderFactory::StartLoading( - network::mojom::URLLoaderRequest loader, +void ElectronURLLoaderFactory::StartLoading( + mojo::PendingReceiver loader, int32_t routing_id, int32_t request_id, uint32_t options, const network::ResourceRequest& request, - network::mojom::URLLoaderClientPtr client, + mojo::PendingRemote client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, network::mojom::URLLoaderFactory* proxy_factory, ProtocolType type, @@ -205,7 +207,9 @@ void AtomURLLoaderFactory::StartLoading( // passed, to keep compatibility with old code. v8::Local response; if (!args->GetNext(&response)) { - client->OnComplete( + mojo::Remote client_remote( + std::move(client)); + client_remote->OnComplete( network::URLLoaderCompletionStatus(net::ERR_NOT_IMPLEMENTED)); return; } @@ -215,7 +219,9 @@ void AtomURLLoaderFactory::StartLoading( if (!dict.IsEmpty()) { int error_code; if (dict.Get("error", &error_code)) { - client->OnComplete(network::URLLoaderCompletionStatus(error_code)); + mojo::Remote client_remote( + std::move(client)); + client_remote->OnComplete(network::URLLoaderCompletionStatus(error_code)); return; } } @@ -259,7 +265,9 @@ void AtomURLLoaderFactory::StartLoading( // Some protocol accepts non-object responses. if (dict.IsEmpty() && ResponseMustBeObject(type)) { - client->OnComplete( + mojo::Remote client_remote( + std::move(client)); + client_remote->OnComplete( network::URLLoaderCompletionStatus(net::ERR_NOT_IMPLEMENTED)); return; } @@ -287,7 +295,10 @@ void AtomURLLoaderFactory::StartLoading( case ProtocolType::kFree: ProtocolType type; if (!gin::ConvertFromV8(args->isolate(), response, &type)) { - client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); + mojo::Remote client_remote( + std::move(client)); + client_remote->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_FAILED)); return; } StartLoading(std::move(loader), routing_id, request_id, options, request, @@ -298,14 +309,17 @@ void AtomURLLoaderFactory::StartLoading( } // static -void AtomURLLoaderFactory::StartLoadingBuffer( - network::mojom::URLLoaderClientPtr client, +void ElectronURLLoaderFactory::StartLoadingBuffer( + mojo::PendingRemote client, network::ResourceResponseHead head, const gin_helper::Dictionary& dict) { v8::Local buffer = dict.GetHandle(); dict.Get("data", &buffer); if (!node::Buffer::HasInstance(buffer)) { - client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); + mojo::Remote client_remote( + std::move(client)); + client_remote->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_FAILED)); return; } @@ -315,8 +329,8 @@ void AtomURLLoaderFactory::StartLoadingBuffer( } // static -void AtomURLLoaderFactory::StartLoadingString( - network::mojom::URLLoaderClientPtr client, +void ElectronURLLoaderFactory::StartLoadingString( + mojo::PendingRemote client, network::ResourceResponseHead head, const gin_helper::Dictionary& dict, v8::Isolate* isolate, @@ -327,7 +341,10 @@ void AtomURLLoaderFactory::StartLoadingString( } else if (!dict.IsEmpty()) { dict.Get("data", &contents); } else { - client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); + mojo::Remote client_remote( + std::move(client)); + client_remote->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_FAILED)); return; } @@ -335,10 +352,10 @@ void AtomURLLoaderFactory::StartLoadingString( } // static -void AtomURLLoaderFactory::StartLoadingFile( - network::mojom::URLLoaderRequest loader, +void ElectronURLLoaderFactory::StartLoadingFile( + mojo::PendingReceiver loader, network::ResourceRequest request, - network::mojom::URLLoaderClientPtr client, + mojo::PendingRemote client, network::ResourceResponseHead head, const gin_helper::Dictionary& dict, v8::Isolate* isolate, @@ -352,7 +369,10 @@ void AtomURLLoaderFactory::StartLoadingFile( if (dict.Get("path", &path)) request.url = net::FilePathToFileURL(path); } else { - client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); + mojo::Remote client_remote( + std::move(client)); + client_remote->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_FAILED)); return; } @@ -362,10 +382,10 @@ void AtomURLLoaderFactory::StartLoadingFile( } // static -void AtomURLLoaderFactory::StartLoadingHttp( - network::mojom::URLLoaderRequest loader, +void ElectronURLLoaderFactory::StartLoadingHttp( + mojo::PendingReceiver loader, const network::ResourceRequest& original_request, - network::mojom::URLLoaderClientPtr client, + mojo::PendingRemote client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, const gin_helper::Dictionary& dict) { auto request = std::make_unique(); @@ -381,12 +401,13 @@ void AtomURLLoaderFactory::StartLoadingHttp( if (request->method != "GET" && request->method != "HEAD") dict.Get("uploadData", &upload_data); - scoped_refptr browser_context = - AtomBrowserContext::From("", false); + scoped_refptr browser_context = + ElectronBrowserContext::From("", false); v8::Local value; if (dict.Get("session", &value)) { if (value->IsNull()) { - browser_context = AtomBrowserContext::From(base::GenerateGUID(), true); + browser_context = + ElectronBrowserContext::From(base::GenerateGUID(), true); } else { mate::Handle session; if (mate::ConvertFromV8(dict.isolate(), value, &session) && @@ -404,9 +425,9 @@ void AtomURLLoaderFactory::StartLoadingHttp( } // static -void AtomURLLoaderFactory::StartLoadingStream( - network::mojom::URLLoaderRequest loader, - network::mojom::URLLoaderClientPtr client, +void ElectronURLLoaderFactory::StartLoadingStream( + mojo::PendingReceiver loader, + mojo::PendingRemote client, network::ResourceResponseHead head, const gin_helper::Dictionary& dict) { v8::Local stream; @@ -414,25 +435,30 @@ void AtomURLLoaderFactory::StartLoadingStream( // Assume the opts is already a stream. stream = dict.GetHandle(); } else if (stream->IsNullOrUndefined()) { + mojo::Remote client_remote( + std::move(client)); // "data" was explicitly passed as null or undefined, assume the user wants // to send an empty body. // // Note that We must submit a empty body otherwise NetworkService would // crash. - client->OnReceiveResponse(head); + client_remote->OnReceiveResponse(head); mojo::ScopedDataPipeProducerHandle producer; mojo::ScopedDataPipeConsumerHandle consumer; if (mojo::CreateDataPipe(nullptr, &producer, &consumer) != MOJO_RESULT_OK) { - client->OnComplete( + client_remote->OnComplete( network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES)); return; } producer.reset(); // The data pipe is empty. - client->OnStartLoadingResponseBody(std::move(consumer)); - client->OnComplete(network::URLLoaderCompletionStatus(net::OK)); + client_remote->OnStartLoadingResponseBody(std::move(consumer)); + client_remote->OnComplete(network::URLLoaderCompletionStatus(net::OK)); return; } else if (!stream->IsObject()) { - client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); + mojo::Remote client_remote( + std::move(client)); + client_remote->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_FAILED)); return; } @@ -440,7 +466,10 @@ void AtomURLLoaderFactory::StartLoadingStream( v8::Local method; if (!data.Get("on", &method) || !method->IsFunction() || !data.Get("removeListener", &method) || !method->IsFunction()) { - client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); + mojo::Remote client_remote( + std::move(client)); + client_remote->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_FAILED)); return; } @@ -449,26 +478,28 @@ void AtomURLLoaderFactory::StartLoadingStream( } // static -void AtomURLLoaderFactory::SendContents( - network::mojom::URLLoaderClientPtr client, +void ElectronURLLoaderFactory::SendContents( + mojo::PendingRemote client, network::ResourceResponseHead head, std::string data) { + mojo::Remote client_remote( + std::move(client)); head.headers->AddHeader(kCORSHeader); - client->OnReceiveResponse(head); + client_remote->OnReceiveResponse(head); // Code bellow follows the pattern of data_url_loader_factory.cc. mojo::ScopedDataPipeProducerHandle producer; mojo::ScopedDataPipeConsumerHandle consumer; if (mojo::CreateDataPipe(nullptr, &producer, &consumer) != MOJO_RESULT_OK) { - client->OnComplete( + client_remote->OnComplete( network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES)); return; } - client->OnStartLoadingResponseBody(std::move(consumer)); + client_remote->OnStartLoadingResponseBody(std::move(consumer)); auto write_data = std::make_unique(); - write_data->client = std::move(client); + write_data->client = std::move(client_remote); write_data->data = std::move(data); write_data->producer = std::make_unique(std::move(producer)); diff --git a/shell/browser/net/electron_url_loader_factory.h b/shell/browser/net/electron_url_loader_factory.h new file mode 100644 index 0000000000000..a7d7b49161e43 --- /dev/null +++ b/shell/browser/net/electron_url_loader_factory.h @@ -0,0 +1,122 @@ +// Copyright (c) 2019 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_BROWSER_NET_ELECTRON_URL_LOADER_FACTORY_H_ +#define SHELL_BROWSER_NET_ELECTRON_URL_LOADER_FACTORY_H_ + +#include +#include +#include + +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "net/url_request/url_request_job_factory.h" +#include "services/network/public/cpp/resource_response.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "shell/common/gin_helper/dictionary.h" + +namespace electron { + +// Old Protocol API can only serve one type of response for one scheme. +enum class ProtocolType { + kBuffer, + kString, + kFile, + kHttp, + kStream, + kFree, // special type for returning arbitrary type of response. +}; + +using StartLoadingCallback = base::OnceCallback; +using ProtocolHandler = + base::Callback; + +// scheme => (type, handler). +using HandlersMap = + std::map>; + +// Implementation of URLLoaderFactory. +class ElectronURLLoaderFactory : public network::mojom::URLLoaderFactory { + public: + ElectronURLLoaderFactory(ProtocolType type, const ProtocolHandler& handler); + ~ElectronURLLoaderFactory() override; + + // network::mojom::URLLoaderFactory: + void CreateLoaderAndStart( + mojo::PendingReceiver loader, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + mojo::PendingRemote client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override; + void Clone(mojo::PendingReceiver receiver) + override; + + static void StartLoading( + mojo::PendingReceiver loader, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + mojo::PendingRemote client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, + network::mojom::URLLoaderFactory* proxy_factory, + ProtocolType type, + gin::Arguments* args); + + private: + static void StartLoadingBuffer( + mojo::PendingRemote client, + network::ResourceResponseHead head, + const gin_helper::Dictionary& dict); + static void StartLoadingString( + mojo::PendingRemote client, + network::ResourceResponseHead head, + const gin_helper::Dictionary& dict, + v8::Isolate* isolate, + v8::Local response); + static void StartLoadingFile( + mojo::PendingReceiver loader, + network::ResourceRequest request, + mojo::PendingRemote client, + network::ResourceResponseHead head, + const gin_helper::Dictionary& dict, + v8::Isolate* isolate, + v8::Local response); + static void StartLoadingHttp( + mojo::PendingReceiver loader, + const network::ResourceRequest& original_request, + mojo::PendingRemote client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, + const gin_helper::Dictionary& dict); + static void StartLoadingStream( + mojo::PendingReceiver loader, + mojo::PendingRemote client, + network::ResourceResponseHead head, + const gin_helper::Dictionary& dict); + + // Helper to send string as response. + static void SendContents( + mojo::PendingRemote client, + network::ResourceResponseHead head, + std::string data); + + // TODO(zcbenz): This comes from extensions/browser/extension_protocols.cc + // but I don't know what it actually does, find out the meanings of |Clone| + // and |bindings_| and add comments for them. + mojo::ReceiverSet receivers_; + + ProtocolType type_; + ProtocolHandler handler_; + + DISALLOW_COPY_AND_ASSIGN(ElectronURLLoaderFactory); +}; + +} // namespace electron + +#endif // SHELL_BROWSER_NET_ELECTRON_URL_LOADER_FACTORY_H_ diff --git a/shell/browser/net/network_context_service.cc b/shell/browser/net/network_context_service.cc index 5464964667044..b822b2d9c85ef 100644 --- a/shell/browser/net/network_context_service.cc +++ b/shell/browser/net/network_context_service.cc @@ -7,14 +7,14 @@ #include "chrome/common/chrome_constants.h" #include "content/public/browser/network_service_instance.h" #include "services/network/network_service.h" -#include "shell/browser/atom_browser_client.h" #include "shell/browser/browser_process_impl.h" +#include "shell/browser/electron_browser_client.h" #include "shell/browser/net/system_network_context_manager.h" namespace electron { NetworkContextService::NetworkContextService(content::BrowserContext* context) - : browser_context_(static_cast(context)), + : browser_context_(static_cast(context)), proxy_config_monitor_(browser_context_->prefs()) {} NetworkContextService::~NetworkContextService() = default; @@ -42,7 +42,7 @@ NetworkContextService::CreateNetworkContextParams(bool in_memory, network_context_params->accept_language = net::HttpUtil::GenerateAcceptLanguageHeader( - AtomBrowserClient::Get()->GetApplicationLocale()); + ElectronBrowserClient::Get()->GetApplicationLocale()); // Enable the HTTP cache. network_context_params->http_cache_enabled = diff --git a/shell/browser/net/network_context_service.h b/shell/browser/net/network_context_service.h index cfe43439991ac..82eb53029720a 100644 --- a/shell/browser/net/network_context_service.h +++ b/shell/browser/net/network_context_service.h @@ -10,7 +10,7 @@ #include "components/keyed_service/core/keyed_service.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/network/public/mojom/network_context.mojom.h" -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/electron_browser_context.h" namespace electron { @@ -33,7 +33,7 @@ class NetworkContextService : public KeyedService { bool in_memory, const base::FilePath& path); - AtomBrowserContext* browser_context_; + ElectronBrowserContext* browser_context_; ProxyConfigMonitor proxy_config_monitor_; }; diff --git a/shell/browser/net/network_context_service_factory.cc b/shell/browser/net/network_context_service_factory.cc index 810f4afa7d7ba..26a66cf8375a5 100644 --- a/shell/browser/net/network_context_service_factory.cc +++ b/shell/browser/net/network_context_service_factory.cc @@ -28,7 +28,8 @@ NetworkContextServiceFactory::~NetworkContextServiceFactory() = default; KeyedService* NetworkContextServiceFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { - return new NetworkContextService(static_cast(context)); + return new NetworkContextService( + static_cast(context)); } content::BrowserContext* NetworkContextServiceFactory::GetBrowserContextToUse( diff --git a/shell/browser/net/node_stream_loader.cc b/shell/browser/net/node_stream_loader.cc index 8c07c696cdc2d..11621eb7fe1d6 100644 --- a/shell/browser/net/node_stream_loader.cc +++ b/shell/browser/net/node_stream_loader.cc @@ -12,11 +12,12 @@ namespace electron { -NodeStreamLoader::NodeStreamLoader(network::ResourceResponseHead head, - network::mojom::URLLoaderRequest loader, - network::mojom::URLLoaderClientPtr client, - v8::Isolate* isolate, - v8::Local emitter) +NodeStreamLoader::NodeStreamLoader( + network::ResourceResponseHead head, + network::mojom::URLLoaderRequest loader, + mojo::PendingRemote client, + v8::Isolate* isolate, + v8::Local emitter) : binding_(this, std::move(loader)), client_(std::move(client)), isolate_(isolate), @@ -26,10 +27,7 @@ NodeStreamLoader::NodeStreamLoader(network::ResourceResponseHead head, base::BindOnce(&NodeStreamLoader::NotifyComplete, weak_factory_.GetWeakPtr(), net::ERR_FAILED)); - // PostTask since it might destruct. - base::SequencedTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&NodeStreamLoader::Start, - weak_factory_.GetWeakPtr(), std::move(head))); + Start(std::move(head)); } NodeStreamLoader::~NodeStreamLoader() { @@ -44,10 +42,6 @@ NodeStreamLoader::~NodeStreamLoader() { node::MakeCallback(isolate_, emitter_.Get(isolate_), "removeListener", node::arraysize(args), args, {0, 0}); } - - // Release references. - emitter_.Reset(); - buffer_.Reset(); } void NodeStreamLoader::Start(network::ResourceResponseHead head) { @@ -60,7 +54,6 @@ void NodeStreamLoader::Start(network::ResourceResponseHead head) { } producer_ = std::make_unique(std::move(producer)); - client_->OnReceiveResponse(head); client_->OnStartLoadingResponseBody(std::move(consumer)); @@ -109,6 +102,9 @@ void NodeStreamLoader::ReadMore() { if (!ret.ToLocal(&buffer) || !node::Buffer::HasInstance(buffer)) { readable_ = false; is_reading_ = false; + if (ended_) { + NotifyComplete(result_); + } return; } diff --git a/shell/browser/net/node_stream_loader.h b/shell/browser/net/node_stream_loader.h index 4375df825b731..55a529eacd123 100644 --- a/shell/browser/net/node_stream_loader.h +++ b/shell/browser/net/node_stream_loader.h @@ -10,6 +10,8 @@ #include #include +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/system/data_pipe_producer.h" #include "services/network/public/cpp/resource_response.h" @@ -30,7 +32,7 @@ class NodeStreamLoader : public network::mojom::URLLoader { public: NodeStreamLoader(network::ResourceResponseHead head, network::mojom::URLLoaderRequest loader, - network::mojom::URLLoaderClientPtr client, + mojo::PendingRemote client, v8::Isolate* isolate, v8::Local emitter); @@ -58,7 +60,7 @@ class NodeStreamLoader : public network::mojom::URLLoader { void ResumeReadingBodyFromNet() override {} mojo::Binding binding_; - network::mojom::URLLoaderClientPtr client_; + mojo::Remote client_; v8::Isolate* isolate_; v8::Global emitter_; diff --git a/shell/browser/net/proxying_url_loader_factory.cc b/shell/browser/net/proxying_url_loader_factory.cc index 589fccc303a86..47df711b9d190 100644 --- a/shell/browser/net/proxying_url_loader_factory.cc +++ b/shell/browser/net/proxying_url_loader_factory.cc @@ -6,21 +6,19 @@ #include +#include "base/command_line.h" +#include "base/strings/string_util.h" +#include "content/public/browser/browser_context.h" #include "extensions/browser/extension_navigation_ui_data.h" #include "mojo/public/cpp/bindings/binding.h" #include "net/base/completion_repeating_callback.h" +#include "net/base/load_flags.h" #include "net/http/http_util.h" #include "services/network/public/cpp/features.h" #include "shell/browser/net/asar/asar_url_loader.h" +#include "shell/common/options_switches.h" namespace electron { - -namespace { - -int64_t g_request_id = 0; - -} // namespace - ProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams:: FollowRedirectParams() = default; ProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams:: @@ -35,7 +33,7 @@ ProxyingURLLoaderFactory::InProgressRequest::InProgressRequest( const network::ResourceRequest& request, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, network::mojom::URLLoaderRequest loader_request, - network::mojom::URLLoaderClientPtr client) + mojo::PendingRemote client) : factory_(factory), request_(request), original_initiator_(request.request_initiator), @@ -46,19 +44,32 @@ ProxyingURLLoaderFactory::InProgressRequest::InProgressRequest( traffic_annotation_(traffic_annotation), proxied_loader_binding_(this, std::move(loader_request)), target_client_(std::move(client)), - proxied_client_binding_(this), - // TODO(zcbenz): We should always use "extraHeaders" mode to be compatible - // with old APIs. - has_any_extra_headers_listeners_(false) { + current_response_(network::mojom::URLResponseHead::New()), + // Always use "extraHeaders" mode to be compatible with old APIs, except + // when the |request_id_| is zero, which is not supported in Chromium and + // only happens in Electron when the request is started from net module. + has_any_extra_headers_listeners_(network_service_request_id != 0) { // If there is a client error, clean up the request. - target_client_.set_connection_error_handler(base::BindOnce( + target_client_.set_disconnect_handler(base::BindOnce( &ProxyingURLLoaderFactory::InProgressRequest::OnRequestError, weak_factory_.GetWeakPtr(), network::URLLoaderCompletionStatus(net::ERR_ABORTED))); } ProxyingURLLoaderFactory::InProgressRequest::~InProgressRequest() { - // TODO(zcbenz): Do cleanup here. + // This is important to ensure that no outstanding blocking requests continue + // to reference state owned by this object. + if (info_) { + factory_->web_request_api()->OnRequestWillBeDestroyed(&info_.value()); + } + if (on_before_send_headers_callback_) { + std::move(on_before_send_headers_callback_) + .Run(net::ERR_ABORTED, base::nullopt); + } + if (on_headers_received_callback_) { + std::move(on_headers_received_callback_) + .Run(net::ERR_ABORTED, base::nullopt, base::nullopt); + } } void ProxyingURLLoaderFactory::InProgressRequest::Restart() { @@ -76,14 +87,15 @@ void ProxyingURLLoaderFactory::InProgressRequest::UpdateRequestInfo() { request_for_info.request_initiator = original_initiator_; info_.emplace(extensions::WebRequestInfoInitParams( request_id_, factory_->render_process_id_, request_.render_frame_id, - nullptr, routing_id_, request_for_info, false, + factory_->navigation_ui_data_ ? factory_->navigation_ui_data_->DeepCopy() + : nullptr, + routing_id_, request_for_info, false, !(options_ & network::mojom::kURLLoadOptionSynchronous), - factory_->IsForServiceWorkerScript())); + factory_->IsForServiceWorkerScript(), factory_->navigation_id_)); current_request_uses_header_client_ = factory_->url_loader_header_client_receiver_.is_bound() && - network_service_request_id_ != 0 && - false /* TODO(zcbenz): HasExtraHeadersListenerForRequest */; + network_service_request_id_ != 0 && has_any_extra_headers_listeners_; } void ProxyingURLLoaderFactory::InProgressRequest::RestartInternal() { @@ -120,8 +132,8 @@ void ProxyingURLLoaderFactory::InProgressRequest::RestartInternal() { // continue or cancel the request. // // We pause the binding here to prevent further client message processing. - if (proxied_client_binding_.is_bound()) - proxied_client_binding_.PauseIncomingMethodCallProcessing(); + if (proxied_client_receiver_.is_bound()) + proxied_client_receiver_.Pause(); // Pause the header client, since we want to wait until OnBeforeRequest has // finished before processing any future events. @@ -192,12 +204,12 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnReceiveResponse( if (current_request_uses_header_client_) { // Use the headers we got from OnHeadersReceived as that'll contain // Set-Cookie if it existed. - auto saved_headers = current_response_.headers; - current_response_ = head; - current_response_.headers = saved_headers; + auto saved_headers = current_response_->headers; + current_response_ = std::move(head); + current_response_->headers = saved_headers; ContinueToResponseStarted(net::OK); } else { - current_response_ = head; + current_response_ = std::move(head); HandleResponseOrRedirectHeaders( base::BindOnce(&InProgressRequest::ContinueToResponseStarted, weak_factory_.GetWeakPtr())); @@ -212,16 +224,16 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnReceiveRedirect( if (current_request_uses_header_client_) { // Use the headers we got from OnHeadersReceived as that'll contain // Set-Cookie if it existed. - auto saved_headers = current_response_.headers; - current_response_ = head; + auto saved_headers = current_response_->headers; + current_response_ = std::move(head); // If this redirect is from an HSTS upgrade, OnHeadersReceived will not be // called before OnReceiveRedirect, so make sure the saved headers exist // before setting them. if (saved_headers) - current_response_.headers = saved_headers; + current_response_->headers = saved_headers; ContinueToBeforeRedirect(redirect_info, net::OK); } else { - current_response_ = head; + current_response_ = std::move(head); HandleResponseOrRedirectHeaders( base::BindOnce(&InProgressRequest::ContinueToBeforeRedirect, weak_factory_.GetWeakPtr(), redirect_info)); @@ -286,6 +298,7 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnBeforeSendHeaders( void ProxyingURLLoaderFactory::InProgressRequest::OnHeadersReceived( const std::string& headers, + const net::IPEndPoint& remote_endpoint, OnHeadersReceivedCallback callback) { if (!current_request_uses_header_client_) { std::move(callback).Run(net::OK, base::nullopt, GURL()); @@ -293,8 +306,10 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnHeadersReceived( } on_headers_received_callback_ = std::move(callback); - current_response_.headers = + current_response_ = network::mojom::URLResponseHead::New(); + current_response_->headers = base::MakeRefCounted(headers); + current_response_->remote_endpoint = remote_endpoint; HandleResponseOrRedirectHeaders( base::BindOnce(&InProgressRequest::ContinueToHandleOverrideHeaders, weak_factory_.GetWeakPtr())); @@ -312,8 +327,8 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToBeforeSendHeaders( return; } - if (proxied_client_binding_.is_bound()) - proxied_client_binding_.ResumeIncomingMethodCallProcessing(); + if (proxied_client_receiver_.is_bound()) + proxied_client_receiver_.Resume(); auto continuation = base::BindRepeating( &InProgressRequest::ContinueToSendHeaders, weak_factory_.GetWeakPtr()); @@ -333,9 +348,9 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToBeforeSendHeaders( // they respond. |continuation| above will be invoked asynchronously to // continue or cancel the request. // - // We pause the binding here to prevent further client message processing. - if (proxied_client_binding_.is_bound()) - proxied_client_binding_.PauseIncomingMethodCallProcessing(); + // We pause the receiver here to prevent further client message processing. + if (proxied_client_receiver_.is_bound()) + proxied_client_receiver_.Resume(); return; } DCHECK_EQ(net::OK, result); @@ -382,8 +397,8 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToSendHeaders( pending_follow_redirect_params_.reset(); } - if (proxied_client_binding_.is_bound()) - proxied_client_binding_.ResumeIncomingMethodCallProcessing(); + if (proxied_client_receiver_.is_bound()) + proxied_client_receiver_.Resume(); // Note: In Electron onSendHeaders is called for all protocols. factory_->web_request_api()->OnSendHeaders(&info_.value(), request_, @@ -405,8 +420,8 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToStartRequest( return; } - if (proxied_client_binding_.is_bound()) - proxied_client_binding_.ResumeIncomingMethodCallProcessing(); + if (proxied_client_receiver_.is_bound()) + proxied_client_receiver_.Resume(); if (header_client_receiver_.is_bound()) header_client_receiver_.Resume(); @@ -414,8 +429,6 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToStartRequest( if (!target_loader_.is_bound() && factory_->target_factory_.is_bound()) { // No extensions have cancelled us up to this point, so it's now OK to // initiate the real network request. - network::mojom::URLLoaderClientPtr proxied_client; - proxied_client_binding_.Bind(mojo::MakeRequest(&proxied_client)); uint32_t options = options_; // Even if this request does not use the header client, future redirects // might, so we need to set the option on the loader. @@ -424,11 +437,12 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToStartRequest( factory_->target_factory_->CreateLoaderAndStart( mojo::MakeRequest(&target_loader_), routing_id_, network_service_request_id_, options, request_, - std::move(proxied_client), traffic_annotation_); + proxied_client_receiver_.BindNewPipeAndPassRemote(), + traffic_annotation_); } // From here the lifecycle of this request is driven by subsequent events on - // either |proxy_loader_binding_|, |proxy_client_binding_|, or + // either |proxy_loader_binding_|, |proxy_client_receiver_|, or // |header_client_receiver_|. } @@ -447,14 +461,14 @@ void ProxyingURLLoaderFactory::InProgressRequest:: // Make sure to update current_response_, since when OnReceiveResponse // is called we will not use its headers as it might be missing the // Set-Cookie line (as that gets stripped over IPC). - current_response_.headers = override_headers_; + current_response_->headers = override_headers_; } } std::move(on_headers_received_callback_).Run(net::OK, headers, redirect_url_); override_headers_ = nullptr; - if (proxied_client_binding_) - proxied_client_binding_.ResumeIncomingMethodCallProcessing(); + if (proxied_client_receiver_.is_bound()) + proxied_client_receiver_.Resume(); } void ProxyingURLLoaderFactory::InProgressRequest::ContinueToResponseStarted( @@ -466,7 +480,7 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToResponseStarted( DCHECK(!current_request_uses_header_client_ || !override_headers_); if (override_headers_) - current_response_.headers = override_headers_; + current_response_->headers = override_headers_; std::string redirect_location; if (override_headers_ && override_headers_->IsRedirect(&redirect_location)) { @@ -488,7 +502,7 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToResponseStarted( // These will get re-bound if a new request is initiated by // |FollowRedirect()|. - proxied_client_binding_.Close(); + proxied_client_receiver_.reset(); header_client_receiver_.reset(); target_loader_.reset(); @@ -496,12 +510,12 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToResponseStarted( return; } - info_->AddResponseInfoFromResourceResponse(current_response_); + info_->AddResponseInfoFromResourceResponse(*current_response_); - proxied_client_binding_.ResumeIncomingMethodCallProcessing(); + proxied_client_receiver_.Resume(); factory_->web_request_api()->OnResponseStarted(&info_.value(), request_); - target_client_->OnReceiveResponse(current_response_); + target_client_->OnReceiveResponse(current_response_.Clone()); } void ProxyingURLLoaderFactory::InProgressRequest::ContinueToBeforeRedirect( @@ -512,14 +526,14 @@ void ProxyingURLLoaderFactory::InProgressRequest::ContinueToBeforeRedirect( return; } - info_->AddResponseInfoFromResourceResponse(current_response_); + info_->AddResponseInfoFromResourceResponse(*current_response_); - if (proxied_client_binding_.is_bound()) - proxied_client_binding_.ResumeIncomingMethodCallProcessing(); + if (proxied_client_receiver_.is_bound()) + proxied_client_receiver_.Resume(); factory_->web_request_api()->OnBeforeRedirect(&info_.value(), request_, redirect_info.new_url); - target_client_->OnReceiveRedirect(redirect_info, current_response_); + target_client_->OnReceiveRedirect(redirect_info, current_response_.Clone()); request_.url = redirect_info.new_url; request_.method = redirect_info.new_method; request_.site_for_cookies = redirect_info.new_site_for_cookies; @@ -545,7 +559,7 @@ void ProxyingURLLoaderFactory::InProgressRequest:: // bugs. The latter doesn't know anything about the redirect. Continuing // the load with it gives unexpected results. See // https://crbug.com/882661#c72. - proxied_client_binding_.Close(); + proxied_client_receiver_.reset(); header_client_receiver_.reset(); target_loader_.reset(); @@ -557,14 +571,14 @@ void ProxyingURLLoaderFactory::InProgressRequest:: redirect_info.new_url = redirect_url_; redirect_info.new_site_for_cookies = redirect_url_; - network::ResourceResponseHead head; + auto head = network::mojom::URLResponseHead::New(); std::string headers = base::StringPrintf( "HTTP/1.1 %i Internal Redirect\n" "Location: %s\n" "Non-Authoritative-Reason: WebRequest API\n\n", kInternalRedirectStatusCode, redirect_url_.spec().c_str()); - if (network::features::ShouldEnableOutOfBlinkCors()) { + if (factory_->browser_context_->ShouldEnableOutOfBlinkCors()) { // Cross-origin requests need to modify the Origin header to 'null'. Since // CorsURLLoader sets |request_initiator| to the Origin request header in // NetworkService, we need to modify |request_initiator| here to craft the @@ -595,11 +609,11 @@ void ProxyingURLLoaderFactory::InProgressRequest:: http_origin.c_str()); } } - head.headers = base::MakeRefCounted( + head->headers = base::MakeRefCounted( net::HttpUtil::AssembleRawHeaders(headers)); - head.encoded_data_length = 0; + head->encoded_data_length = 0; - current_response_ = head; + current_response_ = std::move(head); ContinueToBeforeRedirect(redirect_info, net::OK); } @@ -608,14 +622,14 @@ void ProxyingURLLoaderFactory::InProgressRequest:: override_headers_ = nullptr; redirect_url_ = GURL(); - info_->AddResponseInfoFromResourceResponse(current_response_); + info_->AddResponseInfoFromResourceResponse(*current_response_); net::CompletionRepeatingCallback copyable_callback = base::AdaptCallbackForRepeating(std::move(continuation)); DCHECK(info_.has_value()); int result = factory_->web_request_api()->OnHeadersReceived( &info_.value(), request_, copyable_callback, - current_response_.headers.get(), &override_headers_, &redirect_url_); + current_response_->headers.get(), &override_headers_, &redirect_url_); if (result == net::ERR_BLOCKED_BY_CLIENT) { OnRequestError(network::URLLoaderCompletionStatus(result)); return; @@ -626,8 +640,9 @@ void ProxyingURLLoaderFactory::InProgressRequest:: // they respond. |continuation| above will be invoked asynchronously to // continue or cancel the request. // - // We pause the binding here to prevent further client message processing. - proxied_client_binding_.PauseIncomingMethodCallProcessing(); + // We pause the receiver here to prevent further client message processing. + if (proxied_client_receiver_.is_bound()) + proxied_client_receiver_.Pause(); return; } @@ -651,43 +666,73 @@ void ProxyingURLLoaderFactory::InProgressRequest::OnRequestError( ProxyingURLLoaderFactory::ProxyingURLLoaderFactory( WebRequestAPI* web_request_api, const HandlersMap& intercepted_handlers, + content::BrowserContext* browser_context, int render_process_id, + uint64_t* request_id_generator, + std::unique_ptr navigation_ui_data, + base::Optional navigation_id, network::mojom::URLLoaderFactoryRequest loader_request, - network::mojom::URLLoaderFactoryPtrInfo target_factory_info, + mojo::PendingRemote target_factory_remote, mojo::PendingReceiver header_client_receiver, content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type) : web_request_api_(web_request_api), intercepted_handlers_(intercepted_handlers), + browser_context_(browser_context), render_process_id_(render_process_id), + request_id_generator_(request_id_generator), + navigation_ui_data_(std::move(navigation_ui_data)), + navigation_id_(std::move(navigation_id)), loader_factory_type_(loader_factory_type) { - target_factory_.Bind(std::move(target_factory_info)); - target_factory_.set_connection_error_handler(base::BindOnce( + target_factory_.Bind(std::move(target_factory_remote)); + target_factory_.set_disconnect_handler(base::BindOnce( &ProxyingURLLoaderFactory::OnTargetFactoryError, base::Unretained(this))); - proxy_bindings_.AddBinding(this, std::move(loader_request)); - proxy_bindings_.set_connection_error_handler(base::BindRepeating( + proxy_receivers_.Add(this, std::move(loader_request)); + proxy_receivers_.set_disconnect_handler(base::BindRepeating( &ProxyingURLLoaderFactory::OnProxyBindingError, base::Unretained(this))); if (header_client_receiver) url_loader_header_client_receiver_.Bind(std::move(header_client_receiver)); + + ignore_connections_limit_domains_ = base::SplitString( + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kIgnoreConnectionsLimit), + ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); } ProxyingURLLoaderFactory::~ProxyingURLLoaderFactory() = default; +bool ProxyingURLLoaderFactory::ShouldIgnoreConnectionsLimit( + const network::ResourceRequest& request) { + for (const auto& domain : ignore_connections_limit_domains_) { + if (request.url.DomainIs(domain)) { + return true; + } + } + return false; +} + void ProxyingURLLoaderFactory::CreateLoaderAndStart( - network::mojom::URLLoaderRequest loader, + mojo::PendingReceiver loader, int32_t routing_id, int32_t request_id, uint32_t options, - const network::ResourceRequest& request, - network::mojom::URLLoaderClientPtr client, + const network::ResourceRequest& original_request, + mojo::PendingRemote client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { + // Take a copy so we can mutate the request. + network::ResourceRequest request = original_request; + + if (ShouldIgnoreConnectionsLimit(request)) { + request.load_flags |= net::LOAD_IGNORE_LIMITS; + } + // Check if user has intercepted this scheme. auto it = intercepted_handlers_.find(request.url.scheme()); if (it != intercepted_handlers_.end()) { // > it->second.second.Run( - request, base::BindOnce(&AtomURLLoaderFactory::StartLoading, + request, base::BindOnce(&ElectronURLLoaderFactory::StartLoading, std::move(loader), routing_id, request_id, options, request, std::move(client), traffic_annotation, this, it->second.first)); @@ -713,8 +758,12 @@ void ProxyingURLLoaderFactory::CreateLoaderAndStart( // per-BrowserContext so extensions can make sense of it. Note that // |network_service_request_id_| by contrast is not necessarily unique, so we // don't use it for identity here. - const uint64_t web_request_id = ++g_request_id; + const uint64_t web_request_id = ++(*request_id_generator_); + // Notes: Chromium assumes that requests with zero-ID would never use the + // "extraHeaders" code path, however in Electron requests started from + // the net module would have zero-ID because they do not have renderer process + // associated. if (request_id) network_request_id_to_web_request_id_.emplace(request_id, web_request_id); @@ -727,8 +776,8 @@ void ProxyingURLLoaderFactory::CreateLoaderAndStart( } void ProxyingURLLoaderFactory::Clone( - network::mojom::URLLoaderFactoryRequest loader_request) { - proxy_bindings_.AddBinding(this, std::move(loader_request)); + mojo::PendingReceiver loader_receiver) { + proxy_receivers_.Add(this, std::move(loader_receiver)); } void ProxyingURLLoaderFactory::OnLoaderCreated( @@ -750,13 +799,13 @@ bool ProxyingURLLoaderFactory::IsForServiceWorkerScript() const { void ProxyingURLLoaderFactory::OnTargetFactoryError() { target_factory_.reset(); - proxy_bindings_.CloseAllBindings(); + proxy_receivers_.Clear(); MaybeDeleteThis(); } void ProxyingURLLoaderFactory::OnProxyBindingError() { - if (proxy_bindings_.empty()) + if (proxy_receivers_.empty()) target_factory_.reset(); MaybeDeleteThis(); diff --git a/shell/browser/net/proxying_url_loader_factory.h b/shell/browser/net/proxying_url_loader_factory.h index d3e62c88ffcef..b4d501d9e109e 100644 --- a/shell/browser/net/proxying_url_loader_factory.h +++ b/shell/browser/net/proxying_url_loader_factory.h @@ -14,57 +14,20 @@ #include "base/optional.h" #include "content/public/browser/content_browser_client.h" #include "extensions/browser/api/web_request/web_request_info.h" -#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/resource_response.h" #include "services/network/public/mojom/network_context.mojom.h" #include "services/network/public/mojom/url_loader.mojom.h" -#include "shell/browser/net/atom_url_loader_factory.h" +#include "shell/browser/net/electron_url_loader_factory.h" +#include "shell/browser/net/web_request_api_interface.h" namespace electron { -// Defines the interface for WebRequest API, implemented by api::WebRequestNS. -class WebRequestAPI { - public: - virtual ~WebRequestAPI() {} - - using BeforeSendHeadersCallback = - base::OnceCallback& removed_headers, - const std::set& set_headers, - int error_code)>; - - virtual bool HasListener() const = 0; - virtual int OnBeforeRequest(extensions::WebRequestInfo* info, - const network::ResourceRequest& request, - net::CompletionOnceCallback callback, - GURL* new_url) = 0; - virtual int OnBeforeSendHeaders(extensions::WebRequestInfo* info, - const network::ResourceRequest& request, - BeforeSendHeadersCallback callback, - net::HttpRequestHeaders* headers) = 0; - virtual int OnHeadersReceived( - extensions::WebRequestInfo* info, - const network::ResourceRequest& request, - net::CompletionOnceCallback callback, - const net::HttpResponseHeaders* original_response_headers, - scoped_refptr* override_response_headers, - GURL* allowed_unsafe_redirect_url) = 0; - virtual void OnSendHeaders(extensions::WebRequestInfo* info, - const network::ResourceRequest& request, - const net::HttpRequestHeaders& headers) = 0; - virtual void OnBeforeRedirect(extensions::WebRequestInfo* info, - const network::ResourceRequest& request, - const GURL& new_location) = 0; - virtual void OnResponseStarted(extensions::WebRequestInfo* info, - const network::ResourceRequest& request) = 0; - virtual void OnErrorOccurred(extensions::WebRequestInfo* info, - const network::ResourceRequest& request, - int net_error) = 0; - virtual void OnCompleted(extensions::WebRequestInfo* info, - const network::ResourceRequest& request, - int net_error) = 0; -}; - // This class is responsible for following tasks when NetworkService is enabled: // 1. handling intercepted protocols; // 2. implementing webRequest module; @@ -88,7 +51,7 @@ class ProxyingURLLoaderFactory const network::ResourceRequest& request, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, network::mojom::URLLoaderRequest loader_request, - network::mojom::URLLoaderClientPtr client); + mojo::PendingRemote client); ~InProgressRequest() override; void Restart(); @@ -122,6 +85,7 @@ class ProxyingURLLoaderFactory void OnBeforeSendHeaders(const net::HttpRequestHeaders& headers, OnBeforeSendHeadersCallback callback) override; void OnHeadersReceived(const std::string& headers, + const net::IPEndPoint& endpoint, OnHeadersReceivedCallback callback) override; private: @@ -152,15 +116,16 @@ class ProxyingURLLoaderFactory const uint32_t options_; const net::MutableNetworkTrafficAnnotationTag traffic_annotation_; mojo::Binding proxied_loader_binding_; - network::mojom::URLLoaderClientPtr target_client_; + mojo::Remote target_client_; base::Optional info_; - network::ResourceResponseHead current_response_; + network::mojom::URLResponseHeadPtr current_response_; scoped_refptr override_headers_; GURL redirect_url_; - mojo::Binding proxied_client_binding_; + mojo::Receiver proxied_client_receiver_{ + this}; network::mojom::URLLoaderPtr target_loader_; bool request_completed_ = false; @@ -201,30 +166,41 @@ class ProxyingURLLoaderFactory ProxyingURLLoaderFactory( WebRequestAPI* web_request_api, const HandlersMap& intercepted_handlers, + content::BrowserContext* browser_context, int render_process_id, + uint64_t* request_id_generator, + std::unique_ptr navigation_ui_data, + base::Optional navigation_id, network::mojom::URLLoaderFactoryRequest loader_request, - network::mojom::URLLoaderFactoryPtrInfo target_factory_info, + mojo::PendingRemote + target_factory_remote, mojo::PendingReceiver header_client_receiver, content::ContentBrowserClient::URLLoaderFactoryType loader_factory_type); ~ProxyingURLLoaderFactory() override; // network::mojom::URLLoaderFactory: - void CreateLoaderAndStart(network::mojom::URLLoaderRequest loader, - int32_t routing_id, - int32_t request_id, - uint32_t options, - const network::ResourceRequest& request, - network::mojom::URLLoaderClientPtr client, - const net::MutableNetworkTrafficAnnotationTag& - traffic_annotation) override; - void Clone(network::mojom::URLLoaderFactoryRequest request) override; + void CreateLoaderAndStart( + mojo::PendingReceiver loader, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + mojo::PendingRemote client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override; + void Clone(mojo::PendingReceiver + loader_receiver) override; // network::mojom::TrustedURLLoaderHeaderClient: void OnLoaderCreated( int32_t request_id, mojo::PendingReceiver receiver) override; + void OnLoaderForCorsPreflightCreated( + const network::ResourceRequest& request, + mojo::PendingReceiver receiver) + override {} WebRequestAPI* web_request_api() { return web_request_api_; } @@ -236,6 +212,8 @@ class ProxyingURLLoaderFactory void RemoveRequest(int32_t network_service_request_id, uint64_t request_id); void MaybeDeleteThis(); + bool ShouldIgnoreConnectionsLimit(const network::ResourceRequest& request); + // Passed from api::WebRequestNS. WebRequestAPI* web_request_api_; @@ -248,9 +226,13 @@ class ProxyingURLLoaderFactory // In this way we can avoid using code from api namespace in this file. const HandlersMap& intercepted_handlers_; + content::BrowserContext* const browser_context_; const int render_process_id_; - mojo::BindingSet proxy_bindings_; - network::mojom::URLLoaderFactoryPtr target_factory_; + uint64_t* request_id_generator_; // managed by AtomBrowserClient + std::unique_ptr navigation_ui_data_; + base::Optional navigation_id_; + mojo::ReceiverSet proxy_receivers_; + mojo::Remote target_factory_; mojo::Receiver url_loader_header_client_receiver_{this}; const content::ContentBrowserClient::URLLoaderFactoryType @@ -264,6 +246,8 @@ class ProxyingURLLoaderFactory // internally generated request ID for the same request. std::map network_request_id_to_web_request_id_; + std::vector ignore_connections_limit_domains_; + DISALLOW_COPY_AND_ASSIGN(ProxyingURLLoaderFactory); }; diff --git a/shell/browser/net/proxying_websocket.cc b/shell/browser/net/proxying_websocket.cc new file mode 100644 index 0000000000000..c7c23839aa90e --- /dev/null +++ b/shell/browser/net/proxying_websocket.cc @@ -0,0 +1,456 @@ +// 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. + +#include "electron/shell/browser/net/proxying_websocket.h" + +#include + +#include "base/bind.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h" +#include "content/public/browser/browser_thread.h" +#include "extensions/browser/extension_navigation_ui_data.h" +#include "net/base/ip_endpoint.h" +#include "net/http/http_util.h" + +namespace electron { + +ProxyingWebSocket::ProxyingWebSocket( + WebRequestAPI* web_request_api, + WebSocketFactory factory, + const network::ResourceRequest& request, + mojo::PendingRemote + handshake_client, + bool has_extra_headers, + int process_id, + int render_frame_id, + content::BrowserContext* browser_context, + uint64_t* request_id_generator) + : web_request_api_(web_request_api), + request_(request), + factory_(std::move(factory)), + forwarding_handshake_client_(std::move(handshake_client)), + request_headers_(request.headers), + response_(network::mojom::URLResponseHead::New()), + has_extra_headers_(has_extra_headers), + info_(extensions::WebRequestInfoInitParams( + ++(*request_id_generator), + process_id, + render_frame_id, + nullptr, + MSG_ROUTING_NONE, + request, + /*is_download=*/false, + /*is_async=*/true, + /*is_service_worker_script=*/false, + /*navigation_id=*/base::nullopt)) {} + +ProxyingWebSocket::~ProxyingWebSocket() { + if (on_before_send_headers_callback_) { + std::move(on_before_send_headers_callback_) + .Run(net::ERR_ABORTED, base::nullopt); + } + if (on_headers_received_callback_) { + std::move(on_headers_received_callback_) + .Run(net::ERR_ABORTED, base::nullopt, GURL()); + } +} + +void ProxyingWebSocket::Start() { + // If the header client will be used, we start the request immediately, and + // OnBeforeSendHeaders and OnSendHeaders will be handled there. Otherwise, + // send these events before the request starts. + base::RepeatingCallback continuation; + if (has_extra_headers_) { + continuation = base::BindRepeating( + &ProxyingWebSocket::ContinueToStartRequest, weak_factory_.GetWeakPtr()); + } else { + continuation = + base::BindRepeating(&ProxyingWebSocket::OnBeforeRequestComplete, + weak_factory_.GetWeakPtr()); + } + + int result = web_request_api_->OnBeforeRequest(&info_, request_, continuation, + &redirect_url_); + + if (result == net::ERR_BLOCKED_BY_CLIENT) { + OnError(result); + return; + } + + if (result == net::ERR_IO_PENDING) { + return; + } + + DCHECK_EQ(net::OK, result); + continuation.Run(net::OK); +} + +void ProxyingWebSocket::OnOpeningHandshakeStarted( + network::mojom::WebSocketHandshakeRequestPtr request) { + DCHECK(forwarding_handshake_client_); + forwarding_handshake_client_->OnOpeningHandshakeStarted(std::move(request)); +} + +void ProxyingWebSocket::ContinueToHeadersReceived() { + auto continuation = + base::BindRepeating(&ProxyingWebSocket::OnHeadersReceivedComplete, + weak_factory_.GetWeakPtr()); + info_.AddResponseInfoFromResourceResponse(*response_); + int result = web_request_api_->OnHeadersReceived( + &info_, request_, continuation, response_->headers.get(), + &override_headers_, &redirect_url_); + + if (result == net::ERR_BLOCKED_BY_CLIENT) { + OnError(result); + return; + } + + PauseIncomingMethodCallProcessing(); + if (result == net::ERR_IO_PENDING) + return; + + DCHECK_EQ(net::OK, result); + OnHeadersReceivedComplete(net::OK); +} + +void ProxyingWebSocket::OnConnectionEstablished( + mojo::PendingRemote websocket, + mojo::PendingReceiver client_receiver, + network::mojom::WebSocketHandshakeResponsePtr response, + mojo::ScopedDataPipeConsumerHandle readable) { + DCHECK(forwarding_handshake_client_); + DCHECK(!is_done_); + is_done_ = true; + websocket_ = std::move(websocket); + client_receiver_ = std::move(client_receiver); + handshake_response_ = std::move(response); + readable_ = std::move(readable); + + response_->remote_endpoint = handshake_response_->remote_endpoint; + + // response_->headers will be set in OnBeforeSendHeaders if + // |receiver_as_header_client_| is set. + if (receiver_as_header_client_.is_bound()) { + ContinueToCompleted(); + return; + } + + response_->headers = + base::MakeRefCounted(base::StringPrintf( + "HTTP/%d.%d %d %s", handshake_response_->http_version.major_value(), + handshake_response_->http_version.minor_value(), + handshake_response_->status_code, + handshake_response_->status_text.c_str())); + for (const auto& header : handshake_response_->headers) + response_->headers->AddHeader(header->name + ": " + header->value); + + ContinueToHeadersReceived(); +} + +void ProxyingWebSocket::ContinueToCompleted() { + DCHECK(forwarding_handshake_client_); + DCHECK(is_done_); + web_request_api_->OnCompleted(&info_, request_, net::ERR_WS_UPGRADE); + forwarding_handshake_client_->OnConnectionEstablished( + std::move(websocket_), std::move(client_receiver_), + std::move(handshake_response_), std::move(readable_)); + + // Deletes |this|. + delete this; +} + +void ProxyingWebSocket::OnAuthRequired( + const net::AuthChallengeInfo& auth_info, + const scoped_refptr& headers, + const net::IPEndPoint& remote_endpoint, + OnAuthRequiredCallback callback) { + if (!callback) { + OnError(net::ERR_FAILED); + return; + } + + response_->headers = headers; + response_->remote_endpoint = remote_endpoint; + auth_required_callback_ = std::move(callback); + + auto continuation = + base::BindRepeating(&ProxyingWebSocket::OnHeadersReceivedCompleteForAuth, + weak_factory_.GetWeakPtr(), auth_info); + info_.AddResponseInfoFromResourceResponse(*response_); + int result = web_request_api_->OnHeadersReceived( + &info_, request_, continuation, response_->headers.get(), + &override_headers_, &redirect_url_); + + if (result == net::ERR_BLOCKED_BY_CLIENT) { + OnError(result); + return; + } + + PauseIncomingMethodCallProcessing(); + if (result == net::ERR_IO_PENDING) + return; + + DCHECK_EQ(net::OK, result); + OnHeadersReceivedCompleteForAuth(auth_info, net::OK); +} + +void ProxyingWebSocket::OnBeforeSendHeaders( + const net::HttpRequestHeaders& headers, + OnBeforeSendHeadersCallback callback) { + DCHECK(receiver_as_header_client_.is_bound()); + + request_headers_ = headers; + on_before_send_headers_callback_ = std::move(callback); + OnBeforeRequestComplete(net::OK); +} + +void ProxyingWebSocket::OnHeadersReceived(const std::string& headers, + const net::IPEndPoint& endpoint, + OnHeadersReceivedCallback callback) { + DCHECK(receiver_as_header_client_.is_bound()); + + on_headers_received_callback_ = std::move(callback); + response_->headers = base::MakeRefCounted(headers); + + ContinueToHeadersReceived(); +} + +void ProxyingWebSocket::StartProxying( + WebRequestAPI* web_request_api, + WebSocketFactory factory, + const GURL& url, + const GURL& site_for_cookies, + const base::Optional& user_agent, + mojo::PendingRemote + handshake_client, + bool has_extra_headers, + int process_id, + int render_frame_id, + const url::Origin& origin, + content::BrowserContext* browser_context, + uint64_t* request_id_generator) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + network::ResourceRequest request; + request.url = url; + request.site_for_cookies = site_for_cookies; + if (user_agent) { + request.headers.SetHeader(net::HttpRequestHeaders::kUserAgent, *user_agent); + } + request.request_initiator = origin; + + auto* proxy = new ProxyingWebSocket( + web_request_api, std::move(factory), request, std::move(handshake_client), + has_extra_headers, process_id, render_frame_id, browser_context, + request_id_generator); + proxy->Start(); +} + +void ProxyingWebSocket::OnBeforeRequestComplete(int error_code) { + DCHECK(receiver_as_header_client_.is_bound() || + !receiver_as_handshake_client_.is_bound()); + DCHECK(info_.url.SchemeIsWSOrWSS()); + if (error_code != net::OK) { + OnError(error_code); + return; + } + + auto continuation = + base::BindRepeating(&ProxyingWebSocket::OnBeforeSendHeadersComplete, + weak_factory_.GetWeakPtr()); + + info_.AddResponseInfoFromResourceResponse(*response_); + int result = web_request_api_->OnBeforeSendHeaders( + &info_, request_, continuation, &request_headers_); + + if (result == net::ERR_BLOCKED_BY_CLIENT) { + OnError(result); + return; + } + + if (result == net::ERR_IO_PENDING) + return; + + DCHECK_EQ(net::OK, result); + OnBeforeSendHeadersComplete(std::set(), std::set(), + net::OK); +} + +void ProxyingWebSocket::OnBeforeSendHeadersComplete( + const std::set& removed_headers, + const std::set& set_headers, + int error_code) { + DCHECK(receiver_as_header_client_.is_bound() || + !receiver_as_handshake_client_.is_bound()); + if (error_code != net::OK) { + OnError(error_code); + return; + } + + if (receiver_as_header_client_.is_bound()) { + CHECK(on_before_send_headers_callback_); + std::move(on_before_send_headers_callback_) + .Run(error_code, request_headers_); + } + + info_.AddResponseInfoFromResourceResponse(*response_); + web_request_api_->OnSendHeaders(&info_, request_, request_headers_); + + if (!receiver_as_header_client_.is_bound()) + ContinueToStartRequest(net::OK); +} + +void ProxyingWebSocket::ContinueToStartRequest(int error_code) { + if (error_code != net::OK) { + OnError(error_code); + return; + } + + base::flat_set used_header_names; + std::vector additional_headers; + for (net::HttpRequestHeaders::Iterator it(request_headers_); it.GetNext();) { + additional_headers.push_back( + network::mojom::HttpHeader::New(it.name(), it.value())); + used_header_names.insert(base::ToLowerASCII(it.name())); + } + for (const auto& header : additional_headers_) { + if (!used_header_names.contains(base::ToLowerASCII(header->name))) { + additional_headers.push_back( + network::mojom::HttpHeader::New(header->name, header->value)); + } + } + + mojo::PendingRemote + trusted_header_client = mojo::NullRemote(); + if (has_extra_headers_) { + trusted_header_client = + receiver_as_header_client_.BindNewPipeAndPassRemote(); + } + + std::move(factory_).Run( + info_.url, std::move(additional_headers), + receiver_as_handshake_client_.BindNewPipeAndPassRemote(), + receiver_as_auth_handler_.BindNewPipeAndPassRemote(), + std::move(trusted_header_client)); + + // Here we detect mojo connection errors on |receiver_as_handshake_client_|. + // See also CreateWebSocket in + // //network/services/public/mojom/network_context.mojom. + receiver_as_handshake_client_.set_disconnect_with_reason_handler( + base::BindOnce(&ProxyingWebSocket::OnMojoConnectionErrorWithCustomReason, + base::Unretained(this))); + forwarding_handshake_client_.set_disconnect_handler(base::BindOnce( + &ProxyingWebSocket::OnMojoConnectionError, base::Unretained(this))); +} + +void ProxyingWebSocket::OnHeadersReceivedComplete(int error_code) { + if (error_code != net::OK) { + OnError(error_code); + return; + } + + if (on_headers_received_callback_) { + base::Optional headers; + if (override_headers_) + headers = override_headers_->raw_headers(); + std::move(on_headers_received_callback_) + .Run(net::OK, headers, base::nullopt); + } + + if (override_headers_) { + response_->headers = override_headers_; + override_headers_ = nullptr; + } + + ResumeIncomingMethodCallProcessing(); + info_.AddResponseInfoFromResourceResponse(*response_); + web_request_api_->OnResponseStarted(&info_, request_); + + if (!receiver_as_header_client_.is_bound()) + ContinueToCompleted(); +} + +void ProxyingWebSocket::OnAuthRequiredComplete( + extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse rv) { + CHECK(auth_required_callback_); + ResumeIncomingMethodCallProcessing(); + switch (rv) { + case extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse:: + AUTH_REQUIRED_RESPONSE_NO_ACTION: + case extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse:: + AUTH_REQUIRED_RESPONSE_CANCEL_AUTH: + std::move(auth_required_callback_).Run(base::nullopt); + break; + + case extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse:: + AUTH_REQUIRED_RESPONSE_SET_AUTH: + std::move(auth_required_callback_).Run(auth_credentials_); + break; + case extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse:: + AUTH_REQUIRED_RESPONSE_IO_PENDING: + NOTREACHED(); + break; + } +} + +void ProxyingWebSocket::OnHeadersReceivedCompleteForAuth( + const net::AuthChallengeInfo& auth_info, + int rv) { + if (rv != net::OK) { + OnError(rv); + return; + } + ResumeIncomingMethodCallProcessing(); + info_.AddResponseInfoFromResourceResponse(*response_); + + auto continuation = base::BindRepeating( + &ProxyingWebSocket::OnAuthRequiredComplete, weak_factory_.GetWeakPtr()); + auto auth_rv = extensions::ExtensionWebRequestEventRouter:: + AuthRequiredResponse::AUTH_REQUIRED_RESPONSE_IO_PENDING; + PauseIncomingMethodCallProcessing(); + + OnAuthRequiredComplete(auth_rv); +} + +void ProxyingWebSocket::PauseIncomingMethodCallProcessing() { + receiver_as_handshake_client_.Pause(); + receiver_as_auth_handler_.Pause(); + if (receiver_as_header_client_.is_bound()) + receiver_as_header_client_.Pause(); +} + +void ProxyingWebSocket::ResumeIncomingMethodCallProcessing() { + receiver_as_handshake_client_.Resume(); + receiver_as_auth_handler_.Resume(); + if (receiver_as_header_client_.is_bound()) + receiver_as_header_client_.Resume(); +} + +void ProxyingWebSocket::OnError(int error_code) { + if (!is_done_) { + is_done_ = true; + web_request_api_->OnErrorOccurred(&info_, request_, error_code); + } + + // Deletes |this|. + delete this; +} + +void ProxyingWebSocket::OnMojoConnectionErrorWithCustomReason( + uint32_t custom_reason, + const std::string& description) { + // Here we want to nofiy the custom reason to the client, which is why + // we reset |forwarding_handshake_client_| manually. + forwarding_handshake_client_.ResetWithReason(custom_reason, description); + OnError(net::ERR_FAILED); + // Deletes |this|. +} + +void ProxyingWebSocket::OnMojoConnectionError() { + OnError(net::ERR_FAILED); + // Deletes |this|. +} + +} // namespace electron diff --git a/shell/browser/net/proxying_websocket.h b/shell/browser/net/proxying_websocket.h new file mode 100644 index 0000000000000..0e37c9e36a021 --- /dev/null +++ b/shell/browser/net/proxying_websocket.h @@ -0,0 +1,165 @@ +// 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. + +#ifndef SHELL_BROWSER_NET_PROXYING_WEBSOCKET_H_ +#define SHELL_BROWSER_NET_PROXYING_WEBSOCKET_H_ + +#include +#include +#include +#include + +#include "base/optional.h" +#include "components/keyed_service/core/keyed_service_shutdown_notifier.h" +#include "extensions/browser/api/web_request/web_request_api.h" +#include "extensions/browser/api/web_request/web_request_info.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "services/network/public/mojom/websocket.mojom.h" +#include "shell/browser/net/web_request_api_interface.h" +#include "url/gurl.h" +#include "url/origin.h" + +namespace electron { + +// A ProxyingWebSocket proxies a WebSocket connection and dispatches +// WebRequest API events. +// +// The code is referenced from the +// extensions::WebRequestProxyingWebSocket class. +class ProxyingWebSocket : public network::mojom::WebSocketHandshakeClient, + public network::mojom::AuthenticationHandler, + public network::mojom::TrustedHeaderClient { + public: + using WebSocketFactory = content::ContentBrowserClient::WebSocketFactory; + + ProxyingWebSocket( + WebRequestAPI* web_request_api, + WebSocketFactory factory, + const network::ResourceRequest& request, + mojo::PendingRemote + handshake_client, + bool has_extra_headers, + int process_id, + int render_frame_id, + content::BrowserContext* browser_context, + uint64_t* request_id_generator); + ~ProxyingWebSocket() override; + + void Start(); + + // network::mojom::WebSocketHandshakeClient methods: + void OnOpeningHandshakeStarted( + network::mojom::WebSocketHandshakeRequestPtr request) override; + void OnConnectionEstablished( + mojo::PendingRemote websocket, + mojo::PendingReceiver client_receiver, + network::mojom::WebSocketHandshakeResponsePtr response, + mojo::ScopedDataPipeConsumerHandle readable) override; + + // network::mojom::AuthenticationHandler method: + void OnAuthRequired(const net::AuthChallengeInfo& auth_info, + const scoped_refptr& headers, + const net::IPEndPoint& remote_endpoint, + OnAuthRequiredCallback callback) override; + + // network::mojom::TrustedHeaderClient methods: + void OnBeforeSendHeaders(const net::HttpRequestHeaders& headers, + OnBeforeSendHeadersCallback callback) override; + void OnHeadersReceived(const std::string& headers, + const net::IPEndPoint& endpoint, + OnHeadersReceivedCallback callback) override; + + static void StartProxying( + WebRequestAPI* web_request_api, + WebSocketFactory factory, + const GURL& url, + const GURL& site_for_cookies, + const base::Optional& user_agent, + mojo::PendingRemote + handshake_client, + bool has_extra_headers, + int process_id, + int render_frame_id, + const url::Origin& origin, + content::BrowserContext* browser_context, + uint64_t* request_id_generator); + + WebRequestAPI* web_request_api() { return web_request_api_; } + + private: + void OnBeforeRequestComplete(int error_code); + void OnBeforeSendHeadersComplete(const std::set& removed_headers, + const std::set& set_headers, + int error_code); + void ContinueToStartRequest(int error_code); + void OnHeadersReceivedComplete(int error_code); + void ContinueToHeadersReceived(); + void OnAuthRequiredComplete( + extensions::ExtensionWebRequestEventRouter::AuthRequiredResponse rv); + void OnHeadersReceivedCompleteForAuth(const net::AuthChallengeInfo& auth_info, + int rv); + void ContinueToCompleted(); + + void PauseIncomingMethodCallProcessing(); + void ResumeIncomingMethodCallProcessing(); + void OnError(int result); + // This is used for detecting errors on mojo connection with the network + // service. + void OnMojoConnectionErrorWithCustomReason(uint32_t custom_reason, + const std::string& description); + // This is used for detecting errors on mojo connection with original client + // (i.e., renderer). + void OnMojoConnectionError(); + + // Passed from api::WebRequest. + WebRequestAPI* web_request_api_; + + // Saved to feed the api::WebRequest. + network::ResourceRequest request_; + + WebSocketFactory factory_; + mojo::Remote + forwarding_handshake_client_; + mojo::Receiver + receiver_as_handshake_client_{this}; + mojo::Receiver + receiver_as_auth_handler_{this}; + mojo::Receiver + receiver_as_header_client_{this}; + + net::HttpRequestHeaders request_headers_; + network::mojom::URLResponseHeadPtr response_; + net::AuthCredentials auth_credentials_; + OnAuthRequiredCallback auth_required_callback_; + scoped_refptr override_headers_; + std::vector additional_headers_; + + OnBeforeSendHeadersCallback on_before_send_headers_callback_; + OnHeadersReceivedCallback on_headers_received_callback_; + + GURL redirect_url_; + bool is_done_ = false; + bool has_extra_headers_; + mojo::PendingRemote websocket_; + mojo::PendingReceiver client_receiver_; + network::mojom::WebSocketHandshakeResponsePtr handshake_response_ = nullptr; + mojo::ScopedDataPipeConsumerHandle readable_; + + extensions::WebRequestInfo info_; + + // Notifies the proxy that the browser context has been shutdown. + std::unique_ptr + shutdown_notifier_; + + base::WeakPtrFactory weak_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(ProxyingWebSocket); +}; + +} // namespace electron + +#endif // SHELL_BROWSER_NET_PROXYING_WEBSOCKET_H_ diff --git a/shell/browser/net/resolve_proxy_helper.cc b/shell/browser/net/resolve_proxy_helper.cc index 40d463889d314..e3da04bc6e304 100644 --- a/shell/browser/net/resolve_proxy_helper.cc +++ b/shell/browser/net/resolve_proxy_helper.cc @@ -9,22 +9,22 @@ #include "base/bind.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" -#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "net/proxy_resolution/proxy_info.h" #include "services/network/public/mojom/network_context.mojom.h" -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/electron_browser_context.h" using content::BrowserThread; namespace electron { -ResolveProxyHelper::ResolveProxyHelper(AtomBrowserContext* browser_context) - : binding_(this), browser_context_(browser_context) {} +ResolveProxyHelper::ResolveProxyHelper(ElectronBrowserContext* browser_context) + : browser_context_(browser_context) {} ResolveProxyHelper::~ResolveProxyHelper() { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!owned_self_); - DCHECK(!binding_.is_bound()); + DCHECK(!receiver_.is_bound()); // Clear all pending requests if the ProxyService is still alive. pending_requests_.clear(); } @@ -36,7 +36,7 @@ void ResolveProxyHelper::ResolveProxy(const GURL& url, pending_requests_.emplace_back(url, std::move(callback)); // If nothing is in progress, start. - if (!binding_.is_bound()) { + if (!receiver_.is_bound()) { DCHECK_EQ(1u, pending_requests_.size()); StartPendingRequest(); } @@ -44,18 +44,19 @@ void ResolveProxyHelper::ResolveProxy(const GURL& url, void ResolveProxyHelper::StartPendingRequest() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - DCHECK(!binding_.is_bound()); + DCHECK(!receiver_.is_bound()); DCHECK(!pending_requests_.empty()); // Start the request. - network::mojom::ProxyLookupClientPtr proxy_lookup_client; - binding_.Bind(mojo::MakeRequest(&proxy_lookup_client)); - binding_.set_connection_error_handler( + mojo::PendingRemote proxy_lookup_client = + receiver_.BindNewPipeAndPassRemote(); + receiver_.set_disconnect_handler( base::BindOnce(&ResolveProxyHelper::OnProxyLookupComplete, base::Unretained(this), net::ERR_ABORTED, base::nullopt)); content::BrowserContext::GetDefaultStoragePartition(browser_context_) ->GetNetworkContext() ->LookUpProxyForURL(pending_requests_.front().url, + net::NetworkIsolationKey::Todo(), std::move(proxy_lookup_client)); } @@ -65,7 +66,7 @@ void ResolveProxyHelper::OnProxyLookupComplete( DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!pending_requests_.empty()); - binding_.Close(); + receiver_.reset(); // Clear the current (completed) request. PendingRequest completed_request = std::move(pending_requests_.front()); diff --git a/shell/browser/net/resolve_proxy_helper.h b/shell/browser/net/resolve_proxy_helper.h index bfbc4197fee34..be330bf2f12a5 100644 --- a/shell/browser/net/resolve_proxy_helper.h +++ b/shell/browser/net/resolve_proxy_helper.h @@ -10,13 +10,13 @@ #include "base/memory/ref_counted.h" #include "base/optional.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/receiver.h" #include "services/network/public/mojom/proxy_lookup_client.mojom.h" #include "url/gurl.h" namespace electron { -class AtomBrowserContext; +class ElectronBrowserContext; class ResolveProxyHelper : public base::RefCountedThreadSafe, @@ -24,7 +24,7 @@ class ResolveProxyHelper public: using ResolveProxyCallback = base::OnceCallback; - explicit ResolveProxyHelper(AtomBrowserContext* browser_context); + explicit ResolveProxyHelper(ElectronBrowserContext* browser_context); void ResolveProxy(const GURL& url, ResolveProxyCallback callback); @@ -61,11 +61,11 @@ class ResolveProxyHelper scoped_refptr owned_self_; std::deque pending_requests_; - // Binding for the currently in-progress request, if any. - mojo::Binding binding_; + // Receiver for the currently in-progress request, if any. + mojo::Receiver receiver_{this}; // Weak Ref - AtomBrowserContext* browser_context_; + ElectronBrowserContext* browser_context_; DISALLOW_COPY_AND_ASSIGN(ResolveProxyHelper); }; diff --git a/shell/browser/net/system_network_context_manager.cc b/shell/browser/net/system_network_context_manager.cc index f737dd4d058cc..62fae42b9ea47 100644 --- a/shell/browser/net/system_network_context_manager.cc +++ b/shell/browser/net/system_network_context_manager.cc @@ -11,16 +11,18 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/cors_exempt_headers.h" #include "content/public/browser/network_service_instance.h" #include "content/public/common/content_features.h" #include "content/public/common/service_names.mojom.h" #include "mojo/public/cpp/bindings/associated_interface_ptr.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" #include "net/net_buildflags.h" #include "services/network/network_service.h" -#include "services/network/public/cpp/cross_thread_shared_url_loader_factory_info.h" +#include "services/network/public/cpp/cross_thread_pending_shared_url_loader_factory.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/shared_url_loader_factory.h" -#include "shell/browser/atom_browser_client.h" +#include "shell/browser/electron_browser_client.h" #include "shell/common/application_info.h" #include "shell/common/options_switches.h" #include "url/gurl.h" @@ -68,14 +70,15 @@ class SystemNetworkContextManager::URLLoaderFactoryForSystem } // mojom::URLLoaderFactory implementation: - void CreateLoaderAndStart(network::mojom::URLLoaderRequest request, - int32_t routing_id, - int32_t request_id, - uint32_t options, - const network::ResourceRequest& url_request, - network::mojom::URLLoaderClientPtr client, - const net::MutableNetworkTrafficAnnotationTag& - traffic_annotation) override { + void CreateLoaderAndStart( + mojo::PendingReceiver request, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& url_request, + mojo::PendingRemote client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!manager_) return; @@ -84,17 +87,18 @@ class SystemNetworkContextManager::URLLoaderFactoryForSystem std::move(client), traffic_annotation); } - void Clone(network::mojom::URLLoaderFactoryRequest request) override { + void Clone(mojo::PendingReceiver receiver) + override { if (!manager_) return; - manager_->GetURLLoaderFactory()->Clone(std::move(request)); + manager_->GetURLLoaderFactory()->Clone(std::move(receiver)); } // SharedURLLoaderFactory implementation: - std::unique_ptr Clone() override { + std::unique_ptr Clone() override { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - return std::make_unique( + return std::make_unique( this); } @@ -127,7 +131,7 @@ network::mojom::NetworkContext* SystemNetworkContextManager::GetContext() { network::mojom::URLLoaderFactory* SystemNetworkContextManager::GetURLLoaderFactory() { // Create the URLLoaderFactory as needed. - if (url_loader_factory_ && !url_loader_factory_.encountered_error()) { + if (url_loader_factory_ && url_loader_factory_.is_connected()) { return url_loader_factory_.get(); } @@ -135,8 +139,9 @@ SystemNetworkContextManager::GetURLLoaderFactory() { network::mojom::URLLoaderFactoryParams::New(); params->process_id = network::mojom::kBrowserProcessId; params->is_corb_enabled = false; - GetContext()->CreateURLLoaderFactory(mojo::MakeRequest(&url_loader_factory_), - std::move(params)); + url_loader_factory_.reset(); + GetContext()->CreateURLLoaderFactory( + url_loader_factory_.BindNewPipeAndPassReceiver(), std::move(params)); return url_loader_factory_.get(); } @@ -150,6 +155,10 @@ SystemNetworkContextManager::CreateDefaultNetworkContextParams() { network::mojom::NetworkContextParamsPtr network_context_params = network::mojom::NetworkContextParams::New(); + // This is required to avoid blocking X-Requested-With headers sent by PPAPI + // plugins, more info crbug.com/940331 + content::UpdateCorsExemptHeader(network_context_params.get()); + network_context_params->enable_brotli = true; network_context_params->enable_referrers = true; @@ -216,7 +225,7 @@ SystemNetworkContextManager::CreateNetworkContextParams() { network_context_params->context_name = std::string("system"); network_context_params->user_agent = - electron::AtomBrowserClient::Get()->GetUserAgent(); + electron::ElectronBrowserClient::Get()->GetUserAgent(); network_context_params->http_cache_enabled = false; diff --git a/shell/browser/net/system_network_context_manager.h b/shell/browser/net/system_network_context_manager.h index 8a9368ac225b5..9c851f9da7171 100644 --- a/shell/browser/net/system_network_context_manager.h +++ b/shell/browser/net/system_network_context_manager.h @@ -97,7 +97,7 @@ class SystemNetworkContextManager { // URLLoaderFactory backed by the NetworkContext returned by GetContext(), so // consumers don't all need to create their own factory. scoped_refptr shared_url_loader_factory_; - network::mojom::URLLoaderFactoryPtr url_loader_factory_; + mojo::Remote url_loader_factory_; DISALLOW_COPY_AND_ASSIGN(SystemNetworkContextManager); }; diff --git a/shell/browser/net/url_pipe_loader.cc b/shell/browser/net/url_pipe_loader.cc index e127fcbbedb2e..f928eaa77325e 100644 --- a/shell/browser/net/url_pipe_loader.cc +++ b/shell/browser/net/url_pipe_loader.cc @@ -6,6 +6,7 @@ #include +#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/system/string_data_source.h" #include "services/network/public/cpp/shared_url_loader_factory.h" @@ -15,7 +16,7 @@ URLPipeLoader::URLPipeLoader( scoped_refptr factory, std::unique_ptr request, network::mojom::URLLoaderRequest loader, - network::mojom::URLLoaderClientPtr client, + mojo::PendingRemote client, const net::NetworkTrafficAnnotationTag& annotation, base::DictionaryValue upload_data) : binding_(this, std::move(loader)), diff --git a/shell/browser/net/url_pipe_loader.h b/shell/browser/net/url_pipe_loader.h index b38ccec5b4c41..09ed62c5062e9 100644 --- a/shell/browser/net/url_pipe_loader.h +++ b/shell/browser/net/url_pipe_loader.h @@ -9,6 +9,8 @@ #include #include +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/system/data_pipe_producer.h" #include "services/network/public/cpp/simple_url_loader.h" @@ -34,7 +36,7 @@ class URLPipeLoader : public network::mojom::URLLoader, URLPipeLoader(scoped_refptr factory, std::unique_ptr request, network::mojom::URLLoaderRequest loader, - network::mojom::URLLoaderClientPtr client, + mojo::PendingRemote client, const net::NetworkTrafficAnnotationTag& annotation, base::DictionaryValue upload_data); @@ -66,7 +68,7 @@ class URLPipeLoader : public network::mojom::URLLoader, void ResumeReadingBodyFromNet() override {} mojo::Binding binding_; - network::mojom::URLLoaderClientPtr client_; + mojo::Remote client_; std::unique_ptr producer_; std::unique_ptr loader_; diff --git a/shell/browser/net/web_request_api_interface.h b/shell/browser/net/web_request_api_interface.h new file mode 100644 index 0000000000000..649145f6f66aa --- /dev/null +++ b/shell/browser/net/web_request_api_interface.h @@ -0,0 +1,61 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_BROWSER_NET_WEB_REQUEST_API_INTERFACE_H_ +#define SHELL_BROWSER_NET_WEB_REQUEST_API_INTERFACE_H_ + +#include +#include + +#include "extensions/browser/api/web_request/web_request_info.h" +#include "services/network/public/cpp/resource_request.h" + +namespace electron { + +// Defines the interface for WebRequest API, implemented by api::WebRequestNS. +class WebRequestAPI { + public: + virtual ~WebRequestAPI() {} + + using BeforeSendHeadersCallback = + base::OnceCallback& removed_headers, + const std::set& set_headers, + int error_code)>; + + virtual bool HasListener() const = 0; + virtual int OnBeforeRequest(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, + net::CompletionOnceCallback callback, + GURL* new_url) = 0; + virtual int OnBeforeSendHeaders(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, + BeforeSendHeadersCallback callback, + net::HttpRequestHeaders* headers) = 0; + virtual int OnHeadersReceived( + extensions::WebRequestInfo* info, + const network::ResourceRequest& request, + net::CompletionOnceCallback callback, + const net::HttpResponseHeaders* original_response_headers, + scoped_refptr* override_response_headers, + GURL* allowed_unsafe_redirect_url) = 0; + virtual void OnSendHeaders(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, + const net::HttpRequestHeaders& headers) = 0; + virtual void OnBeforeRedirect(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, + const GURL& new_location) = 0; + virtual void OnResponseStarted(extensions::WebRequestInfo* info, + const network::ResourceRequest& request) = 0; + virtual void OnErrorOccurred(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, + int net_error) = 0; + virtual void OnCompleted(extensions::WebRequestInfo* info, + const network::ResourceRequest& request, + int net_error) = 0; + virtual void OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) = 0; +}; + +} // namespace electron + +#endif // SHELL_BROWSER_NET_WEB_REQUEST_API_INTERFACE_H_ diff --git a/shell/browser/network_hints_handler_impl.cc b/shell/browser/network_hints_handler_impl.cc new file mode 100644 index 0000000000000..4b44f11022426 --- /dev/null +++ b/shell/browser/network_hints_handler_impl.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2019 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/network_hints_handler_impl.h" + +#include + +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" +#include "shell/browser/api/electron_api_session.h" +#include "shell/browser/electron_browser_context.h" +#include "shell/common/native_mate_converters/gurl_converter.h" +#include "v8/include/v8.h" + +NetworkHintsHandlerImpl::NetworkHintsHandlerImpl( + content::RenderFrameHost* frame_host) + : network_hints::SimpleNetworkHintsHandlerImpl( + frame_host->GetProcess()->GetID()), + browser_context_(frame_host->GetProcess()->GetBrowserContext()) {} + +NetworkHintsHandlerImpl::~NetworkHintsHandlerImpl() = default; + +void NetworkHintsHandlerImpl::Preconnect(const GURL& url, + bool allow_credentials) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + if (!browser_context_) { + return; + } + auto* session = electron::api::Session::FromWrappedClass( + v8::Isolate::GetCurrent(), + static_cast(browser_context_)); + if (session) { + session->Emit("preconnect", url, allow_credentials); + } +} + +void NetworkHintsHandlerImpl::Create( + content::RenderFrameHost* frame_host, + mojo::PendingReceiver receiver) { + mojo::MakeSelfOwnedReceiver( + base::WrapUnique(new NetworkHintsHandlerImpl(frame_host)), + std::move(receiver)); +} diff --git a/shell/browser/network_hints_handler_impl.h b/shell/browser/network_hints_handler_impl.h new file mode 100644 index 0000000000000..ba49ab6f1986f --- /dev/null +++ b/shell/browser/network_hints_handler_impl.h @@ -0,0 +1,34 @@ +// Copyright (c) 2019 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_BROWSER_NETWORK_HINTS_HANDLER_IMPL_H_ +#define SHELL_BROWSER_NETWORK_HINTS_HANDLER_IMPL_H_ + +#include "components/network_hints/browser/simple_network_hints_handler_impl.h" + +namespace content { +class RenderFrameHost; +class BrowserContext; +} // namespace content + +class NetworkHintsHandlerImpl + : public network_hints::SimpleNetworkHintsHandlerImpl { + public: + ~NetworkHintsHandlerImpl() override; + + static void Create( + content::RenderFrameHost* frame_host, + mojo::PendingReceiver + receiver); + + // network_hints::mojom::NetworkHintsHandler: + void Preconnect(const GURL& url, bool allow_credentials) override; + + private: + explicit NetworkHintsHandlerImpl(content::RenderFrameHost*); + + content::BrowserContext* browser_context_ = nullptr; +}; + +#endif // SHELL_BROWSER_NETWORK_HINTS_HANDLER_IMPL_H_ diff --git a/shell/browser/notifications/linux/libnotify_notification.cc b/shell/browser/notifications/linux/libnotify_notification.cc index dc52fa5af5b1f..65f4931c836aa 100644 --- a/shell/browser/notifications/linux/libnotify_notification.cc +++ b/shell/browser/notifications/linux/libnotify_notification.cc @@ -13,8 +13,8 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/libgtkui/gtk_util.h" -#include "chrome/browser/ui/libgtkui/skia_utils_gtk.h" #include "shell/browser/notifications/notification_delegate.h" +#include "shell/browser/ui/gtk_util.h" #include "shell/common/application_info.h" #include "shell/common/platform_util.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -111,7 +111,7 @@ void LibnotifyNotification::Show(const NotificationOptions& options) { libnotify_loader_.notify_notification_set_urgency(notification_, urgency); if (!options.icon.drawsNothing()) { - GdkPixbuf* pixbuf = libgtkui::GdkPixbufFromSkBitmap(options.icon); + GdkPixbuf* pixbuf = gtk_util::GdkPixbufFromSkBitmap(options.icon); libnotify_loader_.notify_notification_set_image_from_pixbuf(notification_, pixbuf); g_object_unref(pixbuf); diff --git a/shell/browser/notifications/platform_notification_service.cc b/shell/browser/notifications/platform_notification_service.cc index 159c2dbff9aff..f6f15bfdb542c 100644 --- a/shell/browser/notifications/platform_notification_service.cc +++ b/shell/browser/notifications/platform_notification_service.cc @@ -7,7 +7,7 @@ #include "base/strings/utf_string_conversions.h" #include "content/public/browser/notification_event_dispatcher.h" #include "content/public/browser/render_process_host.h" -#include "shell/browser/atom_browser_client.h" +#include "shell/browser/electron_browser_client.h" #include "shell/browser/notifications/notification.h" #include "shell/browser/notifications/notification_delegate.h" #include "shell/browser/notifications/notification_presenter.h" @@ -72,7 +72,7 @@ class NotificationDelegateImpl final : public electron::NotificationDelegate { } // namespace PlatformNotificationService::PlatformNotificationService( - AtomBrowserClient* browser_client) + ElectronBrowserClient* browser_client) : browser_client_(browser_client) {} PlatformNotificationService::~PlatformNotificationService() = default; diff --git a/shell/browser/notifications/platform_notification_service.h b/shell/browser/notifications/platform_notification_service.h index c5946573335bc..53d93abd202ca 100644 --- a/shell/browser/notifications/platform_notification_service.h +++ b/shell/browser/notifications/platform_notification_service.h @@ -12,12 +12,12 @@ namespace electron { -class AtomBrowserClient; +class ElectronBrowserClient; class PlatformNotificationService : public content::PlatformNotificationService { public: - explicit PlatformNotificationService(AtomBrowserClient* browser_client); + explicit PlatformNotificationService(ElectronBrowserClient* browser_client); ~PlatformNotificationService() override; protected: @@ -45,7 +45,7 @@ class PlatformNotificationService base::Time ReadNextTriggerTimestamp() override; private: - AtomBrowserClient* browser_client_; + ElectronBrowserClient* browser_client_; DISALLOW_COPY_AND_ASSIGN(PlatformNotificationService); }; diff --git a/shell/browser/notifications/win/win32_desktop_notifications/desktop_notification_controller.cc b/shell/browser/notifications/win/win32_desktop_notifications/desktop_notification_controller.cc index 1df2e95339f6e..4af49f7b27ce7 100644 --- a/shell/browser/notifications/win/win32_desktop_notifications/desktop_notification_controller.cc +++ b/shell/browser/notifications/win/win32_desktop_notifications/desktop_notification_controller.cc @@ -20,9 +20,6 @@ #include "shell/browser/notifications/win/win32_desktop_notifications/common.h" #include "shell/browser/notifications/win/win32_desktop_notifications/toast.h" -using std::make_shared; -using std::shared_ptr; - namespace electron { HBITMAP CopyBitmap(HBITMAP bitmap) { @@ -281,8 +278,8 @@ DesktopNotificationController::Notification DesktopNotificationController::AddNotification(std::wstring caption, std::wstring body_text, HBITMAP image) { - NotificationLink data(this); - + auto data = std::make_shared(); + data->controller = this; data->caption = move(caption); data->body_text = move(body_text); data->image = CopyBitmap(image); @@ -298,6 +295,7 @@ void DesktopNotificationController::CloseNotification( // Remove it from the queue auto it = find(queue_.begin(), queue_.end(), notification.data_); if (it != queue_.end()) { + (*it)->controller = nullptr; queue_.erase(it); this->OnNotificationClosed(notification); return; @@ -318,7 +316,8 @@ void DesktopNotificationController::CheckQueue() { } } -void DesktopNotificationController::CreateToast(NotificationLink&& data) { +void DesktopNotificationController::CreateToast( + std::shared_ptr&& data) { auto* hinstance = RegisterWndClasses(); auto* hwnd = Toast::Create(hinstance, data); if (hwnd) { @@ -333,7 +332,7 @@ void DesktopNotificationController::CreateToast(NotificationLink&& data) { scr.Y(toast_margin_); } - instances_.push_back({hwnd, move(data)}); + instances_.push_back({hwnd, std::move(data)}); if (!hwnd_controller_) { // NOTE: We cannot use a message-only window because we need to @@ -377,7 +376,7 @@ DesktopNotificationController::Notification::Notification( const DesktopNotificationController::Notification&) = default; DesktopNotificationController::Notification::Notification( - const shared_ptr& data) + const std::shared_ptr& data) : data_(data) { DCHECK(data != nullptr); } @@ -424,16 +423,14 @@ void DesktopNotificationController::Notification::Set(std::wstring caption, data_->controller->StartAnimation(); } -DesktopNotificationController::NotificationLink::NotificationLink( - DesktopNotificationController* controller) - : shared_ptr(make_shared()) { - get()->controller = controller; -} - -DesktopNotificationController::NotificationLink::~NotificationLink() { - auto* p = get(); - if (p) - p->controller = nullptr; +DesktopNotificationController::ToastInstance::ToastInstance( + HWND hwnd, + std::shared_ptr data) { + this->hwnd = hwnd; + this->data = std::move(data); } +DesktopNotificationController::ToastInstance::~ToastInstance() = default; +DesktopNotificationController::ToastInstance::ToastInstance(ToastInstance&&) = + default; } // namespace electron diff --git a/shell/browser/notifications/win/win32_desktop_notifications/desktop_notification_controller.h b/shell/browser/notifications/win/win32_desktop_notifications/desktop_notification_controller.h index c1cf4d1b52b08..3570e26ec8e0e 100644 --- a/shell/browser/notifications/win/win32_desktop_notifications/desktop_notification_controller.h +++ b/shell/browser/notifications/win/win32_desktop_notifications/desktop_notification_controller.h @@ -28,39 +28,35 @@ class DesktopNotificationController { // Event handlers -- override to receive the events private: + class Toast; + DesktopNotificationController(const DesktopNotificationController&) = delete; + + struct ToastInstance { + ToastInstance(HWND, std::shared_ptr); + ~ToastInstance(); + ToastInstance(ToastInstance&&); + ToastInstance(const ToastInstance&) = delete; + ToastInstance& operator=(ToastInstance&&) = default; + + HWND hwnd; + std::shared_ptr data; + }; + virtual void OnNotificationClosed(const Notification& notification) {} virtual void OnNotificationClicked(const Notification& notification) {} virtual void OnNotificationDismissed(const Notification& notification) {} - private: static HINSTANCE RegisterWndClasses(); void StartAnimation(); HFONT GetCaptionFont(); HFONT GetBodyFont(); - - private: - enum TimerID { TimerID_Animate = 1 }; - - static constexpr int toast_margin_ = 20; - - // Wrapper around `NotificationData` which makes sure that - // the `controller` member is cleared when the controller object - // stops tracking the notification - struct NotificationLink : std::shared_ptr { - explicit NotificationLink(DesktopNotificationController* controller); - ~NotificationLink(); - - NotificationLink(NotificationLink&&) = default; - NotificationLink(const NotificationLink&) = delete; - NotificationLink& operator=(NotificationLink&&) = default; - }; - - struct ToastInstance { - HWND hwnd; - NotificationLink data; - }; - - class Toast; + void InitializeFonts(); + void ClearAssets(); + void AnimateAll(); + void CheckQueue(); + void CreateToast(std::shared_ptr&& data); + HWND GetToast(const NotificationData* data) const; + void DestroyToast(ToastInstance* inst); static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, @@ -71,23 +67,13 @@ class DesktopNotificationController { GetWindowLongPtr(hwnd, 0)); } - DesktopNotificationController(const DesktopNotificationController&) = delete; - - void InitializeFonts(); - void ClearAssets(); - void AnimateAll(); - void CheckQueue(); - void CreateToast(NotificationLink&& data); - HWND GetToast(const NotificationData* data) const; - void DestroyToast(ToastInstance* inst); - - private: + static constexpr int toast_margin_ = 20; static const TCHAR class_name_[]; - + enum TimerID { TimerID_Animate = 1 }; HWND hwnd_controller_ = NULL; HFONT caption_font_ = NULL, body_font_ = NULL; std::vector instances_; - std::deque queue_; + std::deque> queue_; bool is_animating_ = false; }; diff --git a/shell/browser/osr/osr_host_display_client.cc b/shell/browser/osr/osr_host_display_client.cc index 043c2010388c1..f347deeeae047 100644 --- a/shell/browser/osr/osr_host_display_client.cc +++ b/shell/browser/osr/osr_host_display_client.cc @@ -6,7 +6,6 @@ #include -#include "base/memory/shared_memory.h" #include "components/viz/common/resources/resource_format.h" #include "components/viz/common/resources/resource_sizes.h" #include "mojo/public/cpp/system/platform_handle.h" @@ -23,9 +22,9 @@ namespace electron { LayeredWindowUpdater::LayeredWindowUpdater( - viz::mojom::LayeredWindowUpdaterRequest request, + mojo::PendingReceiver receiver, OnPaintCallback callback) - : callback_(callback), binding_(this, std::move(request)) {} + : callback_(callback), receiver_(this, std::move(receiver)) {} LayeredWindowUpdater::~LayeredWindowUpdater() = default; @@ -96,10 +95,15 @@ void OffScreenHostDisplayClient::IsOffscreen(IsOffscreenCallback callback) { } void OffScreenHostDisplayClient::CreateLayeredWindowUpdater( - viz::mojom::LayeredWindowUpdaterRequest request) { + mojo::PendingReceiver receiver) { layered_window_updater_ = - std::make_unique(std::move(request), callback_); + std::make_unique(std::move(receiver), callback_); layered_window_updater_->SetActive(active_); } +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) +void OffScreenHostDisplayClient::DidCompleteSwapWithNewSize( + const gfx::Size& size) {} +#endif + } // namespace electron diff --git a/shell/browser/osr/osr_host_display_client.h b/shell/browser/osr/osr_host_display_client.h index 99947d5fa61c0..c2b56ba62012d 100644 --- a/shell/browser/osr/osr_host_display_client.h +++ b/shell/browser/osr/osr_host_display_client.h @@ -21,8 +21,9 @@ typedef base::Callback OnPaintCallback; class LayeredWindowUpdater : public viz::mojom::LayeredWindowUpdater { public: - explicit LayeredWindowUpdater(viz::mojom::LayeredWindowUpdaterRequest request, - OnPaintCallback callback); + explicit LayeredWindowUpdater( + mojo::PendingReceiver receiver, + OnPaintCallback callback); ~LayeredWindowUpdater() override; void SetActive(bool active); @@ -34,7 +35,7 @@ class LayeredWindowUpdater : public viz::mojom::LayeredWindowUpdater { private: OnPaintCallback callback_; - mojo::Binding binding_; + mojo::Receiver receiver_; std::unique_ptr canvas_; bool active_ = false; @@ -62,7 +63,12 @@ class OffScreenHostDisplayClient : public viz::HostDisplayClient { #endif void CreateLayeredWindowUpdater( - viz::mojom::LayeredWindowUpdaterRequest request) override; + mojo::PendingReceiver receiver) + override; + +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + void DidCompleteSwapWithNewSize(const gfx::Size& size) override; +#endif std::unique_ptr layered_window_updater_; OnPaintCallback callback_; diff --git a/shell/browser/osr/osr_render_widget_host_view.cc b/shell/browser/osr/osr_render_widget_host_view.cc index 141b0a4a20463..beaea8313461f 100644 --- a/shell/browser/osr/osr_render_widget_host_view.cc +++ b/shell/browser/osr/osr_render_widget_host_view.cc @@ -17,6 +17,7 @@ #include "base/task/post_task.h" #include "base/time/time.h" #include "components/viz/common/features.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/delay_based_time_source.h" #include "components/viz/common/gl_helper.h" @@ -121,10 +122,10 @@ ui::MouseWheelEvent UiMouseWheelEventFromWebMouseEvent( } // namespace -class AtomBeginFrameTimer : public viz::DelayBasedTimeSourceClient { +class ElectronBeginFrameTimer : public viz::DelayBasedTimeSourceClient { public: - AtomBeginFrameTimer(int frame_rate_threshold_us, - const base::Closure& callback) + ElectronBeginFrameTimer(int frame_rate_threshold_us, + const base::Closure& callback) : callback_(callback) { time_source_ = std::make_unique( base::CreateSingleThreadTaskRunner({content::BrowserThread::UI}).get()); @@ -150,12 +151,12 @@ class AtomBeginFrameTimer : public viz::DelayBasedTimeSourceClient { const base::Closure callback_; std::unique_ptr time_source_; - DISALLOW_COPY_AND_ASSIGN(AtomBeginFrameTimer); + DISALLOW_COPY_AND_ASSIGN(ElectronBeginFrameTimer); }; - -class AtomDelegatedFrameHostClient : public content::DelegatedFrameHostClient { +class ElectronDelegatedFrameHostClient + : public content::DelegatedFrameHostClient { public: - explicit AtomDelegatedFrameHostClient(OffScreenRenderWidgetHostView* view) + explicit ElectronDelegatedFrameHostClient(OffScreenRenderWidgetHostView* view) : view_(view) {} ui::Layer* DelegatedFrameHostGetLayer() const override { @@ -194,7 +195,7 @@ class AtomDelegatedFrameHostClient : public content::DelegatedFrameHostClient { private: OffScreenRenderWidgetHostView* const view_; - DISALLOW_COPY_AND_ASSIGN(AtomDelegatedFrameHostClient); + DISALLOW_COPY_AND_ASSIGN(ElectronDelegatedFrameHostClient); }; OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView( @@ -219,7 +220,7 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView( backing_(new SkBitmap), weak_ptr_factory_(this) { DCHECK(render_widget_host_); - bool is_guest_view_hack = parent_host_view_ != nullptr; + DCHECK(!render_widget_host_->GetView()); current_device_scale_factor_ = kDefaultScaleFactor; @@ -231,10 +232,9 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView( compositor_allocator_.GetCurrentLocalSurfaceIdAllocation(); delegated_frame_host_client_ = - std::make_unique(this); + std::make_unique(this); delegated_frame_host_ = std::make_unique( - AllocateFrameSinkId(is_guest_view_hack), - delegated_frame_host_client_.get(), + AllocateFrameSinkId(), delegated_frame_host_client_.get(), true /* should_register_frame_sink_id */); root_layer_ = std::make_unique(ui::LAYER_SOLID_COLOR); @@ -243,21 +243,17 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView( GetRootLayer()->SetFillsBoundsOpaquely(opaque); GetRootLayer()->SetColor(background_color_); - content::ImageTransportFactory* factory = - content::ImageTransportFactory::GetInstance(); - ui::ContextFactoryPrivate* context_factory_private = - factory->GetContextFactoryPrivate(); + content::GetContextFactoryPrivate(); compositor_ = std::make_unique( context_factory_private->AllocateFrameSinkId(), content::GetContextFactory(), context_factory_private, base::ThreadTaskRunnerHandle::Get(), false /* enable_pixel_canvas */, false /* use_external_begin_frame_control */); compositor_->SetAcceleratedWidget(gfx::kNullAcceleratedWidget); + compositor_->SetDelegate(this); compositor_->SetRootLayer(root_layer_.get()); - GetCompositor()->SetDelegate(this); - ResizeRootLayer(false); render_widget_host_->SetView(this); InstallTransparency(); @@ -320,7 +316,6 @@ void OffScreenRenderWidgetHostView::SendBeginFrame( compositor_.get(), begin_frame_args, /* force */ true, base::NullCallback()); } - void OffScreenRenderWidgetHostView::InitAsChild(gfx::NativeView) { DCHECK(parent_host_view_); @@ -449,8 +444,6 @@ void OffScreenRenderWidgetHostView::TakeFallbackContentFrom( content::RenderWidgetHostView* view) { DCHECK(!static_cast(view) ->IsRenderWidgetHostViewChildFrame()); - DCHECK(!static_cast(view) - ->IsRenderWidgetHostViewGuest()); OffScreenRenderWidgetHostView* view_osr = static_cast(view); SetBackgroundColor(view_osr->background_color_); @@ -477,7 +470,6 @@ void OffScreenRenderWidgetHostView::SubmitCompositorFrame( base::Optional hit_test_region_list) { NOTREACHED(); } - void OffScreenRenderWidgetHostView::ResetFallbackToFirstNavigationSurface() { GetDelegatedFrameHost()->ResetFallbackToFirstNavigationSurface(); } @@ -581,13 +573,6 @@ void OffScreenRenderWidgetHostView::GetScreenInfo( screen_info->available_rect = gfx::Rect(size_); } -void OffScreenRenderWidgetHostView::InitAsGuest( - content::RenderWidgetHostView* parent_host_view, - content::RenderWidgetHostViewGuest* guest_view) { - parent_host_view_->AddGuestHostView(this); - SetPainting(parent_host_view_->IsPainting()); -} - void OffScreenRenderWidgetHostView::TransformPointToRootSurface( gfx::PointF* point) {} @@ -736,7 +721,6 @@ void OffScreenRenderWidgetHostView::SetNeedsBeginFrames( } void OffScreenRenderWidgetHostView::SetWantsAnimateOnlyBeginFrames() {} - #if defined(OS_MACOSX) void OffScreenRenderWidgetHostView::SetActive(bool active) {} @@ -1055,7 +1039,7 @@ void OffScreenRenderWidgetHostView::SetupFrameRate(bool force) { if (begin_frame_timer_.get()) { begin_frame_timer_->SetFrameRateThresholdUs(frame_rate_threshold_us_); } else { - begin_frame_timer_ = std::make_unique( + begin_frame_timer_ = std::make_unique( frame_rate_threshold_us_, base::BindRepeating( &OffScreenRenderWidgetHostView::OnBeginFrameTimerTick, @@ -1097,18 +1081,18 @@ void OffScreenRenderWidgetHostView::ResizeRootLayer(bool force) { const gfx::Size& size_in_pixels = gfx::ConvertSizeToPixel(current_device_scale_factor_, size); - compositor_allocator_.GenerateId(); - compositor_allocation_ = - compositor_allocator_.GetCurrentLocalSurfaceIdAllocation(); - - GetCompositor()->SetScaleAndSize(current_device_scale_factor_, size_in_pixels, - compositor_allocation_); + if (compositor_) { + compositor_allocator_.GenerateId(); + compositor_allocation_ = + compositor_allocator_.GetCurrentLocalSurfaceIdAllocation(); + compositor_->SetScaleAndSize(current_device_scale_factor_, size_in_pixels, + compositor_allocation_); + } delegated_frame_host_allocator_.GenerateId(); delegated_frame_host_allocation_ = delegated_frame_host_allocator_.GetCurrentLocalSurfaceIdAllocation(); - bool resized = true; GetDelegatedFrameHost()->EmbedSurface( delegated_frame_host_allocation_.local_surface_id(), size, cc::DeadlinePolicy::UseDefaultDeadline()); @@ -1116,25 +1100,15 @@ void OffScreenRenderWidgetHostView::ResizeRootLayer(bool force) { // Note that |render_widget_host_| will retrieve resize parameters from the // DelegatedFrameHost, so it must have SynchronizeVisualProperties called // after. - if (resized && render_widget_host_) { + if (render_widget_host_) { render_widget_host_->SynchronizeVisualProperties(); } } -viz::FrameSinkId OffScreenRenderWidgetHostView::AllocateFrameSinkId( - bool is_guest_view_hack) { - // GuestViews have two RenderWidgetHostViews and so we need to make sure - // we don't have FrameSinkId collisions. - // The FrameSinkId generated here must be unique with FrameSinkId allocated - // in ContextFactoryPrivate. - content::ImageTransportFactory* factory = - content::ImageTransportFactory::GetInstance(); - return is_guest_view_hack - ? factory->GetContextFactoryPrivate()->AllocateFrameSinkId() - : viz::FrameSinkId(base::checked_cast( - render_widget_host_->GetProcess()->GetID()), - base::checked_cast( - render_widget_host_->GetRoutingID())); +viz::FrameSinkId OffScreenRenderWidgetHostView::AllocateFrameSinkId() { + return viz::FrameSinkId( + base::checked_cast(render_widget_host_->GetProcess()->GetID()), + base::checked_cast(render_widget_host_->GetRoutingID())); } void OffScreenRenderWidgetHostView::UpdateBackgroundColorFromRenderer( diff --git a/shell/browser/osr/osr_render_widget_host_view.h b/shell/browser/osr/osr_render_widget_host_view.h index 35225f3ac3452..caccfca52f6da 100644 --- a/shell/browser/osr/osr_render_widget_host_view.h +++ b/shell/browser/osr/osr_render_widget_host_view.h @@ -21,7 +21,6 @@ #include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" -#include "content/browser/frame_host/render_widget_host_view_guest.h" // nogncheck #include "content/browser/renderer_host/delegated_frame_host.h" // nogncheck #include "content/browser/renderer_host/input/mouse_wheel_phase_handler.h" // nogncheck #include "content/browser/renderer_host/render_widget_host_impl.h" // nogncheck @@ -50,10 +49,10 @@ class CursorManager; namespace electron { -class AtomCopyFrameGenerator; -class AtomBeginFrameTimer; +class ElectronCopyFrameGenerator; +class ElectronBeginFrameTimer; -class AtomDelegatedFrameHostClient; +class ElectronDelegatedFrameHostClient; typedef base::Callback OnPaintCallback; typedef base::Callback OnPopupPaintCallback; @@ -134,8 +133,6 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase, const gfx::Size& output_size, base::OnceCallback callback) override; void GetScreenInfo(content::ScreenInfo* results) override; - void InitAsGuest(content::RenderWidgetHostView*, - content::RenderWidgetHostViewGuest*) override; void TransformPointToRootSurface(gfx::PointF* point) override; gfx::Rect GetBoundsInRootWindow(void) override; viz::SurfaceId GetCurrentSurfaceId() const override; @@ -169,7 +166,6 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase, void OnBeginFrameTimerTick(); void SendBeginFrame(base::TimeTicks frame_time, base::TimeDelta vsync_period); - void CancelWidget(); void AddGuestHostView(OffScreenRenderWidgetHostView* guest_host); void RemoveGuestHostView(OffScreenRenderWidgetHostView* guest_host); @@ -230,7 +226,7 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase, void SetupFrameRate(bool force); void ResizeRootLayer(bool force); - viz::FrameSinkId AllocateFrameSinkId(bool is_guest_view_hack); + viz::FrameSinkId AllocateFrameSinkId(); // Applies background color without notifying the RenderWidget about // opaqueness changes. @@ -279,18 +275,17 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase, std::unique_ptr cursor_manager_; - std::unique_ptr begin_frame_timer_; + std::unique_ptr begin_frame_timer_; OffScreenHostDisplayClient* host_display_client_; std::unique_ptr video_consumer_; // Provides |source_id| for BeginFrameArgs that we create. viz::StubBeginFrameSource begin_frame_source_; uint64_t begin_frame_number_ = viz::BeginFrameArgs::kStartingFrameNumber; - - std::unique_ptr delegated_frame_host_client_; + std::unique_ptr + delegated_frame_host_client_; content::MouseWheelPhaseHandler mouse_wheel_phase_handler_; - viz::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink_ = nullptr; diff --git a/shell/browser/osr/osr_video_consumer.cc b/shell/browser/osr/osr_video_consumer.cc index 2d5b1a0206768..b0bfb4524f452 100644 --- a/shell/browser/osr/osr_video_consumer.cc +++ b/shell/browser/osr/osr_video_consumer.cc @@ -54,7 +54,8 @@ void OffScreenVideoConsumer::OnFrameCaptured( base::ReadOnlySharedMemoryRegion data, ::media::mojom::VideoFrameInfoPtr info, const gfx::Rect& content_rect, - viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) { + mojo::PendingRemote + callbacks) { if (!CheckContentRect(content_rect)) { gfx::Size view_size = view_->SizeInPixels(); video_capturer_->SetResolutionConstraints(view_size, view_size, true); @@ -62,18 +63,23 @@ void OffScreenVideoConsumer::OnFrameCaptured( return; } + mojo::Remote + callbacks_remote(std::move(callbacks)); + if (!data.IsValid()) { - callbacks->Done(); + callbacks_remote->Done(); return; } base::ReadOnlySharedMemoryMapping mapping = data.Map(); if (!mapping.IsValid()) { DLOG(ERROR) << "Shared memory mapping failed."; + callbacks_remote->Done(); return; } if (mapping.size() < media::VideoFrame::AllocationSize(info->pixel_format, info->coded_size)) { DLOG(ERROR) << "Shared memory size was less than expected."; + callbacks_remote->Done(); return; } @@ -89,7 +95,8 @@ void OffScreenVideoConsumer::OnFrameCaptured( base::ReadOnlySharedMemoryMapping mapping; // Prevents FrameSinkVideoCapturer from recycling the shared memory that // backs |frame_|. - viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr releaser; + mojo::PendingRemote + releaser; }; SkBitmap bitmap; @@ -102,7 +109,7 @@ void OffScreenVideoConsumer::OnFrameCaptured( [](void* addr, void* context) { delete static_cast(context); }, - new FramePinner{std::move(mapping), std::move(callbacks)}); + new FramePinner{std::move(mapping), callbacks_remote.Unbind()}); bitmap.setImmutable(); media::VideoFrameMetadata metadata; @@ -110,7 +117,7 @@ void OffScreenVideoConsumer::OnFrameCaptured( gfx::Rect damage_rect; auto UPDATE_RECT = media::VideoFrameMetadata::CAPTURE_UPDATE_RECT; - if (!metadata.GetRect(UPDATE_RECT, &damage_rect)) { + if (!metadata.GetRect(UPDATE_RECT, &damage_rect) || damage_rect.IsEmpty()) { damage_rect = content_rect; } diff --git a/shell/browser/osr/osr_video_consumer.h b/shell/browser/osr/osr_video_consumer.h index 8e83075c154ab..eb8d1457f0362 100644 --- a/shell/browser/osr/osr_video_consumer.h +++ b/shell/browser/osr/osr_video_consumer.h @@ -10,6 +10,7 @@ #include "base/callback.h" #include "base/memory/weak_ptr.h" #include "components/viz/host/client_frame_sink_video_capturer.h" +#include "media/capture/mojom/video_capture_types.mojom.h" namespace electron { @@ -34,7 +35,8 @@ class OffScreenVideoConsumer : public viz::mojom::FrameSinkVideoConsumer { base::ReadOnlySharedMemoryRegion data, ::media::mojom::VideoFrameInfoPtr info, const gfx::Rect& content_rect, - viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) override; + mojo::PendingRemote + callbacks) override; void OnStopped() override; bool CheckContentRect(const gfx::Rect& content_rect); diff --git a/shell/browser/osr/osr_web_contents_view.cc b/shell/browser/osr/osr_web_contents_view.cc index 2ac0864ec2ff8..414f49546ce87 100644 --- a/shell/browser/osr/osr_web_contents_view.cc +++ b/shell/browser/osr/osr_web_contents_view.cc @@ -113,8 +113,7 @@ void OffScreenWebContentsView::CreateView(gfx::NativeView context) {} content::RenderWidgetHostViewBase* OffScreenWebContentsView::CreateViewForWidget( - content::RenderWidgetHost* render_widget_host, - bool is_guest_view_hack) { + content::RenderWidgetHost* render_widget_host) { if (render_widget_host->GetView()) { return static_cast( render_widget_host->GetView()); diff --git a/shell/browser/osr/osr_web_contents_view.h b/shell/browser/osr/osr_web_contents_view.h index 9be4c21004050..009d2653f6474 100644 --- a/shell/browser/osr/osr_web_contents_view.h +++ b/shell/browser/osr/osr_web_contents_view.h @@ -54,8 +54,7 @@ class OffScreenWebContentsView : public content::WebContentsView, gfx::Rect GetViewBounds() const override; void CreateView(gfx::NativeView context) override; content::RenderWidgetHostViewBase* CreateViewForWidget( - content::RenderWidgetHost* render_widget_host, - bool is_guest_view_hack) override; + content::RenderWidgetHost* render_widget_host) override; content::RenderWidgetHostViewBase* CreateViewForChildWidget( content::RenderWidgetHost* render_widget_host) override; void SetPageTitle(const base::string16& title) override; diff --git a/shell/browser/pref_store_delegate.cc b/shell/browser/pref_store_delegate.cc index 1273e5a7969ed..34141f0dca9a4 100644 --- a/shell/browser/pref_store_delegate.cc +++ b/shell/browser/pref_store_delegate.cc @@ -7,12 +7,12 @@ #include #include "components/prefs/value_map_pref_store.h" -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/electron_browser_context.h" namespace electron { PrefStoreDelegate::PrefStoreDelegate( - base::WeakPtr browser_context) + base::WeakPtr browser_context) : browser_context_(std::move(browser_context)) {} PrefStoreDelegate::~PrefStoreDelegate() { diff --git a/shell/browser/pref_store_delegate.h b/shell/browser/pref_store_delegate.h index 9f31d0297c7e2..548696bcb7756 100644 --- a/shell/browser/pref_store_delegate.h +++ b/shell/browser/pref_store_delegate.h @@ -14,13 +14,14 @@ namespace electron { -class AtomBrowserContext; +class ElectronBrowserContext; // Retrieves handle to the in memory pref store that gets // initialized with the pref service. class PrefStoreDelegate : public PrefValueStore::Delegate { public: - explicit PrefStoreDelegate(base::WeakPtr browser_context); + explicit PrefStoreDelegate( + base::WeakPtr browser_context); ~PrefStoreDelegate() override; void Init(PrefStore* managed_prefs, @@ -42,7 +43,7 @@ class PrefStoreDelegate : public PrefValueStore::Delegate { void UpdateCommandLinePrefStore(PrefStore* command_line_prefs) override; private: - base::WeakPtr browser_context_; + base::WeakPtr browser_context_; DISALLOW_COPY_AND_ASSIGN(PrefStoreDelegate); }; diff --git a/shell/browser/printing/print_preview_message_handler.cc b/shell/browser/printing/print_preview_message_handler.cc index 3a555c4107e97..6823c6047b7c0 100644 --- a/shell/browser/printing/print_preview_message_handler.cc +++ b/shell/browser/printing/print_preview_message_handler.cc @@ -23,6 +23,7 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" +#include "mojo/public/cpp/bindings/callback_helpers.h" #include "shell/common/api/locker.h" #include "shell/common/node_includes.h" @@ -98,10 +99,16 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting( auto* client = printing::PrintCompositeClient::FromWebContents(web_contents()); DCHECK(client); + + auto callback = + base::BindOnce(&PrintPreviewMessageHandler::OnCompositePdfDocumentDone, + weak_ptr_factory_.GetWeakPtr(), ids); client->DoCompositeDocumentToPdf( params.document_cookie, render_frame_host, content, - base::BindOnce(&PrintPreviewMessageHandler::OnCompositePdfDocumentDone, - weak_ptr_factory_.GetWeakPtr(), ids)); + mojo::WrapCallbackWithDefaultInvokeIfNotRun( + std::move(callback), + printing::mojom::PdfCompositor::Status::kCompositingFailure, + base::ReadOnlySharedMemoryRegion())); } else { ResolvePromise(ids.request_id, base::RefCountedSharedMemoryMapping::CreateFromWholeRegion( diff --git a/shell/browser/relauncher.cc b/shell/browser/relauncher.cc index adf2f5200059c..40ce93be5f81d 100644 --- a/shell/browser/relauncher.cc +++ b/shell/browser/relauncher.cc @@ -15,7 +15,7 @@ #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" -#include "shell/common/atom_command_line.h" +#include "shell/common/electron_command_line.h" #if defined(OS_POSIX) #include "base/posix/eintr_wrapper.h" @@ -138,7 +138,7 @@ bool RelaunchAppWithHelper(const base::FilePath& helper, } int RelauncherMain(const content::MainFunctionParams& main_parameters) { - const StringVector& argv = electron::AtomCommandLine::argv(); + const StringVector& argv = electron::ElectronCommandLine::argv(); if (argv.size() < 4 || argv[1] != internal::kRelauncherTypeArg) { LOG(ERROR) << "relauncher process invoked with unexpected arguments"; diff --git a/shell/browser/renderer_host/electron_render_message_filter.cc b/shell/browser/renderer_host/electron_render_message_filter.cc deleted file mode 100644 index 678bddb4653fd..0000000000000 --- a/shell/browser/renderer_host/electron_render_message_filter.cc +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2019 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 "shell/browser/renderer_host/electron_render_message_filter.h" - -#include - -#include -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/logging.h" -#include "base/stl_util.h" -#include "chrome/browser/predictors/preconnect_manager.h" -#include "components/network_hints/common/network_hints_common.h" -#include "components/network_hints/common/network_hints_messages.h" -#include "content/public/browser/browser_context.h" -#include "shell/browser/api/atom_api_session.h" -#include "shell/browser/atom_browser_context.h" -#include "shell/common/native_mate_converters/gurl_converter.h" - -using content::BrowserThread; - -namespace { - -const uint32_t kRenderFilteredMessageClasses[] = { - NetworkHintsMsgStart, -}; - -void EmitPreconnect(content::BrowserContext* browser_context, - const GURL& url, - bool allow_credentials) { - auto* session = electron::api::Session::FromWrappedClass( - v8::Isolate::GetCurrent(), - static_cast(browser_context)); - if (session) { - session->Emit("preconnect", url, allow_credentials); - } -} - -} // namespace - -ElectronRenderMessageFilter::ElectronRenderMessageFilter( - content::BrowserContext* browser_context) - : BrowserMessageFilter(kRenderFilteredMessageClasses, - base::size(kRenderFilteredMessageClasses)), - browser_context_(browser_context) {} - -ElectronRenderMessageFilter::~ElectronRenderMessageFilter() = default; - -bool ElectronRenderMessageFilter::OnMessageReceived( - const IPC::Message& message) { - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(ElectronRenderMessageFilter, message) - IPC_MESSAGE_HANDLER(NetworkHintsMsg_Preconnect, OnPreconnect) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - - return handled; -} - -void ElectronRenderMessageFilter::OnPreconnect(int render_frame_id, - const GURL& url, - bool allow_credentials, - int count) { - if (count < 1) { - LOG(WARNING) << "NetworkHintsMsg_Preconnect IPC with invalid count: " - << count; - return; - } - - if (!url.is_valid() || !url.has_host() || !url.has_scheme() || - !url.SchemeIsHTTPOrHTTPS()) { - return; - } - - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&EmitPreconnect, browser_context_, url, - allow_credentials)); -} - -namespace predictors { - -PreconnectRequest::PreconnectRequest( - const GURL& origin, - int num_sockets, - const net::NetworkIsolationKey& network_isolation_key) - : origin(origin), num_sockets(num_sockets) { - DCHECK_GE(num_sockets, 0); -} - -} // namespace predictors diff --git a/shell/browser/renderer_host/electron_render_message_filter.h b/shell/browser/renderer_host/electron_render_message_filter.h deleted file mode 100644 index 0ced6f39c0cac..0000000000000 --- a/shell/browser/renderer_host/electron_render_message_filter.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2019 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 SHELL_BROWSER_RENDERER_HOST_ELECTRON_RENDER_MESSAGE_FILTER_H_ -#define SHELL_BROWSER_RENDERER_HOST_ELECTRON_RENDER_MESSAGE_FILTER_H_ - -#include -#include - -#include "content/public/browser/browser_message_filter.h" - -class GURL; - -namespace content { -class BrowserContext; -} - -namespace predictors { -class PreconnectManager; -} - -// This class filters out incoming Chrome-specific IPC messages for the renderer -// process on the IPC thread. -class ElectronRenderMessageFilter : public content::BrowserMessageFilter { - public: - explicit ElectronRenderMessageFilter( - content::BrowserContext* browser_context); - - // content::BrowserMessageFilter methods: - bool OnMessageReceived(const IPC::Message& message) override; - - private: - ~ElectronRenderMessageFilter() override; - - void OnPreconnect(int render_frame_id, - const GURL& url, - bool allow_credentials, - int count); - - content::BrowserContext* browser_context_; - - DISALLOW_COPY_AND_ASSIGN(ElectronRenderMessageFilter); -}; - -#endif // SHELL_BROWSER_RENDERER_HOST_ELECTRON_RENDER_MESSAGE_FILTER_H_ diff --git a/shell/browser/resources/win/atom.rc b/shell/browser/resources/win/atom.rc index 4dba19bd91830..9f6d141ada02b 100644 --- a/shell/browser/resources/win/atom.rc +++ b/shell/browser/resources/win/atom.rc @@ -50,8 +50,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 8,0,0,20191023 - PRODUCTVERSION 8,0,0,20191023 + FILEVERSION 8,3,0,0 + PRODUCTVERSION 8,3,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -68,12 +68,12 @@ BEGIN BEGIN VALUE "CompanyName", "GitHub, Inc." VALUE "FileDescription", "Electron" - VALUE "FileVersion", "8.0.0" + VALUE "FileVersion", "8.3.0" VALUE "InternalName", "electron.exe" VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved." VALUE "OriginalFilename", "electron.exe" VALUE "ProductName", "Electron" - VALUE "ProductVersion", "8.0.0" + VALUE "ProductVersion", "8.3.0" VALUE "SquirrelAwareVersion", "1" END END diff --git a/shell/browser/ui/accelerator_util.cc b/shell/browser/ui/accelerator_util.cc index d061a76a1f600..f1d83d6892ed2 100644 --- a/shell/browser/ui/accelerator_util.cc +++ b/shell/browser/ui/accelerator_util.cc @@ -68,11 +68,11 @@ bool StringToAccelerator(const std::string& shortcut, } void GenerateAcceleratorTable(AcceleratorTable* table, - electron::AtomMenuModel* model) { + electron::ElectronMenuModel* model) { int count = model->GetItemCount(); for (int i = 0; i < count; ++i) { - electron::AtomMenuModel::ItemType type = model->GetTypeAt(i); - if (type == electron::AtomMenuModel::TYPE_SUBMENU) { + electron::ElectronMenuModel::ItemType type = model->GetTypeAt(i); + if (type == electron::ElectronMenuModel::TYPE_SUBMENU) { auto* submodel = model->GetSubmenuModelAt(i); GenerateAcceleratorTable(table, submodel); } else { diff --git a/shell/browser/ui/accelerator_util.h b/shell/browser/ui/accelerator_util.h index e3e78bdcf4831..e3b942dc189db 100644 --- a/shell/browser/ui/accelerator_util.h +++ b/shell/browser/ui/accelerator_util.h @@ -8,14 +8,14 @@ #include #include -#include "shell/browser/ui/atom_menu_model.h" +#include "shell/browser/ui/electron_menu_model.h" #include "ui/base/accelerators/accelerator.h" namespace accelerator_util { typedef struct { int position; - electron::AtomMenuModel* model; + electron::ElectronMenuModel* model; } MenuItem; typedef std::map AcceleratorTable; @@ -25,7 +25,7 @@ bool StringToAccelerator(const std::string& description, // Generate a table that contains memu model's accelerators and command ids. void GenerateAcceleratorTable(AcceleratorTable* table, - electron::AtomMenuModel* model); + electron::ElectronMenuModel* model); // Trigger command from the accelerators table. bool TriggerAcceleratorTableCommand(AcceleratorTable* table, diff --git a/shell/browser/ui/autofill_popup.cc b/shell/browser/ui/autofill_popup.cc index abd7dee5b3bbb..3c74ee797e7ca 100644 --- a/shell/browser/ui/autofill_popup.cc +++ b/shell/browser/ui/autofill_popup.cc @@ -11,6 +11,7 @@ #include "base/i18n/rtl.h" #include "chrome/browser/ui/autofill/popup_view_common.h" #include "electron/buildflags/buildflags.h" +#include "mojo/public/cpp/bindings/associated_remote.h" #include "shell/browser/native_window_views.h" #include "shell/browser/ui/autofill_popup.h" #include "shell/common/api/api.mojom.h" @@ -113,9 +114,8 @@ void AutofillPopup::SetItems(const std::vector& values, } void AutofillPopup::AcceptSuggestion(int index) { - mojom::ElectronAutofillAgentAssociatedPtr autofill_agent; - frame_host_->GetRemoteAssociatedInterfaces()->GetInterface( - mojo::MakeRequest(&autofill_agent)); + mojo::AssociatedRemote autofill_agent; + frame_host_->GetRemoteAssociatedInterfaces()->GetInterface(&autofill_agent); autofill_agent->AcceptDataListSuggestion(GetValueAt(index)); } diff --git a/shell/browser/ui/cocoa/atom_native_widget_mac.h b/shell/browser/ui/cocoa/atom_native_widget_mac.h deleted file mode 100644 index 65ce66874d9fe..0000000000000 --- a/shell/browser/ui/cocoa/atom_native_widget_mac.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2018 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef SHELL_BROWSER_UI_COCOA_ATOM_NATIVE_WIDGET_MAC_H_ -#define SHELL_BROWSER_UI_COCOA_ATOM_NATIVE_WIDGET_MAC_H_ - -#include "ui/views/widget/native_widget_mac.h" - -namespace electron { - -class NativeWindowMac; - -class AtomNativeWidgetMac : public views::NativeWidgetMac { - public: - AtomNativeWidgetMac(NativeWindowMac* shell, - NSUInteger style_mask, - views::internal::NativeWidgetDelegate* delegate); - ~AtomNativeWidgetMac() override; - - protected: - // NativeWidgetMac: - NativeWidgetMacNSWindow* CreateNSWindow( - const remote_cocoa::mojom::CreateWindowParams* params) override; - - private: - NativeWindowMac* shell_; - NSUInteger style_mask_; - - DISALLOW_COPY_AND_ASSIGN(AtomNativeWidgetMac); -}; - -} // namespace electron - -#endif // SHELL_BROWSER_UI_COCOA_ATOM_NATIVE_WIDGET_MAC_H_ diff --git a/shell/browser/ui/cocoa/atom_bundle_mover.h b/shell/browser/ui/cocoa/electron_bundle_mover.h similarity index 86% rename from shell/browser/ui/cocoa/atom_bundle_mover.h rename to shell/browser/ui/cocoa/electron_bundle_mover.h index 8e952a1f23fb6..a68fac162ee55 100644 --- a/shell/browser/ui/cocoa/atom_bundle_mover.h +++ b/shell/browser/ui/cocoa/electron_bundle_mover.h @@ -2,8 +2,10 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_UI_COCOA_ATOM_BUNDLE_MOVER_H_ -#define SHELL_BROWSER_UI_COCOA_ATOM_BUNDLE_MOVER_H_ +#ifndef SHELL_BROWSER_UI_COCOA_ELECTRON_BUNDLE_MOVER_H_ +#define SHELL_BROWSER_UI_COCOA_ELECTRON_BUNDLE_MOVER_H_ + +#include #include "base/mac/foundation_util.h" #include "shell/common/gin_helper/error_thrower.h" @@ -17,7 +19,7 @@ namespace electron { // Possible bundle movement conflicts enum class BundlerMoverConflictType { EXISTS, EXISTS_AND_RUNNING }; -class AtomBundleMover { +class ElectronBundleMover { public: static bool Move(gin_helper::ErrorThrower thrower, gin::Arguments* args); static bool IsCurrentAppInApplicationsFolder(); @@ -41,4 +43,4 @@ class AtomBundleMover { } // namespace electron -#endif // SHELL_BROWSER_UI_COCOA_ATOM_BUNDLE_MOVER_H_ +#endif // SHELL_BROWSER_UI_COCOA_ELECTRON_BUNDLE_MOVER_H_ diff --git a/shell/browser/ui/cocoa/atom_bundle_mover.mm b/shell/browser/ui/cocoa/electron_bundle_mover.mm similarity index 92% rename from shell/browser/ui/cocoa/atom_bundle_mover.mm rename to shell/browser/ui/cocoa/electron_bundle_mover.mm index 99d7ab93d7ae9..a881a51906e02 100644 --- a/shell/browser/ui/cocoa/atom_bundle_mover.mm +++ b/shell/browser/ui/cocoa/electron_bundle_mover.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#import "shell/browser/ui/cocoa/atom_bundle_mover.h" +#import "shell/browser/ui/cocoa/electron_bundle_mover.h" #import #import @@ -10,6 +10,9 @@ #import #import +#include +#include + #include "gin/dictionary.h" #include "shell/browser/browser.h" #include "shell/common/gin_converters/callback_converter.h" @@ -35,9 +38,9 @@ namespace electron { -bool AtomBundleMover::ShouldContinueMove(gin_helper::ErrorThrower thrower, - BundlerMoverConflictType type, - gin::Arguments* args) { +bool ElectronBundleMover::ShouldContinueMove(gin_helper::ErrorThrower thrower, + BundlerMoverConflictType type, + gin::Arguments* args) { gin::Dictionary options(args->isolate()); bool hasOptions = args->GetNext(&options); base::OnceCallback(BundlerMoverConflictType)> @@ -58,8 +61,8 @@ return true; } -bool AtomBundleMover::Move(gin_helper::ErrorThrower thrower, - gin::Arguments* args) { +bool ElectronBundleMover::Move(gin_helper::ErrorThrower thrower, + gin::Arguments* args) { // Path of the current bundle NSString* bundlePath = [[NSBundle mainBundle] bundlePath]; @@ -175,11 +178,11 @@ return true; } -bool AtomBundleMover::IsCurrentAppInApplicationsFolder() { +bool ElectronBundleMover::IsCurrentAppInApplicationsFolder() { return IsInApplicationsFolder([[NSBundle mainBundle] bundlePath]); } -bool AtomBundleMover::IsInApplicationsFolder(NSString* bundlePath) { +bool ElectronBundleMover::IsInApplicationsFolder(NSString* bundlePath) { // Check all the normal Application directories NSArray* applicationDirs = NSSearchPathForDirectoriesInDomains( NSApplicationDirectory, NSAllDomainsMask, true); @@ -196,7 +199,7 @@ return false; } -NSString* AtomBundleMover::ContainingDiskImageDevice(NSString* bundlePath) { +NSString* ElectronBundleMover::ContainingDiskImageDevice(NSString* bundlePath) { NSString* containingPath = [bundlePath stringByDeletingLastPathComponent]; struct statfs fs; @@ -255,9 +258,9 @@ return nil; } -bool AtomBundleMover::AuthorizedInstall(NSString* srcPath, - NSString* dstPath, - bool* canceled) { +bool ElectronBundleMover::AuthorizedInstall(NSString* srcPath, + NSString* dstPath, + bool* canceled) { if (canceled) *canceled = false; @@ -358,21 +361,21 @@ static OSStatus (*security_AuthorizationExecuteWithPrivileges)( return false; } -bool AtomBundleMover::CopyBundle(NSString* srcPath, NSString* dstPath) { +bool ElectronBundleMover::CopyBundle(NSString* srcPath, NSString* dstPath) { NSFileManager* fileManager = [NSFileManager defaultManager]; NSError* error = nil; return [fileManager copyItemAtPath:srcPath toPath:dstPath error:&error]; } -NSString* AtomBundleMover::ShellQuotedString(NSString* string) { +NSString* ElectronBundleMover::ShellQuotedString(NSString* string) { return [NSString stringWithFormat:@"'%@'", [string stringByReplacingOccurrencesOfString:@"'" withString:@"'\\''"]]; } -void AtomBundleMover::Relaunch(NSString* destinationPath) { +void ElectronBundleMover::Relaunch(NSString* destinationPath) { // The shell script waits until the original app process terminates. // This is done so that the relaunched app opens as the front-most app. int pid = [[NSProcessInfo processInfo] processIdentifier]; @@ -399,7 +402,7 @@ static OSStatus (*security_AuthorizationExecuteWithPrivileges)( arguments:[NSArray arrayWithObjects:@"-c", script, nil]]; } -bool AtomBundleMover::IsApplicationAtPathRunning(NSString* bundlePath) { +bool ElectronBundleMover::IsApplicationAtPathRunning(NSString* bundlePath) { bundlePath = [bundlePath stringByStandardizingPath]; for (NSRunningApplication* runningApplication in @@ -413,7 +416,7 @@ static OSStatus (*security_AuthorizationExecuteWithPrivileges)( return false; } -bool AtomBundleMover::Trash(NSString* path) { +bool ElectronBundleMover::Trash(NSString* path) { bool result = false; if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_8) { @@ -453,7 +456,7 @@ static OSStatus (*security_AuthorizationExecuteWithPrivileges)( return result; } -bool AtomBundleMover::DeleteOrTrash(NSString* path) { +bool ElectronBundleMover::DeleteOrTrash(NSString* path) { NSError* error; if ([[NSFileManager defaultManager] removeItemAtPath:path error:&error]) { diff --git a/shell/browser/ui/cocoa/atom_inspectable_web_contents_view.h b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h similarity index 94% rename from shell/browser/ui/cocoa/atom_inspectable_web_contents_view.h rename to shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h index 4166099ff95ba..691c25f639332 100644 --- a/shell/browser/ui/cocoa/atom_inspectable_web_contents_view.h +++ b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h @@ -17,7 +17,7 @@ class InspectableWebContentsViewMac; using electron::InspectableWebContentsViewMac; -@interface AtomInspectableWebContentsView : BaseView { +@interface ElectronInspectableWebContentsView : BaseView { @private electron::InspectableWebContentsViewMac* inspectableWebContentsView_; diff --git a/shell/browser/ui/cocoa/atom_inspectable_web_contents_view.mm b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm similarity index 98% rename from shell/browser/ui/cocoa/atom_inspectable_web_contents_view.mm rename to shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm index 64d01a76de419..0b25862211bd3 100644 --- a/shell/browser/ui/cocoa/atom_inspectable_web_contents_view.mm +++ b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-CHROMIUM file. -#include "shell/browser/ui/cocoa/atom_inspectable_web_contents_view.h" +#include "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h" #include "content/public/browser/render_widget_host_view.h" #include "shell/browser/ui/cocoa/event_dispatching_window.h" @@ -11,7 +11,7 @@ #include "shell/browser/ui/inspectable_web_contents_view_mac.h" #include "ui/gfx/mac/scoped_cocoa_disable_screen_updates.h" -@implementation AtomInspectableWebContentsView +@implementation ElectronInspectableWebContentsView - (instancetype)initWithInspectableWebContentsViewMac: (InspectableWebContentsViewMac*)view { diff --git a/shell/browser/ui/cocoa/atom_menu_controller.h b/shell/browser/ui/cocoa/electron_menu_controller.h similarity index 72% rename from shell/browser/ui/cocoa/atom_menu_controller.h rename to shell/browser/ui/cocoa/electron_menu_controller.h index e47402b97a52b..886fe7f81046b 100644 --- a/shell/browser/ui/cocoa/atom_menu_controller.h +++ b/shell/browser/ui/cocoa/electron_menu_controller.h @@ -3,8 +3,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_UI_COCOA_ATOM_MENU_CONTROLLER_H_ -#define SHELL_BROWSER_UI_COCOA_ATOM_MENU_CONTROLLER_H_ +#ifndef SHELL_BROWSER_UI_COCOA_ELECTRON_MENU_CONTROLLER_H_ +#define SHELL_BROWSER_UI_COCOA_ELECTRON_MENU_CONTROLLER_H_ #import @@ -13,7 +13,7 @@ #include "base/strings/string16.h" namespace electron { -class AtomMenuModel; +class ElectronMenuModel; } // A controller for the cross-platform menu model. The menu that's created @@ -22,26 +22,26 @@ class AtomMenuModel; // allow for hierarchical menus). The tag is the index into that model for // that particular item. It is important that the model outlives this object // as it only maintains weak references. -@interface AtomMenuController : NSObject { +@interface ElectronMenuController : NSObject { @protected - electron::AtomMenuModel* model_; // weak + electron::ElectronMenuModel* model_; // weak base::scoped_nsobject menu_; BOOL isMenuOpen_; BOOL useDefaultAccelerator_; - base::Callback closeCallback; + base::OnceClosure closeCallback; } -@property(nonatomic, assign) electron::AtomMenuModel* model; +@property(nonatomic, assign) electron::ElectronMenuModel* model; // Builds a NSMenu from the pre-built model (must not be nil). Changes made // to the contents of the model after calling this will not be noticed. -- (id)initWithModel:(electron::AtomMenuModel*)model +- (id)initWithModel:(electron::ElectronMenuModel*)model useDefaultAccelerator:(BOOL)use; -- (void)setCloseCallback:(const base::Callback&)callback; +- (void)setCloseCallback:(base::OnceClosure)callback; // Populate current NSMenu with |model|. -- (void)populateWithModel:(electron::AtomMenuModel*)model; +- (void)populateWithModel:(electron::ElectronMenuModel*)model; // Programmatically close the constructed menu. - (void)cancel; @@ -60,4 +60,4 @@ class AtomMenuModel; @end -#endif // SHELL_BROWSER_UI_COCOA_ATOM_MENU_CONTROLLER_H_ +#endif // SHELL_BROWSER_UI_COCOA_ELECTRON_MENU_CONTROLLER_H_ diff --git a/shell/browser/ui/cocoa/atom_menu_controller.mm b/shell/browser/ui/cocoa/electron_menu_controller.mm similarity index 87% rename from shell/browser/ui/cocoa/atom_menu_controller.mm rename to shell/browser/ui/cocoa/electron_menu_controller.mm index c6d7d59d12d85..86b0470081a29 100644 --- a/shell/browser/ui/cocoa/atom_menu_controller.mm +++ b/shell/browser/ui/cocoa/electron_menu_controller.mm @@ -3,7 +3,9 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#import "shell/browser/ui/cocoa/atom_menu_controller.h" +#import "shell/browser/ui/cocoa/electron_menu_controller.h" + +#include #include "base/logging.h" #include "base/strings/sys_string_conversions.h" @@ -11,8 +13,8 @@ #include "base/task/post_task.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" -#include "shell/browser/mac/atom_application.h" -#include "shell/browser/ui/atom_menu_model.h" +#include "shell/browser/mac/electron_application.h" +#include "shell/browser/ui/electron_menu_model.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/accelerators/platform_accelerator_cocoa.h" #include "ui/base/l10n/l10n_util_mac.h" @@ -62,7 +64,7 @@ // Called when adding a submenu to the menu and checks if the submenu, via its // |model|, has visible child items. -bool MenuHasVisibleItems(const electron::AtomMenuModel* model) { +bool MenuHasVisibleItems(const electron::ElectronMenuModel* model) { int count = model->GetItemCount(); for (int index = 0; index < count; index++) { if (model->IsVisibleAt(index)) @@ -91,11 +93,11 @@ bool MenuHasVisibleItems(const electron::AtomMenuModel* model) { // Submenu retained to be swapped back to |recentDocumentsMenuItem_| static base::scoped_nsobject recentDocumentsMenuSwap_; -@implementation AtomMenuController +@implementation ElectronMenuController @synthesize model = model_; -- (id)initWithModel:(electron::AtomMenuModel*)model +- (id)initWithModel:(electron::ElectronMenuModel*)model useDefaultAccelerator:(BOOL)use { if ((self = [super init])) { model_ = model; @@ -118,19 +120,21 @@ - (void)dealloc { [super dealloc]; } -- (void)setCloseCallback:(const base::Callback&)callback { - closeCallback = callback; +- (void)setCloseCallback:(base::OnceClosure)callback { + closeCallback = std::move(callback); } -- (void)populateWithModel:(electron::AtomMenuModel*)model { +- (void)populateWithModel:(electron::ElectronMenuModel*)model { if (!menu_) return; // Locate & retain the recent documents menu item if (!recentDocumentsMenuItem_) { - recentDocumentsMenuItem_.reset( - [[[[[NSApp mainMenu] itemWithTitle:@"Electron"] submenu] - itemWithTitle:@"Open Recent"] retain]); + base::string16 title = base::ASCIIToUTF16("Open Recent"); + NSString* openTitle = l10n_util::FixUpWindowsStyleLabel(title); + + recentDocumentsMenuItem_.reset([[[[[NSApp mainMenu] + itemWithTitle:@"Electron"] submenu] itemWithTitle:openTitle] retain]); } model_ = model; @@ -138,7 +142,7 @@ - (void)populateWithModel:(electron::AtomMenuModel*)model { const int count = model->GetItemCount(); for (int index = 0; index < count; index++) { - if (model->GetTypeAt(index) == electron::AtomMenuModel::TYPE_SEPARATOR) + if (model->GetTypeAt(index) == electron::ElectronMenuModel::TYPE_SEPARATOR) [self addSeparatorToMenu:menu_ atIndex:index]; else [self addItemToMenu:menu_ atIndex:index fromModel:model]; @@ -151,19 +155,19 @@ - (void)cancel { isMenuOpen_ = NO; model_->MenuWillClose(); if (!closeCallback.is_null()) { - base::PostTask(FROM_HERE, {BrowserThread::UI}, closeCallback); + base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(closeCallback)); } } } // Creates a NSMenu from the given model. If the model has submenus, this can // be invoked recursively. -- (NSMenu*)menuFromModel:(electron::AtomMenuModel*)model { +- (NSMenu*)menuFromModel:(electron::ElectronMenuModel*)model { NSMenu* menu = [[[NSMenu alloc] initWithTitle:@""] autorelease]; const int count = model->GetItemCount(); for (int index = 0; index < count; index++) { - if (model->GetTypeAt(index) == electron::AtomMenuModel::TYPE_SEPARATOR) + if (model->GetTypeAt(index) == electron::ElectronMenuModel::TYPE_SEPARATOR) [self addSeparatorToMenu:menu atIndex:index]; else [self addItemToMenu:menu atIndex:index fromModel:model]; @@ -222,7 +226,7 @@ - (void)replaceSubmenuShowingRecentDocuments:(NSMenuItem*)item { // associated with the entry in the model identified by |modelIndex|. - (void)addItemToMenu:(NSMenu*)menu atIndex:(NSInteger)index - fromModel:(electron::AtomMenuModel*)model { + fromModel:(electron::ElectronMenuModel*)model { base::string16 label16 = model->GetLabelAt(index); NSString* label = l10n_util::FixUpWindowsStyleLabel(label16); @@ -240,7 +244,7 @@ - (void)addItemToMenu:(NSMenu*)menu [item setToolTip:base::SysUTF16ToNSString(toolTip)]; base::string16 role = model->GetRoleAt(index); - electron::AtomMenuModel::ItemType type = model->GetTypeAt(index); + electron::ElectronMenuModel::ItemType type = model->GetTypeAt(index); if (role == base::ASCIIToUTF16("services")) { base::string16 title = base::ASCIIToUTF16("Services"); @@ -251,7 +255,7 @@ - (void)addItemToMenu:(NSMenu*)menu NSMenu* submenu = [[NSMenu alloc] initWithTitle:label]; [item setSubmenu:submenu]; [NSApp setServicesMenu:submenu]; - } else if (type == electron::AtomMenuModel::TYPE_SUBMENU && + } else if (type == electron::ElectronMenuModel::TYPE_SUBMENU && model->IsVisibleAt(index)) { // We need to specifically check that the submenu top-level item has been // enabled as it's not validated by validateUserInterfaceItem @@ -261,8 +265,9 @@ - (void)addItemToMenu:(NSMenu*)menu // Recursively build a submenu from the sub-model at this index. [item setTarget:nil]; [item setAction:nil]; - electron::AtomMenuModel* submenuModel = - static_cast(model->GetSubmenuModelAt(index)); + electron::ElectronMenuModel* submenuModel = + static_cast( + model->GetSubmenuModelAt(index)); NSMenu* submenu = MenuHasVisibleItems(submenuModel) ? [self menuFromModel:submenuModel] : MakeEmptySubmenu(); @@ -326,8 +331,9 @@ - (BOOL)validateUserInterfaceItem:(id)item { return NO; NSInteger modelIndex = [item tag]; - electron::AtomMenuModel* model = static_cast( - [[(id)item representedObject] pointerValue]); + electron::ElectronMenuModel* model = + static_cast( + [[(id)item representedObject] pointerValue]); DCHECK(model); if (model) { BOOL checked = model->IsItemCheckedAt(modelIndex); @@ -345,8 +351,9 @@ - (BOOL)validateUserInterfaceItem:(id)item { // item chosen. - (void)itemSelected:(id)sender { NSInteger modelIndex = [sender tag]; - electron::AtomMenuModel* model = static_cast( - [[sender representedObject] pointerValue]); + electron::ElectronMenuModel* model = + static_cast( + [[sender representedObject] pointerValue]); DCHECK(model); if (model) { NSEvent* event = [NSApp currentEvent]; @@ -383,7 +390,7 @@ - (void)menuDidClose:(NSMenu*)menu { // Post async task so that itemSelected runs before the close callback // deletes the controller from the map which deallocates it if (!closeCallback.is_null()) { - base::PostTask(FROM_HERE, {BrowserThread::UI}, closeCallback); + base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(closeCallback)); } } } diff --git a/shell/browser/ui/cocoa/electron_native_widget_mac.h b/shell/browser/ui/cocoa/electron_native_widget_mac.h new file mode 100644 index 0000000000000..5a1f3ad99f0a8 --- /dev/null +++ b/shell/browser/ui/cocoa/electron_native_widget_mac.h @@ -0,0 +1,35 @@ +// Copyright (c) 2018 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_BROWSER_UI_COCOA_ELECTRON_NATIVE_WIDGET_MAC_H_ +#define SHELL_BROWSER_UI_COCOA_ELECTRON_NATIVE_WIDGET_MAC_H_ + +#include "ui/views/widget/native_widget_mac.h" + +namespace electron { + +class NativeWindowMac; + +class ElectronNativeWidgetMac : public views::NativeWidgetMac { + public: + ElectronNativeWidgetMac(NativeWindowMac* shell, + NSUInteger style_mask, + views::internal::NativeWidgetDelegate* delegate); + ~ElectronNativeWidgetMac() override; + + protected: + // NativeWidgetMac: + NativeWidgetMacNSWindow* CreateNSWindow( + const remote_cocoa::mojom::CreateWindowParams* params) override; + + private: + NativeWindowMac* shell_; + NSUInteger style_mask_; + + DISALLOW_COPY_AND_ASSIGN(ElectronNativeWidgetMac); +}; + +} // namespace electron + +#endif // SHELL_BROWSER_UI_COCOA_ELECTRON_NATIVE_WIDGET_MAC_H_ diff --git a/shell/browser/ui/cocoa/atom_native_widget_mac.mm b/shell/browser/ui/cocoa/electron_native_widget_mac.mm similarity index 51% rename from shell/browser/ui/cocoa/atom_native_widget_mac.mm rename to shell/browser/ui/cocoa/electron_native_widget_mac.mm index 3900f6cb85d37..21b5afe1b30d3 100644 --- a/shell/browser/ui/cocoa/atom_native_widget_mac.mm +++ b/shell/browser/ui/cocoa/electron_native_widget_mac.mm @@ -2,13 +2,13 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/ui/cocoa/atom_native_widget_mac.h" +#include "shell/browser/ui/cocoa/electron_native_widget_mac.h" -#include "shell/browser/ui/cocoa/atom_ns_window.h" +#include "shell/browser/ui/cocoa/electron_ns_window.h" namespace electron { -AtomNativeWidgetMac::AtomNativeWidgetMac( +ElectronNativeWidgetMac::ElectronNativeWidgetMac( NativeWindowMac* shell, NSUInteger style_mask, views::internal::NativeWidgetDelegate* delegate) @@ -16,12 +16,12 @@ shell_(shell), style_mask_(style_mask) {} -AtomNativeWidgetMac::~AtomNativeWidgetMac() {} +ElectronNativeWidgetMac::~ElectronNativeWidgetMac() {} -NativeWidgetMacNSWindow* AtomNativeWidgetMac::CreateNSWindow( +NativeWidgetMacNSWindow* ElectronNativeWidgetMac::CreateNSWindow( const remote_cocoa::mojom::CreateWindowParams* params) { - return [[[AtomNSWindow alloc] initWithShell:shell_ - styleMask:style_mask_] autorelease]; + return [[[ElectronNSWindow alloc] initWithShell:shell_ + styleMask:style_mask_] autorelease]; } } // namespace electron diff --git a/shell/browser/ui/cocoa/atom_ns_window.h b/shell/browser/ui/cocoa/electron_ns_window.h similarity index 84% rename from shell/browser/ui/cocoa/atom_ns_window.h rename to shell/browser/ui/cocoa/electron_ns_window.h index f6c7ee65d15b0..1dfc97fab1fb6 100644 --- a/shell/browser/ui/cocoa/atom_ns_window.h +++ b/shell/browser/ui/cocoa/electron_ns_window.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_UI_COCOA_ATOM_NS_WINDOW_H_ -#define SHELL_BROWSER_UI_COCOA_ATOM_NS_WINDOW_H_ +#ifndef SHELL_BROWSER_UI_COCOA_ELECTRON_NS_WINDOW_H_ +#define SHELL_BROWSER_UI_COCOA_ELECTRON_NS_WINDOW_H_ #include "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h" #include "shell/browser/ui/cocoa/event_dispatching_window.h" @@ -27,7 +27,7 @@ class ScopedDisableResize { } // namespace electron -@interface AtomNSWindow : NativeWidgetMacNSWindow { +@interface ElectronNSWindow : NativeWidgetMacNSWindow { @private electron::NativeWindowMac* shell_; } @@ -44,4 +44,4 @@ class ScopedDisableResize { - (void)toggleFullScreenMode:(id)sender; @end -#endif // SHELL_BROWSER_UI_COCOA_ATOM_NS_WINDOW_H_ +#endif // SHELL_BROWSER_UI_COCOA_ELECTRON_NS_WINDOW_H_ diff --git a/shell/browser/ui/cocoa/atom_ns_window.mm b/shell/browser/ui/cocoa/electron_ns_window.mm similarity index 80% rename from shell/browser/ui/cocoa/atom_ns_window.mm rename to shell/browser/ui/cocoa/electron_ns_window.mm index a455346ffc9be..33c97607018be 100644 --- a/shell/browser/ui/cocoa/atom_ns_window.mm +++ b/shell/browser/ui/cocoa/electron_ns_window.mm @@ -2,12 +2,12 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/ui/cocoa/atom_ns_window.h" +#include "shell/browser/ui/cocoa/electron_ns_window.h" #include "base/strings/sys_string_conversions.h" #include "shell/browser/native_window_mac.h" -#include "shell/browser/ui/cocoa/atom_preview_item.h" -#include "shell/browser/ui/cocoa/atom_touch_bar.h" +#include "shell/browser/ui/cocoa/electron_preview_item.h" +#include "shell/browser/ui/cocoa/electron_touch_bar.h" #include "shell/browser/ui/cocoa/root_view_mac.h" #include "ui/base/cocoa/window_size_constants.h" @@ -17,7 +17,7 @@ } // namespace electron -@implementation AtomNSWindow +@implementation ElectronNSWindow @synthesize acceptsFirstMouse; @synthesize enableLargerThanScreen; @@ -103,8 +103,6 @@ - (void)setFrame:(NSRect)windowFrame display:(BOOL)displayViews { } - (id)accessibilityAttributeValue:(NSString*)attribute { - if ([attribute isEqual:NSAccessibilityTitleAttribute]) - return base::SysUTF8ToNSString(shell_->GetTitle()); if ([attribute isEqual:NSAccessibilityEnabledAttribute]) return [NSNumber numberWithBool:YES]; if (![attribute isEqualToString:@"AXChildren"]) @@ -122,7 +120,22 @@ - (id)accessibilityAttributeValue:(NSString*)attribute { [NSButtonCell class], @"RenderWidgetHostViewCocoa"]; NSArray* children = [super accessibilityAttributeValue:attribute]; - return [children filteredArrayUsingPredicate:predicate]; + NSMutableArray* mutableChildren = [[children mutableCopy] autorelease]; + [mutableChildren filterUsingPredicate:predicate]; + + // We need to add the web contents: Without us doing so, VoiceOver + // users will be able to navigate up the a11y tree, but not back down. + // The content view contains the "web contents", which VoiceOver + // immediately understands. + NSView* contentView = + [shell_->GetNativeWindow().GetNativeNSWindow() contentView]; + [mutableChildren addObject:contentView]; + + return mutableChildren; +} + +- (NSString*)accessibilityTitle { + return base::SysUTF8ToNSString(shell_->GetTitle()); } - (BOOL)canBecomeMainWindow { @@ -172,6 +185,10 @@ - (void)performClose:(id)sender { return; } [self close]; + } else if (shell_->is_modal() && shell_->parent() && shell_->IsVisible()) { + // We don't want to actually call [window close] here since + // we've already called endSheet on the modal sheet. + return; } else { [super performClose:sender]; } @@ -183,11 +200,17 @@ - (void)toggleFullScreenMode:(id)sender { // If we're in simple fullscreen mode and trying to exit it // we need to ensure we exit it properly to prevent a crash - // with NSWindowStyleMaskTitled mode - if (is_simple_fs || always_simple_fs) + // with NSWindowStyleMaskTitled mode. + if (is_simple_fs || always_simple_fs) { shell_->SetSimpleFullScreen(!is_simple_fs); - else + } else { + bool maximizable = shell_->IsMaximizable(); [super toggleFullScreen:sender]; + + // Exiting fullscreen causes Cocoa to redraw the NSWindow, which resets + // the enabled state for NSWindowZoomButton. We need to persist it. + shell_->SetMaximizable(maximizable); + } } - (void)performMiniaturize:(id)sender { diff --git a/shell/browser/ui/cocoa/atom_ns_window_delegate.h b/shell/browser/ui/cocoa/electron_ns_window_delegate.h similarity index 70% rename from shell/browser/ui/cocoa/atom_ns_window_delegate.h rename to shell/browser/ui/cocoa/electron_ns_window_delegate.h index 80112054e4017..c5db7fbb27f25 100644 --- a/shell/browser/ui/cocoa/atom_ns_window_delegate.h +++ b/shell/browser/ui/cocoa/electron_ns_window_delegate.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_UI_COCOA_ATOM_NS_WINDOW_DELEGATE_H_ -#define SHELL_BROWSER_UI_COCOA_ATOM_NS_WINDOW_DELEGATE_H_ +#ifndef SHELL_BROWSER_UI_COCOA_ELECTRON_NS_WINDOW_DELEGATE_H_ +#define SHELL_BROWSER_UI_COCOA_ELECTRON_NS_WINDOW_DELEGATE_H_ #include @@ -13,7 +13,7 @@ namespace electron { class NativeWindowMac; } -@interface AtomNSWindowDelegate +@interface ElectronNSWindowDelegate : ViewsNSWindowDelegate { @private electron::NativeWindowMac* shell_; @@ -24,4 +24,4 @@ class NativeWindowMac; - (id)initWithShell:(electron::NativeWindowMac*)shell; @end -#endif // SHELL_BROWSER_UI_COCOA_ATOM_NS_WINDOW_DELEGATE_H_ +#endif // SHELL_BROWSER_UI_COCOA_ELECTRON_NS_WINDOW_DELEGATE_H_ diff --git a/shell/browser/ui/cocoa/atom_ns_window_delegate.mm b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm similarity index 89% rename from shell/browser/ui/cocoa/atom_ns_window_delegate.mm rename to shell/browser/ui/cocoa/electron_ns_window_delegate.mm index 927524e98121c..d503be1867ed8 100644 --- a/shell/browser/ui/cocoa/atom_ns_window_delegate.mm +++ b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/ui/cocoa/atom_ns_window_delegate.h" +#include "shell/browser/ui/cocoa/electron_ns_window_delegate.h" #include @@ -10,15 +10,15 @@ #include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h" #include "shell/browser/browser.h" #include "shell/browser/native_window_mac.h" -#include "shell/browser/ui/cocoa/atom_preview_item.h" -#include "shell/browser/ui/cocoa/atom_touch_bar.h" +#include "shell/browser/ui/cocoa/electron_preview_item.h" +#include "shell/browser/ui/cocoa/electron_touch_bar.h" #include "ui/gfx/mac/coordinate_conversion.h" #include "ui/views/cocoa/native_widget_mac_ns_window_host.h" #include "ui/views/widget/native_widget_mac.h" using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle; -@implementation AtomNSWindowDelegate +@implementation ElectronNSWindowDelegate - (id)initWithShell:(electron::NativeWindowMac*)shell { // The views library assumes the window delegate must be an instance of @@ -136,6 +136,9 @@ - (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize { - (void)windowDidResize:(NSNotification*)notification { [super windowDidResize:notification]; shell_->NotifyWindowResize(); + if (shell_->title_bar_style() == TitleBarStyle::HIDDEN) { + shell_->RepositionTrafficLights(); + } } - (void)windowWillMove:(NSNotification*)notification { @@ -249,16 +252,37 @@ - (void)windowWillExitFullScreen:(NSNotification*)notification { shell_->SetStyleMask(false, NSWindowStyleMaskFullSizeContentView); [window setTitlebarAppearsTransparent:YES]; } + shell_->SetExitingFullScreen(true); + if (shell_->title_bar_style() == TitleBarStyle::HIDDEN) { + shell_->RepositionTrafficLights(); + } } - (void)windowDidExitFullScreen:(NSNotification*)notification { shell_->SetResizable(is_resizable_); shell_->NotifyWindowLeaveFullScreen(); + shell_->SetExitingFullScreen(false); + if (shell_->title_bar_style() == TitleBarStyle::HIDDEN) { + shell_->RepositionTrafficLights(); + } } - (void)windowWillClose:(NSNotification*)notification { shell_->NotifyWindowClosed(); + // Something called -[NSWindow close] on a sheet rather than calling + // -[NSWindow endSheet:] on its parent. If the modal session is not ended + // then the parent will never be able to show another sheet. But calling + // -endSheet: here will block the thread with an animation, so post a task. + if (shell_->is_modal() && shell_->parent() && shell_->IsVisible()) { + NSWindow* window = shell_->GetNativeWindow().GetNativeNSWindow(); + NSWindow* sheetParent = [window sheetParent]; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(base::RetainBlock(^{ + [sheetParent endSheet:window]; + }))); + } + // Clears the delegate when window is going to be closed, since EL Capitan it // is possible that the methods of delegate would get called after the window // has been closed. diff --git a/shell/browser/ui/cocoa/atom_preview_item.h b/shell/browser/ui/cocoa/electron_preview_item.h similarity index 60% rename from shell/browser/ui/cocoa/atom_preview_item.h rename to shell/browser/ui/cocoa/electron_preview_item.h index c267570ce604c..f8172af1834f0 100644 --- a/shell/browser/ui/cocoa/atom_preview_item.h +++ b/shell/browser/ui/cocoa/electron_preview_item.h @@ -2,16 +2,16 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_UI_COCOA_ATOM_PREVIEW_ITEM_H_ -#define SHELL_BROWSER_UI_COCOA_ATOM_PREVIEW_ITEM_H_ +#ifndef SHELL_BROWSER_UI_COCOA_ELECTRON_PREVIEW_ITEM_H_ +#define SHELL_BROWSER_UI_COCOA_ELECTRON_PREVIEW_ITEM_H_ #import #import -@interface AtomPreviewItem : NSObject +@interface ElectronPreviewItem : NSObject @property(nonatomic, retain) NSURL* previewItemURL; @property(nonatomic, retain) NSString* previewItemTitle; - (id)initWithURL:(NSURL*)url title:(NSString*)title; @end -#endif // SHELL_BROWSER_UI_COCOA_ATOM_PREVIEW_ITEM_H_ +#endif // SHELL_BROWSER_UI_COCOA_ELECTRON_PREVIEW_ITEM_H_ diff --git a/shell/browser/ui/cocoa/atom_preview_item.mm b/shell/browser/ui/cocoa/electron_preview_item.mm similarity index 80% rename from shell/browser/ui/cocoa/atom_preview_item.mm rename to shell/browser/ui/cocoa/electron_preview_item.mm index 42920f62506e9..f7f9e8b26b554 100644 --- a/shell/browser/ui/cocoa/atom_preview_item.mm +++ b/shell/browser/ui/cocoa/electron_preview_item.mm @@ -2,9 +2,9 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/ui/cocoa/atom_preview_item.h" +#include "shell/browser/ui/cocoa/electron_preview_item.h" -@implementation AtomPreviewItem +@implementation ElectronPreviewItem @synthesize previewItemURL; @synthesize previewItemTitle; diff --git a/shell/browser/ui/cocoa/atom_touch_bar.h b/shell/browser/ui/cocoa/electron_touch_bar.h similarity index 91% rename from shell/browser/ui/cocoa/atom_touch_bar.h rename to shell/browser/ui/cocoa/electron_touch_bar.h index 0de32f7244332..e367a1b51d0bc 100644 --- a/shell/browser/ui/cocoa/atom_touch_bar.h +++ b/shell/browser/ui/cocoa/electron_touch_bar.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_UI_COCOA_ATOM_TOUCH_BAR_H_ -#define SHELL_BROWSER_UI_COCOA_ATOM_TOUCH_BAR_H_ +#ifndef SHELL_BROWSER_UI_COCOA_ELECTRON_TOUCH_BAR_H_ +#define SHELL_BROWSER_UI_COCOA_ELECTRON_TOUCH_BAR_H_ #import @@ -16,9 +16,9 @@ #include "native_mate/persistent_dictionary.h" #include "shell/browser/native_window.h" -@interface AtomTouchBar : NSObject { +@interface ElectronTouchBar : NSObject { @protected std::vector ordered_settings_; std::map settings_; @@ -97,4 +97,4 @@ @end -#endif // SHELL_BROWSER_UI_COCOA_ATOM_TOUCH_BAR_H_ +#endif // SHELL_BROWSER_UI_COCOA_ELECTRON_TOUCH_BAR_H_ diff --git a/shell/browser/ui/cocoa/atom_touch_bar.mm b/shell/browser/ui/cocoa/electron_touch_bar.mm similarity index 99% rename from shell/browser/ui/cocoa/atom_touch_bar.mm rename to shell/browser/ui/cocoa/electron_touch_bar.mm index 3e1660d2fbf2c..315dd05751dd1 100644 --- a/shell/browser/ui/cocoa/atom_touch_bar.mm +++ b/shell/browser/ui/cocoa/electron_touch_bar.mm @@ -2,9 +2,10 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#import "shell/browser/ui/cocoa/atom_touch_bar.h" +#import "shell/browser/ui/cocoa/electron_touch_bar.h" #include +#include #include #include "base/strings/sys_string_conversions.h" @@ -13,7 +14,7 @@ #include "skia/ext/skia_utils_mac.h" #include "ui/gfx/image/image.h" -@implementation AtomTouchBar +@implementation ElectronTouchBar static NSTouchBarItemIdentifier ButtonIdentifier = @"com.electron.touchbar.button."; diff --git a/shell/browser/ui/devtools_manager_delegate.cc b/shell/browser/ui/devtools_manager_delegate.cc index 3ff13b9627cc3..9dbc58d6b4926 100644 --- a/shell/browser/ui/devtools_manager_delegate.cc +++ b/shell/browser/ui/devtools_manager_delegate.cc @@ -27,7 +27,7 @@ #include "net/base/net_errors.h" #include "net/socket/stream_socket.h" #include "net/socket/tcp_server_socket.h" -#include "shell/browser/atom_paths.h" +#include "shell/browser/electron_paths.h" #include "ui/base/resource/resource_bundle.h" namespace electron { diff --git a/shell/browser/ui/devtools_ui.cc b/shell/browser/ui/devtools_ui.cc index 82faa65890a8b..3361ad0186398 100644 --- a/shell/browser/ui/devtools_ui.cc +++ b/shell/browser/ui/devtools_ui.cc @@ -6,6 +6,7 @@ #include #include +#include #include "base/memory/ref_counted_memory.h" #include "base/strings/string_util.h" @@ -60,21 +61,22 @@ class BundledDataSource : public content::URLDataSource { // content::URLDataSource implementation. std::string GetSource() override { return kChromeUIDevToolsHost; } - void StartDataRequest(const std::string& path, + void StartDataRequest(const GURL& url, const content::WebContents::Getter& wc_getter, - const GotDataCallback& callback) override { + GotDataCallback callback) override { + const std::string path = content::URLDataSource::URLToRequestPath(url); // Serve request from local bundle. std::string bundled_path_prefix(kChromeUIDevToolsBundledPath); bundled_path_prefix += "/"; if (base::StartsWith(path, bundled_path_prefix, base::CompareCase::INSENSITIVE_ASCII)) { StartBundledDataRequest(path.substr(bundled_path_prefix.length()), - callback); + std::move(callback)); return; } // We do not handle remote and custom requests. - callback.Run(nullptr); + std::move(callback).Run(nullptr); } std::string GetMimeType(const std::string& path) override { @@ -88,18 +90,16 @@ class BundledDataSource : public content::URLDataSource { bool ShouldServeMimeTypeAsContentTypeHeader() override { return true; } void StartBundledDataRequest(const std::string& path, - const GotDataCallback& callback) { + GotDataCallback callback) { std::string filename = PathWithoutParams(path); - base::StringPiece resource = - content::DevToolsFrontendHost::GetFrontendResource(filename); + scoped_refptr bytes = + content::DevToolsFrontendHost::GetFrontendResourceBytes(filename); - DLOG_IF(WARNING, resource.empty()) + DLOG_IF(WARNING, !bytes) << "Unable to find dev tool resource: " << filename << ". If you compiled with debug_devtools=1, try running with " "--debug-devtools."; - scoped_refptr bytes( - new base::RefCountedStaticMemory(resource.data(), resource.length())); - callback.Run(bytes.get()); + std::move(callback).Run(bytes); } private: diff --git a/shell/browser/ui/atom_menu_model.cc b/shell/browser/ui/electron_menu_model.cc similarity index 66% rename from shell/browser/ui/atom_menu_model.cc rename to shell/browser/ui/electron_menu_model.cc index f7f1cc6ddb914..3f20ae24ac64c 100644 --- a/shell/browser/ui/atom_menu_model.cc +++ b/shell/browser/ui/electron_menu_model.cc @@ -2,57 +2,57 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/ui/atom_menu_model.h" +#include "shell/browser/ui/electron_menu_model.h" #include "base/stl_util.h" namespace electron { -bool AtomMenuModel::Delegate::GetAcceleratorForCommandId( +bool ElectronMenuModel::Delegate::GetAcceleratorForCommandId( int command_id, ui::Accelerator* accelerator) const { return GetAcceleratorForCommandIdWithParams(command_id, false, accelerator); } -AtomMenuModel::AtomMenuModel(Delegate* delegate) +ElectronMenuModel::ElectronMenuModel(Delegate* delegate) : ui::SimpleMenuModel(delegate), delegate_(delegate) {} -AtomMenuModel::~AtomMenuModel() = default; +ElectronMenuModel::~ElectronMenuModel() = default; -void AtomMenuModel::SetToolTip(int index, const base::string16& toolTip) { +void ElectronMenuModel::SetToolTip(int index, const base::string16& toolTip) { int command_id = GetCommandIdAt(index); toolTips_[command_id] = toolTip; } -base::string16 AtomMenuModel::GetToolTipAt(int index) { +base::string16 ElectronMenuModel::GetToolTipAt(int index) { const int command_id = GetCommandIdAt(index); const auto iter = toolTips_.find(command_id); return iter == std::end(toolTips_) ? base::string16() : iter->second; } -void AtomMenuModel::SetRole(int index, const base::string16& role) { +void ElectronMenuModel::SetRole(int index, const base::string16& role) { int command_id = GetCommandIdAt(index); roles_[command_id] = role; } -base::string16 AtomMenuModel::GetRoleAt(int index) { +base::string16 ElectronMenuModel::GetRoleAt(int index) { const int command_id = GetCommandIdAt(index); const auto iter = roles_.find(command_id); return iter == std::end(roles_) ? base::string16() : iter->second; } -void AtomMenuModel::SetSublabel(int index, const base::string16& sublabel) { +void ElectronMenuModel::SetSublabel(int index, const base::string16& sublabel) { int command_id = GetCommandIdAt(index); sublabels_[command_id] = sublabel; } -base::string16 AtomMenuModel::GetSublabelAt(int index) const { +base::string16 ElectronMenuModel::GetSublabelAt(int index) const { int command_id = GetCommandIdAt(index); const auto iter = sublabels_.find(command_id); return iter == std::end(sublabels_) ? base::string16() : iter->second; } -bool AtomMenuModel::GetAcceleratorAtWithParams( +bool ElectronMenuModel::GetAcceleratorAtWithParams( int index, bool use_default_accelerator, ui::Accelerator* accelerator) const { @@ -63,7 +63,7 @@ bool AtomMenuModel::GetAcceleratorAtWithParams( return false; } -bool AtomMenuModel::ShouldRegisterAcceleratorAt(int index) const { +bool ElectronMenuModel::ShouldRegisterAcceleratorAt(int index) const { if (delegate_) { return delegate_->ShouldRegisterAcceleratorForCommandId( GetCommandIdAt(index)); @@ -71,29 +71,29 @@ bool AtomMenuModel::ShouldRegisterAcceleratorAt(int index) const { return true; } -bool AtomMenuModel::WorksWhenHiddenAt(int index) const { +bool ElectronMenuModel::WorksWhenHiddenAt(int index) const { if (delegate_) { return delegate_->ShouldCommandIdWorkWhenHidden(GetCommandIdAt(index)); } return true; } -void AtomMenuModel::MenuWillClose() { +void ElectronMenuModel::MenuWillClose() { ui::SimpleMenuModel::MenuWillClose(); for (Observer& observer : observers_) { observer.OnMenuWillClose(); } } -void AtomMenuModel::MenuWillShow() { +void ElectronMenuModel::MenuWillShow() { ui::SimpleMenuModel::MenuWillShow(); for (Observer& observer : observers_) { observer.OnMenuWillShow(); } } -AtomMenuModel* AtomMenuModel::GetSubmenuModelAt(int index) { - return static_cast( +ElectronMenuModel* ElectronMenuModel::GetSubmenuModelAt(int index) { + return static_cast( ui::SimpleMenuModel::GetSubmenuModelAt(index)); } diff --git a/shell/browser/ui/atom_menu_model.h b/shell/browser/ui/electron_menu_model.h similarity index 86% rename from shell/browser/ui/atom_menu_model.h rename to shell/browser/ui/electron_menu_model.h index 1de791bb9dc5d..40c314579fd8d 100644 --- a/shell/browser/ui/atom_menu_model.h +++ b/shell/browser/ui/electron_menu_model.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_UI_ATOM_MENU_MODEL_H_ -#define SHELL_BROWSER_UI_ATOM_MENU_MODEL_H_ +#ifndef SHELL_BROWSER_UI_ELECTRON_MENU_MODEL_H_ +#define SHELL_BROWSER_UI_ELECTRON_MENU_MODEL_H_ #include @@ -13,7 +13,7 @@ namespace electron { -class AtomMenuModel : public ui::SimpleMenuModel { +class ElectronMenuModel : public ui::SimpleMenuModel { public: class Delegate : public ui::SimpleMenuModel::Delegate { public: @@ -47,8 +47,8 @@ class AtomMenuModel : public ui::SimpleMenuModel { virtual void OnMenuWillClose() {} }; - explicit AtomMenuModel(Delegate* delegate); - ~AtomMenuModel() override; + explicit ElectronMenuModel(Delegate* delegate); + ~ElectronMenuModel() override; void AddObserver(Observer* obs) { observers_.AddObserver(obs); } void RemoveObserver(Observer* obs) { observers_.RemoveObserver(obs); } @@ -70,7 +70,7 @@ class AtomMenuModel : public ui::SimpleMenuModel { void MenuWillShow() override; using SimpleMenuModel::GetSubmenuModelAt; - AtomMenuModel* GetSubmenuModelAt(int index); + ElectronMenuModel* GetSubmenuModelAt(int index); private: Delegate* delegate_; // weak ref. @@ -80,9 +80,9 @@ class AtomMenuModel : public ui::SimpleMenuModel { std::map sublabels_; // command id -> sublabel base::ObserverList observers_; - DISALLOW_COPY_AND_ASSIGN(AtomMenuModel); + DISALLOW_COPY_AND_ASSIGN(ElectronMenuModel); }; } // namespace electron -#endif // SHELL_BROWSER_UI_ATOM_MENU_MODEL_H_ +#endif // SHELL_BROWSER_UI_ELECTRON_MENU_MODEL_H_ diff --git a/shell/browser/ui/file_dialog_gtk.cc b/shell/browser/ui/file_dialog_gtk.cc index 1325e3e6dc9b0..d7991a2a80d9d 100644 --- a/shell/browser/ui/file_dialog_gtk.cc +++ b/shell/browser/ui/file_dialog_gtk.cc @@ -5,7 +5,7 @@ #include #include "shell/browser/ui/file_dialog.h" -#include "shell/browser/ui/util_gtk.h" +#include "shell/browser/ui/gtk_util.h" #include "base/callback.h" #include "base/files/file_util.h" @@ -54,9 +54,9 @@ class FileChooserDialog { if (!settings.button_label.empty()) confirm_text = settings.button_label.c_str(); else if (action == GTK_FILE_CHOOSER_ACTION_SAVE) - confirm_text = gtk_util::kOpenLabel; - else if (action == GTK_FILE_CHOOSER_ACTION_OPEN) confirm_text = gtk_util::kSaveLabel; + else if (action == GTK_FILE_CHOOSER_ACTION_OPEN) + confirm_text = gtk_util::kOpenLabel; dialog_ = gtk_file_chooser_dialog_new( settings.title.c_str(), nullptr, action, gtk_util::kCancelLabel, diff --git a/shell/browser/ui/file_dialog_mac.mm b/shell/browser/ui/file_dialog_mac.mm index f2ac88038bf15..3a2c447b1ddd1 100644 --- a/shell/browser/ui/file_dialog_mac.mm +++ b/shell/browser/ui/file_dialog_mac.mm @@ -62,10 +62,10 @@ - (void)selectFormat:(id)sender { @end // Manages the PopUpButtonHandler. -@interface AtomAccessoryView : NSView +@interface ElectronAccessoryView : NSView @end -@implementation AtomAccessoryView +@implementation ElectronAccessoryView - (void)dealloc { auto* popupButton = @@ -92,7 +92,16 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) { for (const Filter& filter : filters) { NSMutableSet* file_type_set = [NSMutableSet set]; [filter_names addObject:@(filter.first.c_str())]; - for (const std::string& ext : filter.second) { + for (std::string ext : filter.second) { + // macOS is incapable of understanding multiple file extensions, + // so we need to tokenize the extension that's been passed in. + // We want to err on the side of allowing files, so we pass + // along only the final extension ('tar.gz' => 'gz'). + auto pos = ext.rfind('.'); + if (pos != std::string::npos) { + ext.erase(0, pos + 1); + } + [file_type_set addObject:@(ext.c_str())]; } [file_types_list addObject:[file_type_set allObjects]]; @@ -114,8 +123,8 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) { return; // don't add file format picker // Add file format picker. - AtomAccessoryView* accessoryView = - [[AtomAccessoryView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 200, 32.0)]; + ElectronAccessoryView* accessoryView = [[ElectronAccessoryView alloc] + initWithFrame:NSMakeRect(0.0, 0.0, 200, 32.0)]; NSTextField* label = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 60, 22)]; diff --git a/shell/browser/ui/file_dialog_win.cc b/shell/browser/ui/file_dialog_win.cc index 36428c97ba411..c2aec1c8a65a2 100644 --- a/shell/browser/ui/file_dialog_win.cc +++ b/shell/browser/ui/file_dialog_win.cc @@ -16,10 +16,9 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" #include "base/win/registry.h" #include "shell/browser/native_window_views.h" +#include "shell/browser/ui/win/dialog_thread.h" #include "shell/browser/unresponsive_suppressor.h" #include "shell/common/gin_converters/file_path_converter.h" @@ -63,67 +62,6 @@ void ConvertFilters(const Filters& filters, } } -struct RunState { - base::Thread* dialog_thread; - scoped_refptr ui_task_runner; -}; - -bool CreateDialogThread(RunState* run_state) { - auto thread = - std::make_unique(ELECTRON_PRODUCT_NAME "FileDialogThread"); - thread->init_com_with_mta(false); - if (!thread->Start()) - return false; - - run_state->dialog_thread = thread.release(); - run_state->ui_task_runner = base::ThreadTaskRunnerHandle::Get(); - return true; -} - -void OnDialogOpened(electron::util::Promise promise, - bool canceled, - std::vector paths) { - gin_helper::Dictionary dict = gin::Dictionary::CreateEmpty(promise.isolate()); - dict.Set("canceled", canceled); - dict.Set("filePaths", paths); - promise.ResolveWithGin(dict); -} - -void RunOpenDialogInNewThread( - const RunState& run_state, - const DialogSettings& settings, - electron::util::Promise promise) { - std::vector paths; - bool result = ShowOpenDialogSync(settings, &paths); - run_state.ui_task_runner->PostTask( - FROM_HERE, - base::BindOnce(&OnDialogOpened, std::move(promise), !result, paths)); - run_state.ui_task_runner->DeleteSoon(FROM_HERE, run_state.dialog_thread); -} - -void OnSaveDialogDone(electron::util::Promise promise, - bool canceled, - const base::FilePath path) { - gin_helper::Dictionary dict = gin::Dictionary::CreateEmpty(promise.isolate()); - dict.Set("canceled", canceled); - dict.Set("filePath", path); - promise.ResolveWithGin(dict); -} - -void RunSaveDialogInNewThread( - const RunState& run_state, - const DialogSettings& settings, - electron::util::Promise promise) { - base::FilePath path; - bool result = ShowSaveDialogSync(settings, &path); - run_state.ui_task_runner->PostTask( - FROM_HERE, - base::BindOnce(&OnSaveDialogDone, std::move(promise), !result, path)); - run_state.ui_task_runner->DeleteSoon(FROM_HERE, run_state.dialog_thread); -} - -} // namespace - static HRESULT GetFileNameFromShellItem(IShellItem* pShellItem, SIGDN type, LPWSTR lpstr, @@ -218,6 +156,8 @@ static void ApplySettings(IFileDialog* dialog, const DialogSettings& settings) { } } +} // namespace + bool ShowOpenDialogSync(const DialogSettings& settings, std::vector* paths) { ATL::CComPtr file_open_dialog; @@ -276,17 +216,17 @@ bool ShowOpenDialogSync(const DialogSettings& settings, void ShowOpenDialog(const DialogSettings& settings, electron::util::Promise promise) { - gin_helper::Dictionary dict = gin::Dictionary::CreateEmpty(promise.isolate()); - RunState run_state; - if (!CreateDialogThread(&run_state)) { - dict.Set("canceled", true); - dict.Set("filePaths", std::vector()); + auto done = [](electron::util::Promise promise, + bool success, std::vector result) { + v8::Locker locker(promise.isolate()); + v8::HandleScope handle_scope(promise.isolate()); + gin::Dictionary dict = gin::Dictionary::CreateEmpty(promise.isolate()); + dict.Set("canceled", !success); + dict.Set("filePaths", result); promise.ResolveWithGin(dict); - } else { - run_state.dialog_thread->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&RunOpenDialogInNewThread, run_state, - settings, std::move(promise))); - } + }; + dialog_thread::Run(base::BindOnce(ShowOpenDialogSync, settings), + base::BindOnce(done, std::move(promise))); } bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) { @@ -326,18 +266,17 @@ bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) { void ShowSaveDialog(const DialogSettings& settings, electron::util::Promise promise) { - RunState run_state; - if (!CreateDialogThread(&run_state)) { - gin_helper::Dictionary dict = - gin::Dictionary::CreateEmpty(promise.isolate()); - dict.Set("canceled", true); - dict.Set("filePath", base::FilePath()); + auto done = [](electron::util::Promise promise, + bool success, base::FilePath result) { + v8::Locker locker(promise.isolate()); + v8::HandleScope handle_scope(promise.isolate()); + gin::Dictionary dict = gin::Dictionary::CreateEmpty(promise.isolate()); + dict.Set("canceled", !success); + dict.Set("filePath", result); promise.ResolveWithGin(dict); - } else { - run_state.dialog_thread->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&RunSaveDialogInNewThread, run_state, - settings, std::move(promise))); - } + }; + dialog_thread::Run(base::BindOnce(ShowSaveDialogSync, settings), + base::BindOnce(done, std::move(promise))); } } // namespace file_dialog diff --git a/shell/browser/ui/gtk_util.cc b/shell/browser/ui/gtk_util.cc new file mode 100644 index 0000000000000..3bed1a8edafa8 --- /dev/null +++ b/shell/browser/ui/gtk_util.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2019 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/ui/gtk_util.h" + +#include +#include +#include + +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkUnPreMultiply.h" + +namespace gtk_util { + +// Copied from L40-L55 in +// https://cs.chromium.org/chromium/src/chrome/browser/ui/libgtkui/select_file_dialog_impl_gtk.cc +#if GTK_CHECK_VERSION(3, 90, 0) +// GTK stock items have been deprecated. The docs say to switch to using the +// strings "_Open", etc. However this breaks i18n. We could supply our own +// internationalized strings, but the "_" in these strings is significant: it's +// the keyboard shortcut to select these actions. TODO: Provide +// internationalized strings when GTK provides support for it. +const char* const kCancelLabel = "_Cancel"; +const char* const kNoLabel = "_No"; +const char* const kOkLabel = "_OK"; +const char* const kOpenLabel = "_Open"; +const char* const kSaveLabel = "_Save"; +const char* const kYesLabel = "_Yes"; +#else +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +const char* const kCancelLabel = GTK_STOCK_CANCEL; +const char* const kNoLabel = GTK_STOCK_NO; +const char* const kOkLabel = GTK_STOCK_OK; +const char* const kOpenLabel = GTK_STOCK_OPEN; +const char* const kSaveLabel = GTK_STOCK_SAVE; +const char* const kYesLabel = GTK_STOCK_YES; +G_GNUC_END_IGNORE_DEPRECATIONS +#endif + +GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap) { + if (bitmap.isNull()) + return nullptr; + + int width = bitmap.width(); + int height = bitmap.height(); + + GdkPixbuf* pixbuf = + gdk_pixbuf_new(GDK_COLORSPACE_RGB, // The only colorspace gtk supports. + TRUE, // There is an alpha channel. + 8, width, height); + + // SkBitmaps are premultiplied, we need to unpremultiply them. + const int kBytesPerPixel = 4; + uint8_t* divided = gdk_pixbuf_get_pixels(pixbuf); + + for (int y = 0, i = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + uint32_t pixel = bitmap.getAddr32(0, y)[x]; + + int alpha = SkColorGetA(pixel); + if (alpha != 0 && alpha != 255) { + SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel); + divided[i + 0] = SkColorGetR(unmultiplied); + divided[i + 1] = SkColorGetG(unmultiplied); + divided[i + 2] = SkColorGetB(unmultiplied); + divided[i + 3] = alpha; + } else { + divided[i + 0] = SkColorGetR(pixel); + divided[i + 1] = SkColorGetG(pixel); + divided[i + 2] = SkColorGetB(pixel); + divided[i + 3] = alpha; + } + i += kBytesPerPixel; + } + } + + return pixbuf; +} + +} // namespace gtk_util diff --git a/shell/browser/ui/util_gtk.h b/shell/browser/ui/gtk_util.h similarity index 55% rename from shell/browser/ui/util_gtk.h rename to shell/browser/ui/gtk_util.h index 62909aa7a125b..db9dc9cc36d1d 100644 --- a/shell/browser/ui/util_gtk.h +++ b/shell/browser/ui/gtk_util.h @@ -2,8 +2,12 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_UI_UTIL_GTK_H_ -#define SHELL_BROWSER_UI_UTIL_GTK_H_ +#ifndef SHELL_BROWSER_UI_GTK_UTIL_H_ +#define SHELL_BROWSER_UI_GTK_UTIL_H_ + +#include + +class SkBitmap; namespace gtk_util { @@ -16,6 +20,11 @@ extern const char* const kOpenLabel; extern const char* const kSaveLabel; extern const char* const kYesLabel; +// Convert and copy a SkBitmap to a GdkPixbuf. NOTE: this uses BGRAToRGBA, so +// it is an expensive operation. The returned GdkPixbuf will have a refcount of +// 1, and the caller is responsible for unrefing it when done. +GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap); + } // namespace gtk_util -#endif // SHELL_BROWSER_UI_UTIL_GTK_H_ +#endif // SHELL_BROWSER_UI_GTK_UTIL_H_ diff --git a/shell/browser/ui/inspectable_web_contents_impl.cc b/shell/browser/ui/inspectable_web_contents_impl.cc index ab2ef83c1b589..73c882ac504c8 100644 --- a/shell/browser/ui/inspectable_web_contents_impl.cc +++ b/shell/browser/ui/inspectable_web_contents_impl.cc @@ -27,10 +27,12 @@ #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/file_select_listener.h" +#include "content/public/browser/file_url_loader.h" #include "content/public/browser/host_zoom_map.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/shared_cors_origin_access_list.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/user_agent.h" #include "ipc/ipc_channel.h" @@ -62,7 +64,7 @@ const char kChromeUIDevToolsURL[] = "textColor=rgba(0,0,0,1)&" "experiments=true"; const char kChromeUIDevToolsRemoteFrontendBase[] = - "https://devtools-frontend.appspot.com/"; + "https://chrome-devtools-frontend.appspot.com/"; const char kChromeUIDevToolsRemoteFrontendPath[] = "serve_file"; const char kDevToolsBoundsPref[] = "electron.devtools.bounds"; @@ -150,29 +152,87 @@ GURL GetDevToolsURL(bool can_dock) { return GURL(url_string); } +constexpr base::TimeDelta kInitialBackoffDelay = + base::TimeDelta::FromMilliseconds(250); +constexpr base::TimeDelta kMaxBackoffDelay = base::TimeDelta::FromSeconds(10); + } // namespace class InspectableWebContentsImpl::NetworkResourceLoader : public network::SimpleURLLoaderStreamConsumer { public: - NetworkResourceLoader(int stream_id, - InspectableWebContentsImpl* bindings, - std::unique_ptr loader, - network::mojom::URLLoaderFactory* url_loader_factory, - const DispatchCallback& callback) + class URLLoaderFactoryHolder { + public: + network::mojom::URLLoaderFactory* get() { + return ptr_.get() ? ptr_.get() : refptr_.get(); + } + void operator=(std::unique_ptr&& ptr) { + ptr_ = std::move(ptr); + } + void operator=(scoped_refptr&& refptr) { + refptr_ = std::move(refptr); + } + + private: + std::unique_ptr ptr_; + scoped_refptr refptr_; + }; + + static void Create(int stream_id, + InspectableWebContentsImpl* bindings, + const network::ResourceRequest& resource_request, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + URLLoaderFactoryHolder url_loader_factory, + const DispatchCallback& callback, + base::TimeDelta retry_delay = base::TimeDelta()) { + auto resource_loader = + std::make_unique( + stream_id, bindings, resource_request, traffic_annotation, + std::move(url_loader_factory), callback, retry_delay); + bindings->loaders_.insert(std::move(resource_loader)); + } + + NetworkResourceLoader( + int stream_id, + InspectableWebContentsImpl* bindings, + const network::ResourceRequest& resource_request, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + URLLoaderFactoryHolder url_loader_factory, + const DispatchCallback& callback, + base::TimeDelta delay) : stream_id_(stream_id), bindings_(bindings), - loader_(std::move(loader)), - callback_(callback) { + resource_request_(resource_request), + traffic_annotation_(traffic_annotation), + loader_(network::SimpleURLLoader::Create( + std::make_unique(resource_request), + traffic_annotation)), + url_loader_factory_(std::move(url_loader_factory)), + callback_(callback), + retry_delay_(delay) { loader_->SetOnResponseStartedCallback(base::BindOnce( &NetworkResourceLoader::OnResponseStarted, base::Unretained(this))); - loader_->DownloadAsStream(url_loader_factory, this); + timer_.Start(FROM_HERE, delay, + base::BindRepeating(&NetworkResourceLoader::DownloadAsStream, + base::Unretained(this))); } NetworkResourceLoader(const NetworkResourceLoader&) = delete; NetworkResourceLoader& operator=(const NetworkResourceLoader&) = delete; private: + void DownloadAsStream() { + loader_->DownloadAsStream(url_loader_factory_.get(), this); + } + + base::TimeDelta GetNextExponentialBackoffDelay(const base::TimeDelta& delta) { + if (delta.is_zero()) { + return kInitialBackoffDelay; + } else { + return delta * 1.3; + } + } + void OnResponseStarted(const GURL& final_url, const network::mojom::URLResponseHead& response_head) { response_headers_ = response_head.headers; @@ -199,21 +259,34 @@ class InspectableWebContentsImpl::NetworkResourceLoader } void OnComplete(bool success) override { - base::DictionaryValue response; - response.SetInteger("statusCode", response_headers_ - ? response_headers_->response_code() - : 200); - - auto headers = std::make_unique(); - size_t iterator = 0; - std::string name; - std::string value; - while (response_headers_ && - response_headers_->EnumerateHeaderLines(&iterator, &name, &value)) - headers->SetString(name, value); - - response.Set("headers", std::move(headers)); - callback_.Run(&response); + if (!success && loader_->NetError() == net::ERR_INSUFFICIENT_RESOURCES && + retry_delay_ < kMaxBackoffDelay) { + const base::TimeDelta delay = + GetNextExponentialBackoffDelay(retry_delay_); + LOG(WARNING) << "InspectableWebContentsImpl::NetworkResourceLoader id = " + << stream_id_ + << " failed with insufficient resources, retrying in " + << delay << "." << std::endl; + NetworkResourceLoader::Create( + stream_id_, bindings_, resource_request_, traffic_annotation_, + std::move(url_loader_factory_), callback_, delay); + } else { + base::DictionaryValue response; + response.SetInteger("statusCode", response_headers_ + ? response_headers_->response_code() + : 200); + + auto headers = std::make_unique(); + size_t iterator = 0; + std::string name; + std::string value; + while (response_headers_ && + response_headers_->EnumerateHeaderLines(&iterator, &name, &value)) + headers->SetString(name, value); + + response.Set("headers", std::move(headers)); + callback_.Run(&response); + } bindings_->loaders_.erase(bindings_->loaders_.find(this)); } @@ -222,9 +295,14 @@ class InspectableWebContentsImpl::NetworkResourceLoader const int stream_id_; InspectableWebContentsImpl* const bindings_; + const network::ResourceRequest resource_request_; + const net::NetworkTrafficAnnotationTag traffic_annotation_; std::unique_ptr loader_; + URLLoaderFactoryHolder url_loader_factory_; DispatchCallback callback_; scoped_refptr response_headers_; + base::OneShotTimer timer_; + base::TimeDelta retry_delay_; }; // Implemented separately on each platform. @@ -528,19 +606,45 @@ void InspectableWebContentsImpl::LoadNetworkResource( return; } - auto resource_request = std::make_unique(); - resource_request->url = gurl; - resource_request->headers.AddHeadersFromString(headers); - - auto* partition = content::BrowserContext::GetDefaultStoragePartition( - GetDevToolsWebContents()->GetBrowserContext()); - auto factory = partition->GetURLLoaderFactoryForBrowserProcess(); + // Create traffic annotation tag. + net::NetworkTrafficAnnotationTag traffic_annotation = + net::DefineNetworkTrafficAnnotation("devtools_network_resource", R"( + semantics { + sender: "Developer Tools" + description: + "When user opens Developer Tools, the browser may fetch additional " + "resources from the network to enrich the debugging experience " + "(e.g. source map resources)." + trigger: "User opens Developer Tools to debug a web page." + data: "Any resources requested by Developer Tools." + destination: WEBSITE + } + policy { + cookies_allowed: YES + cookies_store: "user" + setting: + "It's not possible to disable this feature from settings." + })"); + + network::ResourceRequest resource_request; + resource_request.url = gurl; + resource_request.site_for_cookies = gurl; + resource_request.headers.AddHeadersFromString(headers); + + NetworkResourceLoader::URLLoaderFactoryHolder url_loader_factory; + if (gurl.SchemeIsFile()) { + url_loader_factory = content::CreateFileURLLoaderFactory( + base::FilePath() /* profile_path */, + nullptr /* shared_cors_origin_access_list */); + } else { + auto* partition = content::BrowserContext::GetDefaultStoragePartition( + GetDevToolsWebContents()->GetBrowserContext()); + url_loader_factory = partition->GetURLLoaderFactoryForBrowserProcess(); + } - auto simple_url_loader = network::SimpleURLLoader::Create( - std::move(resource_request), MISSING_TRAFFIC_ANNOTATION); - auto resource_loader = std::make_unique( - stream_id, this, std::move(simple_url_loader), factory.get(), callback); - loaders_.insert(std::move(resource_loader)); + NetworkResourceLoader::Create(stream_id, this, resource_request, + traffic_annotation, + std::move(url_loader_factory), callback); } void InspectableWebContentsImpl::SetIsDocked(const DispatchCallback& callback, @@ -805,22 +909,6 @@ bool InspectableWebContentsImpl::DidAddMessageToConsole( return true; } -bool InspectableWebContentsImpl::ShouldCreateWebContents( - content::WebContents* web_contents, - content::RenderFrameHost* opener, - content::SiteInstance* source_site_instance, - int32_t route_id, - int32_t main_frame_route_id, - int32_t main_frame_widget_route_id, - content::mojom::WindowContainerType window_container_type, - const GURL& opener_url, - const std::string& frame_name, - const GURL& target_url, - const std::string& partition_id, - content::SessionStorageNamespace* session_storage_namespace) { - return false; -} - bool InspectableWebContentsImpl::HandleKeyboardEvent( content::WebContents* source, const content::NativeWebKeyboardEvent& event) { diff --git a/shell/browser/ui/inspectable_web_contents_impl.h b/shell/browser/ui/inspectable_web_contents_impl.h index 5a93e458f324e..b38ed36c34eb1 100644 --- a/shell/browser/ui/inspectable_web_contents_impl.h +++ b/shell/browser/ui/inspectable_web_contents_impl.h @@ -176,19 +176,6 @@ class InspectableWebContentsImpl const base::string16& message, int32_t line_no, const base::string16& source_id) override; - bool ShouldCreateWebContents( - content::WebContents* web_contents, - content::RenderFrameHost* opener, - content::SiteInstance* source_site_instance, - int32_t route_id, - int32_t main_frame_route_id, - int32_t main_frame_widget_route_id, - content::mojom::WindowContainerType window_container_type, - const GURL& opener_url, - const std::string& frame_name, - const GURL& target_url, - const std::string& partition_id, - content::SessionStorageNamespace* session_storage_namespace) override; bool HandleKeyboardEvent(content::WebContents*, const content::NativeWebKeyboardEvent&) override; void CloseContents(content::WebContents* source) override; diff --git a/shell/browser/ui/inspectable_web_contents_view_mac.h b/shell/browser/ui/inspectable_web_contents_view_mac.h index a788c0dadfa40..cd83588be20fd 100644 --- a/shell/browser/ui/inspectable_web_contents_view_mac.h +++ b/shell/browser/ui/inspectable_web_contents_view_mac.h @@ -10,7 +10,7 @@ #include "base/mac/scoped_nsobject.h" -@class AtomInspectableWebContentsView; +@class ElectronInspectableWebContentsView; namespace electron { @@ -40,7 +40,7 @@ class InspectableWebContentsViewMac : public InspectableWebContentsView { // Owns us. InspectableWebContentsImpl* inspectable_web_contents_; - base::scoped_nsobject view_; + base::scoped_nsobject view_; DISALLOW_COPY_AND_ASSIGN(InspectableWebContentsViewMac); }; diff --git a/shell/browser/ui/inspectable_web_contents_view_mac.mm b/shell/browser/ui/inspectable_web_contents_view_mac.mm index 4a241a7917d4d..7d8da3bf51d88 100644 --- a/shell/browser/ui/inspectable_web_contents_view_mac.mm +++ b/shell/browser/ui/inspectable_web_contents_view_mac.mm @@ -8,7 +8,7 @@ #import #include "base/strings/sys_string_conversions.h" -#import "shell/browser/ui/cocoa/atom_inspectable_web_contents_view.h" +#import "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h" #include "shell/browser/ui/inspectable_web_contents.h" #include "shell/browser/ui/inspectable_web_contents_view_delegate.h" @@ -22,7 +22,7 @@ InspectableWebContentsViewMac::InspectableWebContentsViewMac( InspectableWebContentsImpl* inspectable_web_contents) : inspectable_web_contents_(inspectable_web_contents), - view_([[AtomInspectableWebContentsView alloc] + view_([[ElectronInspectableWebContentsView alloc] initWithInspectableWebContentsViewMac:this]) {} InspectableWebContentsViewMac::~InspectableWebContentsViewMac() { diff --git a/shell/browser/ui/message_box.h b/shell/browser/ui/message_box.h index 514fd7704b887..d2548ffa53667 100644 --- a/shell/browser/ui/message_box.h +++ b/shell/browser/ui/message_box.h @@ -6,6 +6,7 @@ #define SHELL_BROWSER_UI_MESSAGE_BOX_H_ #include +#include #include #include "base/callback_forward.h" @@ -24,10 +25,7 @@ enum class MessageBoxType { kQuestion, }; -enum MessageBoxOptions { - MESSAGE_BOX_NONE = 0, - MESSAGE_BOX_NO_LINK = 1 << 0, -}; +using DialogResult = std::pair; struct MessageBoxSettings { electron::NativeWindow* parent_window = nullptr; @@ -35,7 +33,7 @@ struct MessageBoxSettings { std::vector buttons; int default_id; int cancel_id; - int options = electron::MessageBoxOptions::MESSAGE_BOX_NONE; + bool no_link = false; std::string title; std::string message; std::string detail; diff --git a/shell/browser/ui/message_box_gtk.cc b/shell/browser/ui/message_box_gtk.cc index c3480cb7ada26..e5ec1bfc61153 100644 --- a/shell/browser/ui/message_box_gtk.cc +++ b/shell/browser/ui/message_box_gtk.cc @@ -2,14 +2,13 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. +#include "shell/browser/ui/gtk_util.h" #include "shell/browser/ui/message_box.h" -#include "shell/browser/ui/util_gtk.h" #include "base/callback.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/libgtkui/gtk_util.h" -#include "chrome/browser/ui/libgtkui/skia_utils_gtk.h" #include "shell/browser/browser.h" #include "shell/browser/native_window_observer.h" #include "shell/browser/native_window_views.h" @@ -56,7 +55,7 @@ class GtkMessageBox : public NativeWindowObserver { static constexpr int pixel_width = 48; static constexpr int pixel_height = 48; GdkPixbuf* pixbuf = - libgtkui::GdkPixbufFromSkBitmap(*settings.icon.bitmap()); + gtk_util::GdkPixbufFromSkBitmap(*settings.icon.bitmap()); GdkPixbuf* scaled_pixbuf = gdk_pixbuf_scale_simple( pixbuf, pixel_width, pixel_height, GDK_INTERP_BILINEAR); GtkWidget* w = gtk_image_new_from_pixbuf(scaled_pixbuf); diff --git a/shell/browser/ui/message_box_mac.mm b/shell/browser/ui/message_box_mac.mm index 5e91015c30bc4..63820e6e3167a 100644 --- a/shell/browser/ui/message_box_mac.mm +++ b/shell/browser/ui/message_box_mac.mm @@ -59,9 +59,11 @@ int button_count = static_cast([ns_buttons count]); if (settings.default_id >= 0 && settings.default_id < button_count) { - // Focus the button at default_id if the user opted to do so. - // The first button added gets set as the default selected. - // So remove that default, and make the requested button the default. + // Highlight the button at default_id + [[ns_buttons objectAtIndex:settings.default_id] highlight:YES]; + + // The first button added gets set as the default selected, so remove + // that and set the button @ default_id to be default. [[ns_buttons objectAtIndex:0] setKeyEquivalent:@""]; [[ns_buttons objectAtIndex:settings.default_id] setKeyEquivalent:@"\r"]; } @@ -95,8 +97,10 @@ int ShowMessageBoxSync(const MessageBoxSettings& settings) { NSAlert* alert = CreateNSAlert(settings); // Use runModal for synchronous alert without parent, since we don't have a - // window to wait for. - if (!settings.parent_window) + // window to wait for. Also use it when window is provided but it is not + // shown as it would be impossible to dismiss the alert if it is connected + // to invisible window (see #22671). + if (!settings.parent_window || !settings.parent_window->IsVisible()) return [[alert autorelease] runModal]; __block int ret_code = -1; diff --git a/shell/browser/ui/message_box_win.cc b/shell/browser/ui/message_box_win.cc index 208bf0b22d93b..975f092381a41 100644 --- a/shell/browser/ui/message_box_win.cc +++ b/shell/browser/ui/message_box_win.cc @@ -14,12 +14,10 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/task/post_task.h" -#include "base/threading/thread.h" #include "base/win/scoped_gdi_object.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" #include "shell/browser/browser.h" #include "shell/browser/native_window_views.h" +#include "shell/browser/ui/win/dialog_thread.h" #include "shell/browser/unresponsive_suppressor.h" #include "ui/gfx/icon_util.h" #include "ui/gfx/image/image_skia.h" @@ -78,18 +76,18 @@ void MapToCommonID(const std::vector& buttons, } } -int ShowTaskDialogUTF16(NativeWindow* parent, - MessageBoxType type, - const std::vector& buttons, - int default_id, - int cancel_id, - int options, - const base::string16& title, - const base::string16& message, - const base::string16& detail, - const base::string16& checkbox_label, - bool* checkbox_checked, - const gfx::ImageSkia& icon) { +DialogResult ShowTaskDialogUTF16(NativeWindow* parent, + MessageBoxType type, + const std::vector& buttons, + int default_id, + int cancel_id, + bool no_link, + const base::string16& title, + const base::string16& message, + const base::string16& detail, + const base::string16& checkbox_label, + bool checkbox_checked, + const gfx::ImageSkia& icon) { TASKDIALOG_FLAGS flags = TDF_SIZE_TO_CONTENT | // Show all content. TDF_ALLOW_DIALOG_CANCELLATION; // Allow canceling the dialog. @@ -148,17 +146,15 @@ int ShowTaskDialogUTF16(NativeWindow* parent, if (!checkbox_label.empty()) { config.pszVerificationText = checkbox_label.c_str(); - - if (checkbox_checked && *checkbox_checked) { + if (checkbox_checked) config.dwFlags |= TDF_VERIFICATION_FLAG_CHECKED; - } } // Iterate through the buttons, put common buttons in dwCommonButtons // and custom buttons in pButtons. std::map id_map; std::vector dialog_buttons; - if (options & MESSAGE_BOX_NO_LINK) { + if (no_link) { for (size_t i = 0; i < buttons.size(); ++i) dialog_buttons.push_back( {static_cast(i + kIDStart), buttons[i].c_str()}); @@ -168,26 +164,27 @@ int ShowTaskDialogUTF16(NativeWindow* parent, if (dialog_buttons.size() > 0) { config.pButtons = &dialog_buttons.front(); config.cButtons = dialog_buttons.size(); - if (!(options & MESSAGE_BOX_NO_LINK)) + if (!no_link) config.dwFlags |= TDF_USE_COMMAND_LINKS; // custom buttons as links. } + int button_id; + int id = 0; BOOL verificationFlagChecked = FALSE; TaskDialogIndirect(&config, &id, nullptr, &verificationFlagChecked); - if (checkbox_checked) { - *checkbox_checked = verificationFlagChecked; - } if (id_map.find(id) != id_map.end()) // common button. - return id_map[id]; + button_id = id_map[id]; else if (id >= kIDStart) // custom button. - return id - kIDStart; + button_id = id - kIDStart; else - return cancel_id; + button_id = cancel_id; + + return std::make_pair(button_id, verificationFlagChecked); } -int ShowTaskDialogUTF8(const MessageBoxSettings& settings) { +DialogResult ShowTaskDialogUTF8(const MessageBoxSettings& settings) { std::vector utf16_buttons; for (const auto& button : settings.buttons) utf16_buttons.push_back(base::UTF8ToUTF16(button)); @@ -197,53 +194,35 @@ int ShowTaskDialogUTF8(const MessageBoxSettings& settings) { const base::string16 detail_16 = base::UTF8ToUTF16(settings.detail); const base::string16 checkbox_label_16 = base::UTF8ToUTF16(settings.checkbox_label); - bool cb_checked = settings.checkbox_checked; return ShowTaskDialogUTF16( settings.parent_window, settings.type, utf16_buttons, settings.default_id, - settings.cancel_id, settings.options, title_16, message_16, detail_16, - checkbox_label_16, &cb_checked, settings.icon); -} - -void RunMessageBoxInNewThread(base::Thread* thread, - const MessageBoxSettings& settings, - MessageBoxCallback callback) { - int result = ShowTaskDialogUTF8(settings); - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(std::move(callback), result, settings.checkbox_checked)); - content::BrowserThread::DeleteSoon(content::BrowserThread::UI, FROM_HERE, - thread); + settings.cancel_id, settings.no_link, title_16, message_16, detail_16, + checkbox_label_16, settings.checkbox_checked, settings.icon); } } // namespace int ShowMessageBoxSync(const MessageBoxSettings& settings) { electron::UnresponsiveSuppressor suppressor; - return ShowTaskDialogUTF8(settings); + DialogResult result = ShowTaskDialogUTF8(settings); + return result.first; } void ShowMessageBox(const MessageBoxSettings& settings, MessageBoxCallback callback) { - auto thread = - std::make_unique(ELECTRON_PRODUCT_NAME "MessageBoxThread"); - thread->init_com_with_mta(false); - if (!thread->Start()) { - std::move(callback).Run(settings.cancel_id, settings.checkbox_checked); - return; - } - - base::Thread* unretained = thread.release(); - unretained->task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&RunMessageBoxInNewThread, base::Unretained(unretained), - settings, std::move(callback))); + dialog_thread::Run(base::BindOnce(&ShowTaskDialogUTF8, settings), + base::BindOnce( + [](MessageBoxCallback callback, DialogResult result) { + std::move(callback).Run(result.first, result.second); + }, + std::move(callback))); } void ShowErrorBox(const base::string16& title, const base::string16& content) { electron::UnresponsiveSuppressor suppressor; - ShowTaskDialogUTF16(nullptr, MessageBoxType::kError, {}, -1, 0, 0, L"Error", - title, content, L"", nullptr, gfx::ImageSkia()); + ShowTaskDialogUTF16(nullptr, MessageBoxType::kError, {}, -1, 0, false, + L"Error", title, content, L"", false, gfx::ImageSkia()); } } // namespace electron diff --git a/shell/browser/ui/tray_icon.cc b/shell/browser/ui/tray_icon.cc index 37a867d59428f..cf76ae3e0c84a 100644 --- a/shell/browser/ui/tray_icon.cc +++ b/shell/browser/ui/tray_icon.cc @@ -21,7 +21,7 @@ void TrayIcon::RemoveBalloon() {} void TrayIcon::Focus() {} void TrayIcon::PopUpContextMenu(const gfx::Point& pos, - AtomMenuModel* menu_model) {} + ElectronMenuModel* menu_model) {} gfx::Rect TrayIcon::GetBounds() { return gfx::Rect(); diff --git a/shell/browser/ui/tray_icon.h b/shell/browser/ui/tray_icon.h index e3964e5cbfac6..bb211de9e9ea6 100644 --- a/shell/browser/ui/tray_icon.h +++ b/shell/browser/ui/tray_icon.h @@ -9,7 +9,7 @@ #include #include "base/observer_list.h" -#include "shell/browser/ui/atom_menu_model.h" +#include "shell/browser/ui/electron_menu_model.h" #include "shell/browser/ui/tray_icon_observer.h" #include "ui/gfx/geometry/rect.h" @@ -79,10 +79,10 @@ class TrayIcon { // Popups the menu. virtual void PopUpContextMenu(const gfx::Point& pos, - AtomMenuModel* menu_model); + ElectronMenuModel* menu_model); // Set the context menu for this icon. - virtual void SetContextMenu(AtomMenuModel* menu_model) = 0; + virtual void SetContextMenu(ElectronMenuModel* menu_model) = 0; // Returns the bounds of tray icon. virtual gfx::Rect GetBounds(); diff --git a/shell/browser/ui/tray_icon_cocoa.h b/shell/browser/ui/tray_icon_cocoa.h index 7b38fa84c505f..91611ef6ac99f 100644 --- a/shell/browser/ui/tray_icon_cocoa.h +++ b/shell/browser/ui/tray_icon_cocoa.h @@ -12,7 +12,7 @@ #include "base/mac/scoped_nsobject.h" #include "shell/browser/ui/tray_icon.h" -@class AtomMenuController; +@class ElectronMenuController; @class StatusItemView; namespace electron { @@ -29,10 +29,10 @@ class TrayIconCocoa : public TrayIcon { std::string GetTitle() override; void SetIgnoreDoubleClickEvents(bool ignore) override; bool GetIgnoreDoubleClickEvents() override; - void PopUpOnUI(AtomMenuModel* menu_model); + void PopUpOnUI(ElectronMenuModel* menu_model); void PopUpContextMenu(const gfx::Point& pos, - AtomMenuModel* menu_model) override; - void SetContextMenu(AtomMenuModel* menu_model) override; + ElectronMenuModel* menu_model) override; + void SetContextMenu(ElectronMenuModel* menu_model) override; gfx::Rect GetBounds() override; private: @@ -40,7 +40,7 @@ class TrayIconCocoa : public TrayIcon { base::scoped_nsobject status_item_view_; // Status menu shown when right-clicking the system icon. - base::scoped_nsobject menu_; + base::scoped_nsobject menu_; base::WeakPtrFactory weak_factory_; diff --git a/shell/browser/ui/tray_icon_cocoa.mm b/shell/browser/ui/tray_icon_cocoa.mm index e2b67abbe822c..ba1751066039b 100644 --- a/shell/browser/ui/tray_icon_cocoa.mm +++ b/shell/browser/ui/tray_icon_cocoa.mm @@ -14,14 +14,14 @@ #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "shell/browser/ui/cocoa/NSString+ANSI.h" -#include "shell/browser/ui/cocoa/atom_menu_controller.h" +#include "shell/browser/ui/cocoa/electron_menu_controller.h" #include "ui/events/cocoa/cocoa_event_utils.h" #include "ui/gfx/mac/coordinate_conversion.h" #include "ui/native_theme/native_theme.h" @interface StatusItemView : NSView { - electron::TrayIconCocoa* trayIcon_; // weak - AtomMenuController* menuController_; // weak + electron::TrayIconCocoa* trayIcon_; // weak + ElectronMenuController* menuController_; // weak BOOL ignoreDoubleClickEvents_; base::scoped_nsobject statusItem_; base::scoped_nsobject trackingArea_; @@ -87,12 +87,12 @@ - (void)removeItem { } - (void)setImage:(NSImage*)image { - [[statusItem_ button] setImage:[image copy]]; + [[statusItem_ button] setImage:image]; [self updateDimensions]; } - (void)setAlternateImage:(NSImage*)image { - [[statusItem_ button] setAlternateImage:[image copy]]; + [[statusItem_ button] setAlternateImage:image]; } - (void)setIgnoreDoubleClickEvents:(BOOL)ignore { @@ -108,7 +108,7 @@ - (void)setTitle:(NSString*)title { [[statusItem_ button] setAttributedTitle:[title attributedStringParsingANSICodes]]; } else { - [[statusItem_ button] setTitle:[title copy]]; + [[statusItem_ button] setTitle:title]; } // Fix icon margins. @@ -125,7 +125,7 @@ - (NSString*)title { return [statusItem_ button].title; } -- (void)setMenuController:(AtomMenuController*)menu { +- (void)setMenuController:(ElectronMenuController*)menu { menuController_ = menu; [statusItem_ setMenu:[menuController_ menu]]; } @@ -162,19 +162,27 @@ - (void)mouseUp:(NSEvent*)event { ui::EventFlagsFromModifiers([event modifierFlags])); } -- (void)popUpContextMenu:(electron::AtomMenuModel*)menu_model { +- (void)popUpContextMenu:(electron::ElectronMenuModel*)menu_model { // Make sure events can be pumped while the menu is up. base::MessageLoopCurrent::ScopedNestableTaskAllower allow; // Show a custom menu. if (menu_model) { - base::scoped_nsobject menuController( - [[AtomMenuController alloc] initWithModel:menu_model - useDefaultAccelerator:NO]); + base::scoped_nsobject menuController( + [[ElectronMenuController alloc] initWithModel:menu_model + useDefaultAccelerator:NO]); // Hacky way to mimic design of ordinary tray menu. [statusItem_ setMenu:[menuController menu]]; + // -performClick: is a blocking call, which will run the task loop inside + // itself. This can potentially include running JS, which can result in + // this object being released. We take a temporary reference here to make + // sure we stay alive long enough to successfully return from this + // function. + // TODO(nornagon/codebytere): Avoid nesting task loops here. + [self retain]; [[statusItem_ button] performClick:self]; [statusItem_ setMenu:[menuController_ menu]]; + [self release]; return; } @@ -295,23 +303,23 @@ - (BOOL)performDragOperation:(id)sender { return [status_item_view_ getIgnoreDoubleClickEvents]; } -void TrayIconCocoa::PopUpOnUI(AtomMenuModel* menu_model) { +void TrayIconCocoa::PopUpOnUI(ElectronMenuModel* menu_model) { [status_item_view_ popUpContextMenu:menu_model]; } void TrayIconCocoa::PopUpContextMenu(const gfx::Point& pos, - AtomMenuModel* menu_model) { + ElectronMenuModel* menu_model) { base::PostTask( FROM_HERE, {content::BrowserThread::UI}, base::BindOnce(&TrayIconCocoa::PopUpOnUI, weak_factory_.GetWeakPtr(), base::Unretained(menu_model))); } -void TrayIconCocoa::SetContextMenu(AtomMenuModel* menu_model) { +void TrayIconCocoa::SetContextMenu(ElectronMenuModel* menu_model) { if (menu_model) { // Create native menu. - menu_.reset([[AtomMenuController alloc] initWithModel:menu_model - useDefaultAccelerator:NO]); + menu_.reset([[ElectronMenuController alloc] initWithModel:menu_model + useDefaultAccelerator:NO]); } else { menu_.reset(); } diff --git a/shell/browser/ui/tray_icon_gtk.cc b/shell/browser/ui/tray_icon_gtk.cc index 87949d0b861a8..e36c420f4d1f1 100644 --- a/shell/browser/ui/tray_icon_gtk.cc +++ b/shell/browser/ui/tray_icon_gtk.cc @@ -10,15 +10,41 @@ #include "shell/browser/browser.h" #include "shell/common/application_info.h" #include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia_operations.h" namespace electron { +namespace { + +gfx::ImageSkia GetIconFromImage(const gfx::Image& image) { + auto icon = image.AsImageSkia(); + auto size = icon.size(); + + // Systray icons are historically 22 pixels tall, e.g. on Ubuntu GNOME, + // KDE, and xfce. Taller icons are causing incorrect sizing issues -- e.g. + // a 1x1 icon -- so for now, pin the height manually. Similar behavior to + // https://bugs.chromium.org/p/chromium/issues/detail?id=1042098 ? + static constexpr int DESIRED_HEIGHT = 22; + if ((size.height() != 0) && (size.height() != DESIRED_HEIGHT)) { + const double ratio = DESIRED_HEIGHT / static_cast(size.height()); + size = gfx::Size(static_cast(ratio * size.width()), + static_cast(ratio * size.height())); + icon = gfx::ImageSkiaOperations::CreateResizedImage( + icon, skia::ImageOperations::RESIZE_BEST, size); + } + + return icon; +} + +} // namespace + TrayIconGtk::TrayIconGtk() = default; TrayIconGtk::~TrayIconGtk() = default; void TrayIconGtk::SetImage(const gfx::Image& image) { - image_ = image.AsImageSkia(); + image_ = GetIconFromImage(image); + if (icon_) { icon_->SetIcon(image_); return; @@ -37,9 +63,9 @@ void TrayIconGtk::SetToolTip(const std::string& tool_tip) { icon_->SetToolTip(tool_tip_); } -void TrayIconGtk::SetContextMenu(AtomMenuModel* menu_model) { - icon_->UpdatePlatformContextMenu(menu_model_); +void TrayIconGtk::SetContextMenu(ElectronMenuModel* menu_model) { menu_model_ = menu_model; + icon_->UpdatePlatformContextMenu(menu_model_); } const gfx::ImageSkia& TrayIconGtk::GetImage() const { diff --git a/shell/browser/ui/tray_icon_gtk.h b/shell/browser/ui/tray_icon_gtk.h index a474b99196dad..bf10df377af2e 100644 --- a/shell/browser/ui/tray_icon_gtk.h +++ b/shell/browser/ui/tray_icon_gtk.h @@ -26,7 +26,7 @@ class TrayIconGtk : public TrayIcon, public views::StatusIconLinux::Delegate { // TrayIcon: void SetImage(const gfx::Image& image) override; void SetToolTip(const std::string& tool_tip) override; - void SetContextMenu(AtomMenuModel* menu_model) override; + void SetContextMenu(ElectronMenuModel* menu_model) override; // views::StatusIconLinux::Delegate void OnClick() override; diff --git a/shell/browser/ui/util_gtk.cc b/shell/browser/ui/util_gtk.cc deleted file mode 100644 index dfe9231eee45d..0000000000000 --- a/shell/browser/ui/util_gtk.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2019 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "shell/browser/ui/util_gtk.h" - -#include - -namespace gtk_util { - -// Copied from L40-L55 in -// https://cs.chromium.org/chromium/src/chrome/browser/ui/libgtkui/select_file_dialog_impl_gtk.cc -#if GTK_CHECK_VERSION(3, 90, 0) -// GTK stock items have been deprecated. The docs say to switch to using the -// strings "_Open", etc. However this breaks i18n. We could supply our own -// internationalized strings, but the "_" in these strings is significant: it's -// the keyboard shortcut to select these actions. TODO: Provide -// internationalized strings when GTK provides support for it. -const char* const kCancelLabel = "_Cancel"; -const char* const kNoLabel = "_No"; -const char* const kOkLabel = "_OK"; -const char* const kOpenLabel = "_Open"; -const char* const kSaveLabel = "_Save"; -const char* const kYesLabel = "_Yes"; -#else -G_GNUC_BEGIN_IGNORE_DEPRECATIONS -const char* const kCancelLabel = GTK_STOCK_CANCEL; -const char* const kNoLabel = GTK_STOCK_NO; -const char* const kOkLabel = GTK_STOCK_OK; -const char* const kOpenLabel = GTK_STOCK_OPEN; -const char* const kSaveLabel = GTK_STOCK_SAVE; -const char* const kYesLabel = GTK_STOCK_YES; -G_GNUC_END_IGNORE_DEPRECATIONS -#endif - -} // namespace gtk_util diff --git a/shell/browser/ui/views/autofill_popup_view.cc b/shell/browser/ui/views/autofill_popup_view.cc index bae2b8696b624..798c1a371654b 100644 --- a/shell/browser/ui/views/autofill_popup_view.cc +++ b/shell/browser/ui/views/autofill_popup_view.cc @@ -11,6 +11,7 @@ #include "base/i18n/rtl.h" #include "cc/paint/skia_paint_canvas.h" #include "content/public/browser/render_view_host.h" +#include "ui/accessibility/ax_enums.mojom.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/point.h" diff --git a/shell/browser/ui/views/atom_views_delegate.cc b/shell/browser/ui/views/electron_views_delegate.cc similarity index 85% rename from shell/browser/ui/views/atom_views_delegate.cc rename to shell/browser/ui/views/electron_views_delegate.cc index c0d42b0975531..e21023f1b3c32 100644 --- a/shell/browser/ui/views/atom_views_delegate.cc +++ b/shell/browser/ui/views/electron_views_delegate.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-CHROMIUM file. -#include "shell/browser/ui/views/atom_views_delegate.h" +#include "shell/browser/ui/views/electron_views_delegate.h" #include @@ -53,22 +53,7 @@ void ViewsDelegate::NotifyMenuItemFocused(const base::string16& menu_name, int item_count, bool has_submenu) {} -#if defined(OS_WIN) -HICON ViewsDelegate::GetDefaultWindowIcon() const { - // Use current exe's icon as default window icon. - return LoadIcon(GetModuleHandle(NULL), - MAKEINTRESOURCE(1 /* IDR_MAINFRAME */)); -} - -HICON ViewsDelegate::GetSmallWindowIcon() const { - return GetDefaultWindowIcon(); -} - -bool ViewsDelegate::IsWindowInMetro(gfx::NativeWindow window) const { - return false; -} - -#elif defined(OS_LINUX) && !defined(OS_CHROMEOS) +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) gfx::ImageSkia* ViewsDelegate::GetDefaultWindowIcon() const { return nullptr; } diff --git a/shell/browser/ui/views/atom_views_delegate.h b/shell/browser/ui/views/electron_views_delegate.h similarity index 65% rename from shell/browser/ui/views/atom_views_delegate.h rename to shell/browser/ui/views/electron_views_delegate.h index d3a086652efb5..8ed463e38a12c 100644 --- a/shell/browser/ui/views/atom_views_delegate.h +++ b/shell/browser/ui/views/electron_views_delegate.h @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-CHROMIUM file. -#ifndef SHELL_BROWSER_UI_VIEWS_ATOM_VIEWS_DELEGATE_H_ -#define SHELL_BROWSER_UI_VIEWS_ATOM_VIEWS_DELEGATE_H_ +#ifndef SHELL_BROWSER_UI_VIEWS_ELECTRON_VIEWS_DELEGATE_H_ +#define SHELL_BROWSER_UI_VIEWS_ELECTRON_VIEWS_DELEGATE_H_ +#include #include #include "base/compiler_specific.h" @@ -37,6 +38,8 @@ class ViewsDelegate : public views::ViewsDelegate { HICON GetDefaultWindowIcon() const override; HICON GetSmallWindowIcon() const override; bool IsWindowInMetro(gfx::NativeWindow window) const override; + int GetAppbarAutohideEdges(HMONITOR monitor, + base::OnceClosure callback) override; #elif defined(OS_LINUX) && !defined(OS_CHROMEOS) gfx::ImageSkia* GetDefaultWindowIcon() const override; #endif @@ -50,9 +53,27 @@ class ViewsDelegate : public views::ViewsDelegate { bool WindowManagerProvidesTitleBar(bool maximized) override; private: +#if defined(OS_WIN) + using AppbarAutohideEdgeMap = std::map; + + // Callback on main thread with the edges. |returned_edges| is the value that + // was returned from the call to GetAutohideEdges() that initiated the lookup. + void OnGotAppbarAutohideEdges(base::OnceClosure callback, + HMONITOR monitor, + int returned_edges, + int edges); + + AppbarAutohideEdgeMap appbar_autohide_edge_map_; + // If true we're in the process of notifying a callback from + // GetAutohideEdges().start a new query. + bool in_autohide_edges_callback_ = false; + + base::WeakPtrFactory weak_factory_{this}; +#endif + DISALLOW_COPY_AND_ASSIGN(ViewsDelegate); }; } // namespace electron -#endif // SHELL_BROWSER_UI_VIEWS_ATOM_VIEWS_DELEGATE_H_ +#endif // SHELL_BROWSER_UI_VIEWS_ELECTRON_VIEWS_DELEGATE_H_ diff --git a/shell/browser/ui/views/electron_views_delegate_win.cc b/shell/browser/ui/views/electron_views_delegate_win.cc new file mode 100644 index 0000000000000..ff2c6f0274d82 --- /dev/null +++ b/shell/browser/ui/views/electron_views_delegate_win.cc @@ -0,0 +1,158 @@ +// Copyright (c) 2017 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-CHROMIUM file. + +#include "shell/browser/ui/views/electron_views_delegate.h" + +#include +#include + +#include + +#include "base/bind.h" +#include "base/task/post_task.h" + +namespace { + +bool MonitorHasAutohideTaskbarForEdge(UINT edge, HMONITOR monitor) { + APPBARDATA taskbar_data = {sizeof(APPBARDATA), NULL, 0, edge}; + taskbar_data.hWnd = ::GetForegroundWindow(); + + // MSDN documents an ABM_GETAUTOHIDEBAREX, which supposedly takes a monitor + // rect and returns autohide bars on that monitor. This sounds like a good + // idea for multi-monitor systems. Unfortunately, it appears to not work at + // least some of the time (erroneously returning NULL) and there's almost no + // online documentation or other sample code using it that suggests ways to + // address this problem. We do the following:- + // 1. Use the ABM_GETAUTOHIDEBAR message. If it works, i.e. returns a valid + // window we are done. + // 2. If the ABM_GETAUTOHIDEBAR message does not work we query the auto hide + // state of the taskbar and then retrieve its position. That call returns + // the edge on which the taskbar is present. If it matches the edge we + // are looking for, we are done. + // NOTE: This call spins a nested run loop. + HWND taskbar = reinterpret_cast( + SHAppBarMessage(ABM_GETAUTOHIDEBAR, &taskbar_data)); + if (!::IsWindow(taskbar)) { + APPBARDATA taskbar_data = {sizeof(APPBARDATA), 0, 0, 0}; + unsigned int taskbar_state = SHAppBarMessage(ABM_GETSTATE, &taskbar_data); + if (!(taskbar_state & ABS_AUTOHIDE)) + return false; + + taskbar_data.hWnd = ::FindWindow(L"Shell_TrayWnd", NULL); + if (!::IsWindow(taskbar_data.hWnd)) + return false; + + SHAppBarMessage(ABM_GETTASKBARPOS, &taskbar_data); + if (taskbar_data.uEdge == edge) + taskbar = taskbar_data.hWnd; + } + + // There is a potential race condition here: + // 1. A maximized chrome window is fullscreened. + // 2. It is switched back to maximized. + // 3. In the process the window gets a WM_NCCACLSIZE message which calls us to + // get the autohide state. + // 4. The worker thread is invoked. It calls the API to get the autohide + // state. On Windows versions earlier than Windows 7, taskbars could + // easily be always on top or not. + // This meant that we only want to look for taskbars which have the topmost + // bit set. However this causes problems in cases where the window on the + // main thread is still in the process of switching away from fullscreen. + // In this case the taskbar might not yet have the topmost bit set. + // 5. The main thread resumes and does not leave space for the taskbar and + // hence it does not pop when hovered. + // + // To address point 4 above, it is best to not check for the WS_EX_TOPMOST + // window style on the taskbar, as starting from Windows 7, the topmost + // style is always set. We don't support XP and Vista anymore. + if (::IsWindow(taskbar)) { + if (MonitorFromWindow(taskbar, MONITOR_DEFAULTTONEAREST) == monitor) + return true; + // In some cases like when the autohide taskbar is on the left of the + // secondary monitor, the MonitorFromWindow call above fails to return the + // correct monitor the taskbar is on. We fallback to MonitorFromPoint for + // the cursor position in that case, which seems to work well. + POINT cursor_pos = {0}; + GetCursorPos(&cursor_pos); + if (MonitorFromPoint(cursor_pos, MONITOR_DEFAULTTONEAREST) == monitor) + return true; + } + return false; +} + +int GetAppbarAutohideEdgesOnWorkerThread(HMONITOR monitor) { + DCHECK(monitor); + + int edges = 0; + if (MonitorHasAutohideTaskbarForEdge(ABE_LEFT, monitor)) + edges |= views::ViewsDelegate::EDGE_LEFT; + if (MonitorHasAutohideTaskbarForEdge(ABE_TOP, monitor)) + edges |= views::ViewsDelegate::EDGE_TOP; + if (MonitorHasAutohideTaskbarForEdge(ABE_RIGHT, monitor)) + edges |= views::ViewsDelegate::EDGE_RIGHT; + if (MonitorHasAutohideTaskbarForEdge(ABE_BOTTOM, monitor)) + edges |= views::ViewsDelegate::EDGE_BOTTOM; + return edges; +} + +} // namespace + +namespace electron { + +HICON ViewsDelegate::GetDefaultWindowIcon() const { + // Use current exe's icon as default window icon. + return LoadIcon(GetModuleHandle(NULL), + MAKEINTRESOURCE(1 /* IDR_MAINFRAME */)); +} + +HICON ViewsDelegate::GetSmallWindowIcon() const { + return GetDefaultWindowIcon(); +} + +bool ViewsDelegate::IsWindowInMetro(gfx::NativeWindow window) const { + return false; +} + +int ViewsDelegate::GetAppbarAutohideEdges(HMONITOR monitor, + base::OnceClosure callback) { + // Initialize the map with EDGE_BOTTOM. This is important, as if we return an + // initial value of 0 (no auto-hide edges) then we'll go fullscreen and + // windows will automatically remove WS_EX_TOPMOST from the appbar resulting + // in us thinking there is no auto-hide edges. By returning at least one edge + // we don't initially go fullscreen until we figure out the real auto-hide + // edges. + if (!appbar_autohide_edge_map_.count(monitor)) + appbar_autohide_edge_map_[monitor] = EDGE_BOTTOM; + + // We use the SHAppBarMessage API to get the taskbar autohide state. This API + // spins a modal loop which could cause callers to be reentered. To avoid + // that we retrieve the taskbar state in a worker thread. + if (monitor && !in_autohide_edges_callback_) { + // TODO(robliao): Annotate this task with .WithCOM() once supported. + // https://crbug.com/662122 + base::PostTaskAndReplyWithResult( + FROM_HERE, + {base::ThreadPool(), base::MayBlock(), + base::TaskPriority::USER_BLOCKING}, + base::BindOnce(&GetAppbarAutohideEdgesOnWorkerThread, monitor), + base::BindOnce(&ViewsDelegate::OnGotAppbarAutohideEdges, + weak_factory_.GetWeakPtr(), std::move(callback), monitor, + appbar_autohide_edge_map_[monitor])); + } + return appbar_autohide_edge_map_[monitor]; +} + +void ViewsDelegate::OnGotAppbarAutohideEdges(base::OnceClosure callback, + HMONITOR monitor, + int returned_edges, + int edges) { + appbar_autohide_edge_map_[monitor] = edges; + if (returned_edges == edges) + return; + + base::AutoReset in_callback_setter(&in_autohide_edges_callback_, true); + std::move(callback).Run(); +} + +} // namespace electron diff --git a/shell/browser/ui/views/global_menu_bar_x11.cc b/shell/browser/ui/views/global_menu_bar_x11.cc index f5996efa1cc20..44a260281af0a 100644 --- a/shell/browser/ui/views/global_menu_bar_x11.cc +++ b/shell/browser/ui/views/global_menu_bar_x11.cc @@ -12,7 +12,7 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h" #include "shell/browser/native_window_views.h" -#include "shell/browser/ui/atom_menu_model.h" +#include "shell/browser/ui/electron_menu_model.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/base/accelerators/menu_label_accelerator_util_linux.h" @@ -134,8 +134,8 @@ void EnsureMethodsLoaded() { dlsym(dbusmenu_lib, "dbusmenu_server_set_root")); } -AtomMenuModel* ModelForMenuItem(DbusmenuMenuitem* item) { - return reinterpret_cast( +ElectronMenuModel* ModelForMenuItem(DbusmenuMenuitem* item) { + return reinterpret_cast( g_object_get_data(G_OBJECT(item), "model")); } @@ -156,7 +156,7 @@ void SetMenuItemID(DbusmenuMenuitem* item, int id) { g_object_set_data(G_OBJECT(item), "menu-id", GINT_TO_POINTER(id + 1)); } -std::string GetMenuModelStatus(AtomMenuModel* model) { +std::string GetMenuModelStatus(ElectronMenuModel* model) { std::string ret; for (int i = 0; i < model->GetItemCount(); ++i) { int status = model->GetTypeAt(i) | (model->IsVisibleAt(i) << 3) | @@ -192,7 +192,7 @@ std::string GlobalMenuBarX11::GetPathForWindow(gfx::AcceleratedWidget xid) { return base::StringPrintf("/com/canonical/menu/%lX", xid); } -void GlobalMenuBarX11::SetMenu(AtomMenuModel* menu_model) { +void GlobalMenuBarX11::SetMenu(ElectronMenuModel* menu_model) { if (!IsServerStarted()) return; @@ -224,14 +224,14 @@ void GlobalMenuBarX11::OnWindowUnmapped() { GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid_); } -void GlobalMenuBarX11::BuildMenuFromModel(AtomMenuModel* model, +void GlobalMenuBarX11::BuildMenuFromModel(ElectronMenuModel* model, DbusmenuMenuitem* parent) { for (int i = 0; i < model->GetItemCount(); ++i) { DbusmenuMenuitem* item = menuitem_new(); menuitem_property_set_bool(item, kPropertyVisible, model->IsVisibleAt(i)); - AtomMenuModel::ItemType type = model->GetTypeAt(i); - if (type == AtomMenuModel::TYPE_SEPARATOR) { + ElectronMenuModel::ItemType type = model->GetTypeAt(i); + if (type == ElectronMenuModel::TYPE_SEPARATOR) { menuitem_property_set(item, kPropertyType, kTypeSeparator); } else { std::string label = ui::ConvertAcceleratorsFromWindowsStyle( @@ -242,7 +242,7 @@ void GlobalMenuBarX11::BuildMenuFromModel(AtomMenuModel* model, g_object_set_data(G_OBJECT(item), "model", model); SetMenuItemID(item, i); - if (type == AtomMenuModel::TYPE_SUBMENU) { + if (type == ElectronMenuModel::TYPE_SUBMENU) { menuitem_property_set(item, kPropertyChildrenDisplay, kDisplaySubmenu); g_signal_connect(item, "about-to-show", G_CALLBACK(OnSubMenuShowThunk), this); @@ -254,11 +254,12 @@ void GlobalMenuBarX11::BuildMenuFromModel(AtomMenuModel* model, g_signal_connect(item, "item-activated", G_CALLBACK(OnItemActivatedThunk), this); - if (type == AtomMenuModel::TYPE_CHECK || - type == AtomMenuModel::TYPE_RADIO) { - menuitem_property_set( - item, kPropertyToggleType, - type == AtomMenuModel::TYPE_CHECK ? kToggleCheck : kToggleRadio); + if (type == ElectronMenuModel::TYPE_CHECK || + type == ElectronMenuModel::TYPE_RADIO) { + menuitem_property_set(item, kPropertyToggleType, + type == ElectronMenuModel::TYPE_CHECK + ? kToggleCheck + : kToggleRadio); menuitem_property_set_int(item, kPropertyToggleState, model->IsItemCheckedAt(i)); } @@ -303,14 +304,14 @@ void GlobalMenuBarX11::RegisterAccelerator(DbusmenuMenuitem* item, void GlobalMenuBarX11::OnItemActivated(DbusmenuMenuitem* item, unsigned int timestamp) { int id; - AtomMenuModel* model = ModelForMenuItem(item); + ElectronMenuModel* model = ModelForMenuItem(item); if (model && GetMenuItemID(item, &id)) model->ActivatedAt(id, 0); } void GlobalMenuBarX11::OnSubMenuShow(DbusmenuMenuitem* item) { int id; - AtomMenuModel* model = ModelForMenuItem(item); + ElectronMenuModel* model = ModelForMenuItem(item); if (!model || !GetMenuItemID(item, &id)) return; diff --git a/shell/browser/ui/views/global_menu_bar_x11.h b/shell/browser/ui/views/global_menu_bar_x11.h index b0ccf332176cf..467ce62548e2d 100644 --- a/shell/browser/ui/views/global_menu_bar_x11.h +++ b/shell/browser/ui/views/global_menu_bar_x11.h @@ -9,7 +9,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "shell/browser/ui/atom_menu_model.h" +#include "shell/browser/ui/electron_menu_model.h" #include "ui/base/glib/glib_signal.h" #include "ui/gfx/native_widget_types.h" @@ -43,7 +43,7 @@ class GlobalMenuBarX11 { // Creates the object path for DbusmenuServer which is attached to |xid|. static std::string GetPathForWindow(gfx::AcceleratedWidget xid); - void SetMenu(AtomMenuModel* menu_model); + void SetMenu(ElectronMenuModel* menu_model); bool IsServerStarted() const; // Called by NativeWindow when it show/hides. @@ -55,7 +55,7 @@ class GlobalMenuBarX11 { void InitServer(gfx::AcceleratedWidget xid); // Create a menu from menu model. - void BuildMenuFromModel(AtomMenuModel* model, DbusmenuMenuitem* parent); + void BuildMenuFromModel(ElectronMenuModel* model, DbusmenuMenuitem* parent); // Sets the accelerator for |item|. void RegisterAccelerator(DbusmenuMenuitem* item, diff --git a/shell/browser/ui/views/menu_bar.cc b/shell/browser/ui/views/menu_bar.cc index ec5dd316fefb3..a04a9724467e9 100644 --- a/shell/browser/ui/views/menu_bar.cc +++ b/shell/browser/ui/views/menu_bar.cc @@ -68,7 +68,7 @@ MenuBar::~MenuBar() { window_->GetFocusManager()->RemoveFocusChangeListener(color_updater_.get()); } -void MenuBar::SetMenu(AtomMenuModel* model) { +void MenuBar::SetMenu(ElectronMenuModel* model) { menu_model_ = model; RebuildChildren(); } @@ -101,7 +101,7 @@ int MenuBar::GetItemCount() const { } bool MenuBar::GetMenuButtonFromScreenPoint(const gfx::Point& screenPoint, - AtomMenuModel** menu_model, + ElectronMenuModel** menu_model, views::MenuButton** button) { if (!GetBoundsInScreen().Contains(screenPoint)) return false; @@ -109,7 +109,7 @@ bool MenuBar::GetMenuButtonFromScreenPoint(const gfx::Point& screenPoint, auto children = GetChildrenInZOrder(); for (int i = 0, n = children.size(); i < n; ++i) { if (children[i]->GetBoundsInScreen().Contains(screenPoint) && - (menu_model_->GetTypeAt(i) == AtomMenuModel::TYPE_SUBMENU)) { + (menu_model_->GetTypeAt(i) == ElectronMenuModel::TYPE_SUBMENU)) { *menu_model = menu_model_->GetSubmenuModelAt(i); *button = static_cast(children[i]); return true; @@ -166,9 +166,8 @@ bool MenuBar::AcceleratorPressed(const ui::Accelerator& accelerator) { electron::KeyboardCodeFromCharCode(button->accelerator(), &shifted); if (keycode == accelerator.key_code()) { - const gfx::Point p(0, 0); auto event = accelerator.ToKeyEvent(); - OnMenuButtonClicked(button, p, &event); + ButtonPressed(button, event); return true; } } @@ -255,9 +254,7 @@ const char* MenuBar::GetClassName() const { return kViewClassName; } -void MenuBar::OnMenuButtonClicked(views::Button* source, - const gfx::Point& point, - const ui::Event* event) { +void MenuBar::ButtonPressed(views::Button* source, const ui::Event& event) { // Hide the accelerator when a submenu is activated. SetAcceleratorVisibility(false); @@ -268,18 +265,17 @@ void MenuBar::OnMenuButtonClicked(views::Button* source, window_->RequestFocus(); int id = source->tag(); - AtomMenuModel::ItemType type = menu_model_->GetTypeAt(id); - if (type != AtomMenuModel::TYPE_SUBMENU) { + ElectronMenuModel::ItemType type = menu_model_->GetTypeAt(id); + if (type != ElectronMenuModel::TYPE_SUBMENU) { menu_model_->ActivatedAt(id, 0); return; } // Deleted in MenuDelegate::OnMenuClosed MenuDelegate* menu_delegate = new MenuDelegate(this); - menu_delegate->RunMenu(menu_model_->GetSubmenuModelAt(id), source, - event != nullptr && event->IsKeyEvent() - ? ui::MENU_SOURCE_KEYBOARD - : ui::MENU_SOURCE_MOUSE); + menu_delegate->RunMenu( + menu_model_->GetSubmenuModelAt(id), source, + event.IsKeyEvent() ? ui::MENU_SOURCE_KEYBOARD : ui::MENU_SOURCE_MOUSE); menu_delegate->AddObserver(this); } diff --git a/shell/browser/ui/views/menu_bar.h b/shell/browser/ui/views/menu_bar.h index c7192ed6c9add..67fc4e3f54fe8 100644 --- a/shell/browser/ui/views/menu_bar.h +++ b/shell/browser/ui/views/menu_bar.h @@ -7,11 +7,11 @@ #include -#include "shell/browser/ui/atom_menu_model.h" +#include "shell/browser/ui/electron_menu_model.h" #include "shell/browser/ui/views/menu_delegate.h" #include "shell/browser/ui/views/root_view.h" #include "ui/views/accessible_pane_view.h" -#include "ui/views/controls/button/menu_button_listener.h" +#include "ui/views/controls/button/button.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/view.h" @@ -37,7 +37,7 @@ class MenuBarColorUpdater : public views::FocusChangeListener { }; class MenuBar : public views::AccessiblePaneView, - public views::MenuButtonListener, + public views::ButtonListener, public electron::MenuDelegate::Observer { public: static const char kViewClassName[]; @@ -46,7 +46,7 @@ class MenuBar : public views::AccessiblePaneView, ~MenuBar() override; // Replaces current menu with a new one. - void SetMenu(AtomMenuModel* menu_model); + void SetMenu(ElectronMenuModel* menu_model); // Shows underline under accelerators. void SetAcceleratorVisibility(bool visible); @@ -62,7 +62,7 @@ class MenuBar : public views::AccessiblePaneView, // Get the menu under specified screen point. bool GetMenuButtonFromScreenPoint(const gfx::Point& point, - AtomMenuModel** menu_model, + ElectronMenuModel** menu_model, views::MenuButton** button); // electron::MenuDelegate::Observer: @@ -71,20 +71,17 @@ class MenuBar : public views::AccessiblePaneView, // views::AccessiblePaneView: bool AcceleratorPressed(const ui::Accelerator& accelerator) override; + bool SetPaneFocus(views::View* initial_focus) override; + void RemovePaneFocus() override; protected: // views::View: const char* GetClassName() const override; - // views::MenuButtonListener: - void OnMenuButtonClicked(views::Button* source, - const gfx::Point& point, - const ui::Event* event) override; + // views::ButtonListener: + void ButtonPressed(views::Button* source, const ui::Event& event) override; void OnThemeChanged() override; - bool SetPaneFocus(views::View* initial_focus); - void RemovePaneFocus(); - private: friend class MenuBarColorUpdater; @@ -99,7 +96,7 @@ class MenuBar : public views::AccessiblePaneView, #endif RootView* window_ = nullptr; - AtomMenuModel* menu_model_ = nullptr; + ElectronMenuModel* menu_model_ = nullptr; View* FindAccelChild(base::char16 key); diff --git a/shell/browser/ui/views/menu_delegate.cc b/shell/browser/ui/views/menu_delegate.cc index ec0ce612f2daf..3623914dad269 100644 --- a/shell/browser/ui/views/menu_delegate.cc +++ b/shell/browser/ui/views/menu_delegate.cc @@ -23,7 +23,7 @@ MenuDelegate::MenuDelegate(MenuBar* menu_bar) MenuDelegate::~MenuDelegate() = default; -void MenuDelegate::RunMenu(AtomMenuModel* model, +void MenuDelegate::RunMenu(ElectronMenuModel* model, views::Button* button, ui::MenuSourceType source_type) { gfx::Point screen_loc; @@ -123,7 +123,7 @@ views::MenuItemView* MenuDelegate::GetSiblingMenu( // TODO(zcbenz): We should follow Chromium's logics on implementing the // sibling menu switches, this code is almost a hack. views::MenuButton* button; - AtomMenuModel* model; + ElectronMenuModel* model; if (menu_bar_->GetMenuButtonFromScreenPoint(screen_point, &model, &button) && button->tag() != id_) { bool switch_in_progress = !!button_to_open_; diff --git a/shell/browser/ui/views/menu_delegate.h b/shell/browser/ui/views/menu_delegate.h index db19b265c65f4..73aafb486372a 100644 --- a/shell/browser/ui/views/menu_delegate.h +++ b/shell/browser/ui/views/menu_delegate.h @@ -8,7 +8,7 @@ #include #include "base/observer_list.h" -#include "shell/browser/ui/atom_menu_model.h" +#include "shell/browser/ui/electron_menu_model.h" #include "ui/views/controls/menu/menu_delegate.h" namespace views { @@ -25,7 +25,7 @@ class MenuDelegate : public views::MenuDelegate { explicit MenuDelegate(MenuBar* menu_bar); ~MenuDelegate() override; - void RunMenu(AtomMenuModel* model, + void RunMenu(ElectronMenuModel* model, views::Button* button, ui::MenuSourceType source_type); diff --git a/shell/browser/ui/views/menu_model_adapter.cc b/shell/browser/ui/views/menu_model_adapter.cc index 0fe6b8b9c2c6a..be8083833d01d 100644 --- a/shell/browser/ui/views/menu_model_adapter.cc +++ b/shell/browser/ui/views/menu_model_adapter.cc @@ -6,7 +6,7 @@ namespace electron { -MenuModelAdapter::MenuModelAdapter(AtomMenuModel* menu_model) +MenuModelAdapter::MenuModelAdapter(ElectronMenuModel* menu_model) : views::MenuModelAdapter(menu_model), menu_model_(menu_model) {} MenuModelAdapter::~MenuModelAdapter() = default; @@ -16,7 +16,7 @@ bool MenuModelAdapter::GetAccelerator(int id, ui::MenuModel* model = menu_model_; int index = 0; if (ui::MenuModel::GetModelAndIndexForCommandId(id, &model, &index)) { - return static_cast(model)->GetAcceleratorAtWithParams( + return static_cast(model)->GetAcceleratorAtWithParams( index, true, accelerator); } return false; diff --git a/shell/browser/ui/views/menu_model_adapter.h b/shell/browser/ui/views/menu_model_adapter.h index 6647fa3435304..1468bea3d1252 100644 --- a/shell/browser/ui/views/menu_model_adapter.h +++ b/shell/browser/ui/views/menu_model_adapter.h @@ -5,21 +5,21 @@ #ifndef SHELL_BROWSER_UI_VIEWS_MENU_MODEL_ADAPTER_H_ #define SHELL_BROWSER_UI_VIEWS_MENU_MODEL_ADAPTER_H_ -#include "shell/browser/ui/atom_menu_model.h" +#include "shell/browser/ui/electron_menu_model.h" #include "ui/views/controls/menu/menu_model_adapter.h" namespace electron { class MenuModelAdapter : public views::MenuModelAdapter { public: - explicit MenuModelAdapter(AtomMenuModel* menu_model); + explicit MenuModelAdapter(ElectronMenuModel* menu_model); ~MenuModelAdapter() override; protected: bool GetAccelerator(int id, ui::Accelerator* accelerator) const override; private: - AtomMenuModel* menu_model_; + ElectronMenuModel* menu_model_; DISALLOW_COPY_AND_ASSIGN(MenuModelAdapter); }; diff --git a/shell/browser/ui/views/native_frame_view.cc b/shell/browser/ui/views/native_frame_view.cc index 02f7ba8c2f14e..48fa68f61d8c6 100644 --- a/shell/browser/ui/views/native_frame_view.cc +++ b/shell/browser/ui/views/native_frame_view.cc @@ -8,7 +8,7 @@ namespace electron { -const char NativeFrameView::kViewClassName[] = "AtomNativeFrameView"; +const char NativeFrameView::kViewClassName[] = "ElectronNativeFrameView"; NativeFrameView::NativeFrameView(NativeWindow* window, views::Widget* widget) : views::NativeFrameView(widget), window_(window) {} diff --git a/shell/browser/ui/views/root_view.cc b/shell/browser/ui/views/root_view.cc index 6d4b45c67f5e2..c3250a273501e 100644 --- a/shell/browser/ui/views/root_view.cc +++ b/shell/browser/ui/views/root_view.cc @@ -45,7 +45,7 @@ RootView::RootView(NativeWindow* window) RootView::~RootView() = default; -void RootView::SetMenu(AtomMenuModel* menu_model) { +void RootView::SetMenu(ElectronMenuModel* menu_model) { if (menu_model == nullptr) { // Remove accelerators UnregisterAcceleratorsWithFocusManager(); @@ -201,7 +201,8 @@ bool RootView::AcceleratorPressed(const ui::Accelerator& accelerator) { accelerator); } -void RootView::RegisterAcceleratorsWithFocusManager(AtomMenuModel* menu_model) { +void RootView::RegisterAcceleratorsWithFocusManager( + ElectronMenuModel* menu_model) { if (!menu_model) return; // Clear previous accelerators. diff --git a/shell/browser/ui/views/root_view.h b/shell/browser/ui/views/root_view.h index 5a1c412324503..afd82a070df5d 100644 --- a/shell/browser/ui/views/root_view.h +++ b/shell/browser/ui/views/root_view.h @@ -18,7 +18,7 @@ struct NativeWebKeyboardEvent; namespace electron { -class AtomMenuModel; +class ElectronMenuModel; class MenuBar; class NativeWindow; @@ -27,7 +27,7 @@ class RootView : public views::View { explicit RootView(NativeWindow* window); ~RootView() override; - void SetMenu(AtomMenuModel* menu_model); + void SetMenu(ElectronMenuModel* menu_model); bool HasMenu() const; int GetMenuBarHeight() const; void SetAutoHideMenuBar(bool auto_hide); @@ -38,7 +38,7 @@ class RootView : public views::View { void ResetAltState(); void RestoreFocus(); // Register/Unregister accelerators supported by the menu model. - void RegisterAcceleratorsWithFocusManager(AtomMenuModel* menu_model); + void RegisterAcceleratorsWithFocusManager(ElectronMenuModel* menu_model); void UnregisterAcceleratorsWithFocusManager(); // views::View: diff --git a/shell/browser/ui/views/submenu_button.cc b/shell/browser/ui/views/submenu_button.cc index 2155362ef9d65..c57d6e8034928 100644 --- a/shell/browser/ui/views/submenu_button.cc +++ b/shell/browser/ui/views/submenu_button.cc @@ -9,6 +9,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "ui/accessibility/ax_enums.mojom.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/text_utils.h" @@ -20,11 +21,11 @@ namespace electron { SubmenuButton::SubmenuButton(const base::string16& title, - views::MenuButtonListener* menu_button_listener, + views::ButtonListener* button_listener, const SkColor& background_color) : views::MenuButton( gfx::RemoveAcceleratorChar(title, '&', nullptr, nullptr), - menu_button_listener), + button_listener), background_color_(background_color) { #if defined(OS_LINUX) // Dont' use native style border. diff --git a/shell/browser/ui/views/submenu_button.h b/shell/browser/ui/views/submenu_button.h index 01416ce21220f..6cd83eab5b881 100644 --- a/shell/browser/ui/views/submenu_button.h +++ b/shell/browser/ui/views/submenu_button.h @@ -17,7 +17,7 @@ namespace electron { class SubmenuButton : public views::MenuButton { public: SubmenuButton(const base::string16& title, - views::MenuButtonListener* menu_button_listener, + views::ButtonListener* button_listener, const SkColor& background_color); ~SubmenuButton() override; diff --git a/shell/browser/ui/win/atom_desktop_window_tree_host_win.cc b/shell/browser/ui/win/atom_desktop_window_tree_host_win.cc deleted file mode 100644 index fe698cc69ee4a..0000000000000 --- a/shell/browser/ui/win/atom_desktop_window_tree_host_win.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2015 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "shell/browser/ui/win/atom_desktop_window_tree_host_win.h" - -namespace electron { - -AtomDesktopWindowTreeHostWin::AtomDesktopWindowTreeHostWin( - NativeWindowViews* native_window_view, - views::DesktopNativeWidgetAura* desktop_native_widget_aura) - : views::DesktopWindowTreeHostWin(native_window_view->widget(), - desktop_native_widget_aura), - native_window_view_(native_window_view) {} - -AtomDesktopWindowTreeHostWin::~AtomDesktopWindowTreeHostWin() {} - -bool AtomDesktopWindowTreeHostWin::PreHandleMSG(UINT message, - WPARAM w_param, - LPARAM l_param, - LRESULT* result) { - return native_window_view_->PreHandleMSG(message, w_param, l_param, result); -} - -bool AtomDesktopWindowTreeHostWin::HasNativeFrame() const { - // Since we never use chromium's titlebar implementation, we can just say - // that we use a native titlebar. This will disable the repaint locking when - // DWM composition is disabled. - return true; -} - -} // namespace electron diff --git a/shell/browser/ui/win/dialog_thread.cc b/shell/browser/ui/win/dialog_thread.cc new file mode 100644 index 0000000000000..00ff9ae321c6f --- /dev/null +++ b/shell/browser/ui/win/dialog_thread.cc @@ -0,0 +1,23 @@ +// Copyright (c) 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. + +#include "shell/browser/ui/win/dialog_thread.h" + +#include "base/win/scoped_com_initializer.h" + +namespace dialog_thread { + +// Creates a SingleThreadTaskRunner to run a shell dialog on. Each dialog +// requires its own dedicated single-threaded sequence otherwise in some +// situations where a singleton owns a single instance of this object we can +// have a situation where a modal dialog in one window blocks the appearance +// of a modal dialog in another. +TaskRunner CreateDialogTaskRunner() { + return CreateCOMSTATaskRunner( + {base::ThreadPool(), base::TaskPriority::USER_BLOCKING, + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, base::MayBlock()}, + base::SingleThreadTaskRunnerThreadMode::DEDICATED); +} + +} // namespace dialog_thread diff --git a/shell/browser/ui/win/dialog_thread.h b/shell/browser/ui/win/dialog_thread.h new file mode 100644 index 0000000000000..accb8d91bf3b7 --- /dev/null +++ b/shell/browser/ui/win/dialog_thread.h @@ -0,0 +1,81 @@ +// Copyright (c) 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. + +#ifndef SHELL_BROWSER_UI_WIN_DIALOG_THREAD_H_ +#define SHELL_BROWSER_UI_WIN_DIALOG_THREAD_H_ + +#include +#include + +#include "base/memory/scoped_refptr.h" +#include "base/task/post_task.h" +#include "content/public/browser/browser_thread.h" + +namespace dialog_thread { + +// Returns the dedicated single-threaded sequence that the dialog will be on. +using TaskRunner = scoped_refptr; +TaskRunner CreateDialogTaskRunner(); + +// Runs the |execute| in dialog thread and pass result to |done| in UI thread. +template +void Run(base::OnceCallback execute, base::OnceCallback done) { + // dialogThread.postTask(() => { + // r = execute() + // uiThread.postTask(() => { + // done(r) + // } + // }) + TaskRunner task_runner = CreateDialogTaskRunner(); + task_runner->PostTask( + FROM_HERE, + base::BindOnce( + [](TaskRunner task_runner, base::OnceCallback execute, + base::OnceCallback done) { + R r = std::move(execute).Run(); + base::PostTask( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce( + [](TaskRunner task_runner, base::OnceCallback done, + R r) { + std::move(done).Run(std::move(r)); + // Task runner will destroyed automatically after the + // scope ends. + }, + std::move(task_runner), std::move(done), std::move(r))); + }, + std::move(task_runner), std::move(execute), std::move(done))); +} + +// Adaptor to handle the |execute| that returns bool. +template +void Run(base::OnceCallback execute, + base::OnceCallback done) { + // run(() => { + // result = execute(&value) + // return {result, value} + // }, ({result, value}) => { + // done(result, value) + // }) + struct Result { + bool result; + R value; + }; + Run(base::BindOnce( + [](base::OnceCallback execute) { + Result r; + r.result = std::move(execute).Run(&r.value); + return r; + }, + std::move(execute)), + base::BindOnce( + [](base::OnceCallback done, Result r) { + std::move(done).Run(r.result, std::move(r.value)); + }, + std::move(done))); +} + +} // namespace dialog_thread + +#endif // SHELL_BROWSER_UI_WIN_DIALOG_THREAD_H_ diff --git a/shell/browser/ui/win/atom_desktop_native_widget_aura.cc b/shell/browser/ui/win/electron_desktop_native_widget_aura.cc similarity index 78% rename from shell/browser/ui/win/atom_desktop_native_widget_aura.cc rename to shell/browser/ui/win/electron_desktop_native_widget_aura.cc index ef42304c00d2a..91eb7d71b049b 100644 --- a/shell/browser/ui/win/atom_desktop_native_widget_aura.cc +++ b/shell/browser/ui/win/electron_desktop_native_widget_aura.cc @@ -2,35 +2,35 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/browser/ui/win/atom_desktop_native_widget_aura.h" +#include "shell/browser/ui/win/electron_desktop_native_widget_aura.h" #include -#include "shell/browser/ui/win/atom_desktop_window_tree_host_win.h" +#include "shell/browser/ui/win/electron_desktop_window_tree_host_win.h" #include "ui/views/corewm/tooltip_controller.h" #include "ui/wm/public/tooltip_client.h" namespace electron { -AtomDesktopNativeWidgetAura::AtomDesktopNativeWidgetAura( +ElectronDesktopNativeWidgetAura::ElectronDesktopNativeWidgetAura( NativeWindowViews* native_window_view) : views::DesktopNativeWidgetAura(native_window_view->widget()), native_window_view_(native_window_view) { - GetNativeWindow()->SetName("AtomDesktopNativeWidgetAura"); + GetNativeWindow()->SetName("ElectronDesktopNativeWidgetAura"); // This is to enable the override of OnWindowActivated wm::SetActivationChangeObserver(GetNativeWindow(), this); } -void AtomDesktopNativeWidgetAura::InitNativeWidget( +void ElectronDesktopNativeWidgetAura::InitNativeWidget( views::Widget::InitParams params) { - desktop_window_tree_host_ = new AtomDesktopWindowTreeHostWin( + desktop_window_tree_host_ = new ElectronDesktopWindowTreeHostWin( native_window_view_, static_cast(params.native_widget)); params.desktop_window_tree_host = desktop_window_tree_host_; views::DesktopNativeWidgetAura::InitNativeWidget(std::move(params)); } -void AtomDesktopNativeWidgetAura::Activate() { +void ElectronDesktopNativeWidgetAura::Activate() { // Activate can cause the focused window to be blurred so only // call when the window being activated is visible. This prevents // hidden windows from blurring the focused window when created. @@ -38,7 +38,7 @@ void AtomDesktopNativeWidgetAura::Activate() { views::DesktopNativeWidgetAura::Activate(); } -void AtomDesktopNativeWidgetAura::OnWindowActivated( +void ElectronDesktopNativeWidgetAura::OnWindowActivated( wm::ActivationChangeObserver::ActivationReason reason, aura::Window* gained_active, aura::Window* lost_active) { diff --git a/shell/browser/ui/win/atom_desktop_native_widget_aura.h b/shell/browser/ui/win/electron_desktop_native_widget_aura.h similarity index 66% rename from shell/browser/ui/win/atom_desktop_native_widget_aura.h rename to shell/browser/ui/win/electron_desktop_native_widget_aura.h index 9beb10913c9ac..00ffe0e86a2cd 100644 --- a/shell/browser/ui/win/atom_desktop_native_widget_aura.h +++ b/shell/browser/ui/win/electron_desktop_native_widget_aura.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_UI_WIN_ATOM_DESKTOP_NATIVE_WIDGET_AURA_H_ -#define SHELL_BROWSER_UI_WIN_ATOM_DESKTOP_NATIVE_WIDGET_AURA_H_ +#ifndef SHELL_BROWSER_UI_WIN_ELECTRON_DESKTOP_NATIVE_WIDGET_AURA_H_ +#define SHELL_BROWSER_UI_WIN_ELECTRON_DESKTOP_NATIVE_WIDGET_AURA_H_ #include "shell/browser/native_window_views.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" @@ -14,9 +14,10 @@ class DesktopWindowTreeHost; namespace electron { -class AtomDesktopNativeWidgetAura : public views::DesktopNativeWidgetAura { +class ElectronDesktopNativeWidgetAura : public views::DesktopNativeWidgetAura { public: - explicit AtomDesktopNativeWidgetAura(NativeWindowViews* native_window_view); + explicit ElectronDesktopNativeWidgetAura( + NativeWindowViews* native_window_view); // views::DesktopNativeWidgetAura: void InitNativeWidget(views::Widget::InitParams params) override; @@ -34,9 +35,9 @@ class AtomDesktopNativeWidgetAura : public views::DesktopNativeWidgetAura { // Owned by DesktopNativeWidgetAura. views::DesktopWindowTreeHost* desktop_window_tree_host_; - DISALLOW_COPY_AND_ASSIGN(AtomDesktopNativeWidgetAura); + DISALLOW_COPY_AND_ASSIGN(ElectronDesktopNativeWidgetAura); }; } // namespace electron -#endif // SHELL_BROWSER_UI_WIN_ATOM_DESKTOP_NATIVE_WIDGET_AURA_H_ +#endif // SHELL_BROWSER_UI_WIN_ELECTRON_DESKTOP_NATIVE_WIDGET_AURA_H_ diff --git a/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc new file mode 100644 index 0000000000000..18ad4555c7121 --- /dev/null +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/ui/win/electron_desktop_window_tree_host_win.h" + +#include "ui/base/win/hwnd_metrics.h" + +namespace electron { + +ElectronDesktopWindowTreeHostWin::ElectronDesktopWindowTreeHostWin( + NativeWindowViews* native_window_view, + views::DesktopNativeWidgetAura* desktop_native_widget_aura) + : views::DesktopWindowTreeHostWin(native_window_view->widget(), + desktop_native_widget_aura), + native_window_view_(native_window_view) {} + +ElectronDesktopWindowTreeHostWin::~ElectronDesktopWindowTreeHostWin() {} + +bool ElectronDesktopWindowTreeHostWin::PreHandleMSG(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) { + return native_window_view_->PreHandleMSG(message, w_param, l_param, result); +} + +bool ElectronDesktopWindowTreeHostWin::HasNativeFrame() const { + // Since we never use chromium's titlebar implementation, we can just say + // that we use a native titlebar. This will disable the repaint locking when + // DWM composition is disabled. + return true; +} + +bool ElectronDesktopWindowTreeHostWin::GetClientAreaInsets( + gfx::Insets* insets, + HMONITOR monitor) const { + if (IsMaximized() && !native_window_view_->has_frame()) { + // Windows automatically adds a standard width border to all sides when a + // window is maximized. + int frame_thickness = ui::GetFrameThickness(monitor) - 1; + *insets = gfx::Insets(frame_thickness, frame_thickness, frame_thickness, + frame_thickness); + return true; + } + return false; +} + +} // namespace electron diff --git a/shell/browser/ui/win/atom_desktop_window_tree_host_win.h b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h similarity index 56% rename from shell/browser/ui/win/atom_desktop_window_tree_host_win.h rename to shell/browser/ui/win/electron_desktop_window_tree_host_win.h index 01f60dabe780a..3c8f9692b50e0 100644 --- a/shell/browser/ui/win/atom_desktop_window_tree_host_win.h +++ b/shell/browser/ui/win/electron_desktop_window_tree_host_win.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_BROWSER_UI_WIN_ATOM_DESKTOP_WINDOW_TREE_HOST_WIN_H_ -#define SHELL_BROWSER_UI_WIN_ATOM_DESKTOP_WINDOW_TREE_HOST_WIN_H_ +#ifndef SHELL_BROWSER_UI_WIN_ELECTRON_DESKTOP_WINDOW_TREE_HOST_WIN_H_ +#define SHELL_BROWSER_UI_WIN_ELECTRON_DESKTOP_WINDOW_TREE_HOST_WIN_H_ #include @@ -12,12 +12,13 @@ namespace electron { -class AtomDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin { +class ElectronDesktopWindowTreeHostWin + : public views::DesktopWindowTreeHostWin { public: - AtomDesktopWindowTreeHostWin( + ElectronDesktopWindowTreeHostWin( NativeWindowViews* native_window_view, views::DesktopNativeWidgetAura* desktop_native_widget_aura); - ~AtomDesktopWindowTreeHostWin() override; + ~ElectronDesktopWindowTreeHostWin() override; protected: bool PreHandleMSG(UINT message, @@ -25,13 +26,15 @@ class AtomDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin { LPARAM l_param, LRESULT* result) override; bool HasNativeFrame() const override; + bool GetClientAreaInsets(gfx::Insets* insets, + HMONITOR monitor) const override; private: NativeWindowViews* native_window_view_; // weak ref - DISALLOW_COPY_AND_ASSIGN(AtomDesktopWindowTreeHostWin); + DISALLOW_COPY_AND_ASSIGN(ElectronDesktopWindowTreeHostWin); }; } // namespace electron -#endif // SHELL_BROWSER_UI_WIN_ATOM_DESKTOP_WINDOW_TREE_HOST_WIN_H_ +#endif // SHELL_BROWSER_UI_WIN_ELECTRON_DESKTOP_WINDOW_TREE_HOST_WIN_H_ diff --git a/shell/browser/ui/win/notify_icon.cc b/shell/browser/ui/win/notify_icon.cc index 2567f8b91fec3..cfd94b3ed693a 100644 --- a/shell/browser/ui/win/notify_icon.cc +++ b/shell/browser/ui/win/notify_icon.cc @@ -188,7 +188,7 @@ void NotifyIcon::Focus() { } void NotifyIcon::PopUpContextMenu(const gfx::Point& pos, - AtomMenuModel* menu_model) { + ElectronMenuModel* menu_model) { // Returns if context menu isn't set. if (menu_model == nullptr && menu_model_ == nullptr) return; @@ -231,7 +231,7 @@ void NotifyIcon::PopUpContextMenu(const gfx::Point& pos, ui::MENU_SOURCE_MOUSE); } -void NotifyIcon::SetContextMenu(AtomMenuModel* menu_model) { +void NotifyIcon::SetContextMenu(ElectronMenuModel* menu_model) { menu_model_ = menu_model; } diff --git a/shell/browser/ui/win/notify_icon.h b/shell/browser/ui/win/notify_icon.h index 9dc29beda4df7..6384716530028 100644 --- a/shell/browser/ui/win/notify_icon.h +++ b/shell/browser/ui/win/notify_icon.h @@ -62,8 +62,8 @@ class NotifyIcon : public TrayIcon { void RemoveBalloon() override; void Focus() override; void PopUpContextMenu(const gfx::Point& pos, - AtomMenuModel* menu_model) override; - void SetContextMenu(AtomMenuModel* menu_model) override; + ElectronMenuModel* menu_model) override; + void SetContextMenu(ElectronMenuModel* menu_model) override; gfx::Rect GetBounds() override; private: @@ -86,7 +86,7 @@ class NotifyIcon : public TrayIcon { base::win::ScopedHICON icon_; // The context menu. - AtomMenuModel* menu_model_ = nullptr; + ElectronMenuModel* menu_model_ = nullptr; // Context menu associated with this icon (if any). std::unique_ptr menu_runner_; diff --git a/shell/browser/web_contents_permission_helper.cc b/shell/browser/web_contents_permission_helper.cc index 887a9c175a201..0b0c16a13974a 100644 --- a/shell/browser/web_contents_permission_helper.cc +++ b/shell/browser/web_contents_permission_helper.cc @@ -10,7 +10,7 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/render_process_host.h" -#include "shell/browser/atom_permission_manager.h" +#include "shell/browser/electron_permission_manager.h" #include "shell/browser/media/media_stream_devices_controller.h" #include "shell/common/native_mate_converters/gurl_converter.h" @@ -70,7 +70,7 @@ void WebContentsPermissionHelper::RequestPermission( bool user_gesture, const base::DictionaryValue* details) { auto* rfh = web_contents_->GetMainFrame(); - auto* permission_manager = static_cast( + auto* permission_manager = static_cast( web_contents_->GetBrowserContext()->GetPermissionControllerDelegate()); auto origin = web_contents_->GetLastCommittedURL(); permission_manager->RequestPermissionWithDetails( @@ -82,7 +82,7 @@ bool WebContentsPermissionHelper::CheckPermission( content::PermissionType permission, const base::DictionaryValue* details) const { auto* rfh = web_contents_->GetMainFrame(); - auto* permission_manager = static_cast( + auto* permission_manager = static_cast( web_contents_->GetBrowserContext()->GetPermissionControllerDelegate()); auto origin = web_contents_->GetLastCommittedURL(); return permission_manager->CheckPermissionWithDetails(permission, rfh, origin, diff --git a/shell/browser/web_contents_preferences.cc b/shell/browser/web_contents_preferences.cc index e247641773325..df91afd4cd707 100644 --- a/shell/browser/web_contents_preferences.cc +++ b/shell/browser/web_contents_preferences.cc @@ -130,6 +130,7 @@ WebContentsPreferences::WebContentsPreferences( SetDefaultBoolIfUndefined(options::kImages, true); SetDefaultBoolIfUndefined(options::kTextAreasAreResizable, true); SetDefaultBoolIfUndefined(options::kWebGL, true); + SetDefaultBoolIfUndefined(options::kEnableWebSQL, true); bool webSecurity = true; SetDefaultBoolIfUndefined(options::kWebSecurity, webSecurity); // If webSecurity was explicity set to false, let's inherit that into @@ -144,6 +145,9 @@ WebContentsPreferences::WebContentsPreferences( SetDefaultBoolIfUndefined(options::kScrollBounce, false); #endif SetDefaultBoolIfUndefined(options::kOffscreen, false); +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + SetDefaultBoolIfUndefined(options::kSpellcheck, false); +#endif // If this is a tag, and the embedder is offscreen-rendered, then // this WebContents is also offscreen-rendered. @@ -414,6 +418,16 @@ void WebContentsPreferences::AppendCommandLineSwitches( if (IsEnabled(options::kNodeIntegrationInSubFrames)) command_line->AppendSwitch(switches::kNodeIntegrationInSubFrames); +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + if (IsEnabled(options::kSpellcheck)) { + command_line->AppendSwitch(switches::kEnableSpellcheck); + } +#endif + + // Whether to allow the WebSQL api + if (IsEnabled(options::kEnableWebSQL)) + command_line->AppendSwitch(switches::kEnableWebSQL); + // We are appending args to a webContents so let's save the current state // of our preferences object so that during the lifetime of the WebContents // we can fetch the options used to initally configure the WebContents diff --git a/shell/browser/web_contents_preferences.h b/shell/browser/web_contents_preferences.h index 993c2f420e132..743ba20c114f8 100644 --- a/shell/browser/web_contents_preferences.h +++ b/shell/browser/web_contents_preferences.h @@ -67,7 +67,7 @@ class WebContentsPreferences private: friend class content::WebContentsUserData; - friend class AtomBrowserClient; + friend class ElectronBrowserClient; // Get WebContents according to process ID. static content::WebContents* GetWebContentsFromProcessID(int process_id); diff --git a/shell/browser/web_dialog_helper.cc b/shell/browser/web_dialog_helper.cc index 8129016b89ffe..db476dd80bcce 100644 --- a/shell/browser/web_dialog_helper.cc +++ b/shell/browser/web_dialog_helper.cc @@ -25,7 +25,7 @@ #include "gin/dictionary.h" #include "net/base/directory_lister.h" #include "net/base/mime_util.h" -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/electron_browser_context.h" #include "shell/browser/native_window.h" #include "shell/browser/ui/file_dialog.h" #include "shell/common/gin_converters/callback_converter.h" @@ -149,8 +149,9 @@ class FileSelectHelper : public base::RefCounted, } if (render_frame_host_ && !paths.empty()) { - auto* browser_context = static_cast( - render_frame_host_->GetProcess()->GetBrowserContext()); + auto* browser_context = + static_cast( + render_frame_host_->GetProcess()->GetBrowserContext()); browser_context->prefs()->SetFilePath(prefs::kSelectFileLastDirectory, paths[0].DirName()); } @@ -330,7 +331,7 @@ void WebDialogHelper::RunFileChooser( NOTREACHED(); } - auto* browser_context = static_cast( + auto* browser_context = static_cast( render_frame_host->GetProcess()->GetBrowserContext()); settings.default_path = browser_context->prefs() ->GetFilePath(prefs::kSelectFileLastDirectory) diff --git a/shell/browser/web_view_guest_delegate.cc b/shell/browser/web_view_guest_delegate.cc index bd92bbc830cd1..3ce930206d91f 100644 --- a/shell/browser/web_view_guest_delegate.cc +++ b/shell/browser/web_view_guest_delegate.cc @@ -13,7 +13,7 @@ #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host_view.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/common/native_mate_converters/gurl_converter.h" #include "third_party/blink/public/common/page/page_zoom.h" @@ -45,7 +45,7 @@ void WebViewGuestDelegate::AttachToIframe( // frame |embedder_frame| hosts the inner WebContents. embedder_web_contents_->AttachInnerWebContents( base::WrapUnique(guest_web_contents), - embedder_frame); + embedder_frame, false); ResetZoomController(); @@ -113,8 +113,7 @@ content::WebContents* WebViewGuestDelegate::CreateNewGuestWindow( guest_contents->GetRenderViewHost()->GetWidget(); auto* guest_contents_impl = static_cast(guest_contents.release()); - guest_contents_impl->GetView()->CreateViewForWidget(render_widget_host, - false); + guest_contents_impl->GetView()->CreateViewForWidget(render_widget_host); return guest_contents_impl; } diff --git a/shell/browser/web_view_manager.cc b/shell/browser/web_view_manager.cc index e48ad9cb11f0b..64ecb9223338b 100644 --- a/shell/browser/web_view_manager.cc +++ b/shell/browser/web_view_manager.cc @@ -7,7 +7,7 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" -#include "shell/browser/atom_browser_context.h" +#include "shell/browser/electron_browser_context.h" namespace electron { @@ -62,10 +62,14 @@ content::WebContents* WebViewManager::GetGuestByInstanceID( bool WebViewManager::ForEachGuest(content::WebContents* embedder_web_contents, const GuestCallback& callback) { - for (auto& item : web_contents_embedder_map_) - if (item.second.embedder == embedder_web_contents && - callback.Run(item.second.web_contents)) + for (auto& item : web_contents_embedder_map_) { + if (item.second.embedder != embedder_web_contents) + continue; + + auto* guest_web_contents = item.second.web_contents; + if (guest_web_contents && callback.Run(guest_web_contents)) return true; + } return false; } diff --git a/shell/browser/window_list.cc b/shell/browser/window_list.cc index 536dfe513c2dd..95a5ddeff7bda 100644 --- a/shell/browser/window_list.cc +++ b/shell/browser/window_list.cc @@ -10,6 +10,18 @@ #include "shell/browser/native_window.h" #include "shell/browser/window_list_observer.h" +namespace { +template +std::vector> ConvertToWeakPtrVector(std::vector raw_ptrs) { + std::vector> converted_to_weak; + converted_to_weak.reserve(raw_ptrs.size()); + for (auto* raw_ptr : raw_ptrs) { + converted_to_weak.push_back(raw_ptr->GetWeakPtr()); + } + return converted_to_weak; +} +} // namespace + namespace electron { // static @@ -80,20 +92,26 @@ void WindowList::RemoveObserver(WindowListObserver* observer) { // static void WindowList::CloseAllWindows() { - WindowVector windows = GetInstance()->windows_; + std::vector> weak_windows = + ConvertToWeakPtrVector(GetInstance()->windows_); #if defined(OS_MACOSX) - std::reverse(windows.begin(), windows.end()); + std::reverse(weak_windows.begin(), weak_windows.end()); #endif - for (auto* const& window : windows) - if (!window->IsClosed()) + for (const auto& window : weak_windows) { + if (window && !window->IsClosed()) window->Close(); + } } // static void WindowList::DestroyAllWindows() { - WindowVector windows = GetInstance()->windows_; - for (auto* const& window : windows) - window->CloseImmediately(); // e.g. Destroy() + std::vector> weak_windows = + ConvertToWeakPtrVector(GetInstance()->windows_); + + for (const auto& window : weak_windows) { + if (window) + window->CloseImmediately(); + } } WindowList::WindowList() = default; diff --git a/shell/common/api/api.mojom b/shell/common/api/api.mojom index 06d4e50c844c0..d02f4132dd501 100644 --- a/shell/common/api/api.mojom +++ b/shell/common/api/api.mojom @@ -55,10 +55,7 @@ interface ElectronBrowser { // Emits an event on |channel| from the ipcMain JavaScript object in the main // process, and waits synchronously for a response. - // - // NB. this is not marked [Sync] because mojo synchronous methods can be - // reordered with respect to asynchronous methods on the same channel. - // Instead, callers can manually block on the response to this method. + [Sync] MessageSync( bool internal, string channel, @@ -90,8 +87,6 @@ interface ElectronBrowser { SetTemporaryZoomLevel(double zoom_level); - SetZoomLimits(double min_zoom, double max_zoom); - [Sync] DoGetZoomLevel() => (double result); }; diff --git a/shell/common/api/atom_api_asar.cc b/shell/common/api/electron_api_asar.cc similarity index 100% rename from shell/common/api/atom_api_asar.cc rename to shell/common/api/electron_api_asar.cc diff --git a/shell/common/api/atom_api_clipboard.cc b/shell/common/api/electron_api_clipboard.cc similarity index 96% rename from shell/common/api/atom_api_clipboard.cc rename to shell/common/api/electron_api_clipboard.cc index 45b04fb283588..a8186801a2c19 100644 --- a/shell/common/api/atom_api_clipboard.cc +++ b/shell/common/api/electron_api_clipboard.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/common/api/atom_api_clipboard.h" +#include "shell/common/api/electron_api_clipboard.h" #include "base/strings/utf_string_conversions.h" #include "shell/common/native_mate_converters/image_converter.h" @@ -68,9 +68,12 @@ void Clipboard::WriteBuffer(const std::string& format, } ui::ScopedClipboardWriter writer(GetClipboardBuffer(args)); + base::span payload_span( + reinterpret_cast(node::Buffer::Data(buffer)), + node::Buffer::Length(buffer)); writer.WriteData( - ui::ClipboardFormatType::GetType(format).Serialize(), - std::string(node::Buffer::Data(buffer), node::Buffer::Length(buffer))); + base::UTF8ToUTF16(ui::ClipboardFormatType::GetType(format).Serialize()), + mojo_base::BigBuffer(payload_span)); } void Clipboard::Write(const mate::Dictionary& data, mate::Arguments* args) { diff --git a/shell/common/api/atom_api_clipboard.h b/shell/common/api/electron_api_clipboard.h similarity index 92% rename from shell/common/api/atom_api_clipboard.h rename to shell/common/api/electron_api_clipboard.h index 9c7fd008a00da..0df09d8e9c40c 100644 --- a/shell/common/api/atom_api_clipboard.h +++ b/shell/common/api/electron_api_clipboard.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_COMMON_API_ATOM_API_CLIPBOARD_H_ -#define SHELL_COMMON_API_ATOM_API_CLIPBOARD_H_ +#ifndef SHELL_COMMON_API_ELECTRON_API_CLIPBOARD_H_ +#define SHELL_COMMON_API_ELECTRON_API_CLIPBOARD_H_ #include #include @@ -61,4 +61,4 @@ class Clipboard { } // namespace electron -#endif // SHELL_COMMON_API_ATOM_API_CLIPBOARD_H_ +#endif // SHELL_COMMON_API_ELECTRON_API_CLIPBOARD_H_ diff --git a/shell/common/api/atom_api_clipboard_mac.mm b/shell/common/api/electron_api_clipboard_mac.mm similarity index 91% rename from shell/common/api/atom_api_clipboard_mac.mm rename to shell/common/api/electron_api_clipboard_mac.mm index 9e1e957cb9124..aa47bab85dd36 100644 --- a/shell/common/api/atom_api_clipboard_mac.mm +++ b/shell/common/api/electron_api_clipboard_mac.mm @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "base/strings/sys_string_conversions.h" -#include "shell/common/api/atom_api_clipboard.h" +#include "shell/common/api/electron_api_clipboard.h" #include "ui/base/cocoa/find_pasteboard.h" namespace electron { diff --git a/shell/common/api/atom_api_command_line.cc b/shell/common/api/electron_api_command_line.cc similarity index 100% rename from shell/common/api/atom_api_command_line.cc rename to shell/common/api/electron_api_command_line.cc diff --git a/shell/common/api/atom_api_crash_reporter.cc b/shell/common/api/electron_api_crash_reporter.cc similarity index 100% rename from shell/common/api/atom_api_crash_reporter.cc rename to shell/common/api/electron_api_crash_reporter.cc diff --git a/shell/common/api/atom_api_key_weak_map.h b/shell/common/api/electron_api_key_weak_map.h similarity index 91% rename from shell/common/api/atom_api_key_weak_map.h rename to shell/common/api/electron_api_key_weak_map.h index 159d0596a0539..2101267081876 100644 --- a/shell/common/api/atom_api_key_weak_map.h +++ b/shell/common/api/electron_api_key_weak_map.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_COMMON_API_ATOM_API_KEY_WEAK_MAP_H_ -#define SHELL_COMMON_API_ATOM_API_KEY_WEAK_MAP_H_ +#ifndef SHELL_COMMON_API_ELECTRON_API_KEY_WEAK_MAP_H_ +#define SHELL_COMMON_API_ELECTRON_API_KEY_WEAK_MAP_H_ #include "native_mate/handle.h" #include "native_mate/wrappable.h" @@ -61,4 +61,4 @@ class KeyWeakMap : public mate::Wrappable> { } // namespace electron -#endif // SHELL_COMMON_API_ATOM_API_KEY_WEAK_MAP_H_ +#endif // SHELL_COMMON_API_ELECTRON_API_KEY_WEAK_MAP_H_ diff --git a/shell/common/api/atom_api_native_image.cc b/shell/common/api/electron_api_native_image.cc similarity index 87% rename from shell/common/api/atom_api_native_image.cc rename to shell/common/api/electron_api_native_image.cc index 2f7c3be86573d..d563b7c085f68 100644 --- a/shell/common/api/atom_api_native_image.cc +++ b/shell/common/api/electron_api_native_image.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/common/api/atom_api_native_image.h" +#include "shell/common/api/electron_api_native_image.h" #include #include @@ -259,7 +259,7 @@ float NativeImage::GetAspectRatio() { return static_cast(size.width()) / static_cast(size.height()); } -mate::Handle NativeImage::Resize( +gin::Handle NativeImage::Resize( v8::Isolate* isolate, const base::DictionaryValue& options) { gfx::Size size = GetSize(); @@ -290,16 +290,16 @@ mate::Handle NativeImage::Resize( gfx::ImageSkia resized = gfx::ImageSkiaOperations::CreateResizedImage( image_.AsImageSkia(), method, size); - return mate::CreateHandle(isolate, - new NativeImage(isolate, gfx::Image(resized))); + return gin::CreateHandle(isolate, + new NativeImage(isolate, gfx::Image(resized))); } -mate::Handle NativeImage::Crop(v8::Isolate* isolate, - const gfx::Rect& rect) { +gin::Handle NativeImage::Crop(v8::Isolate* isolate, + const gfx::Rect& rect) { gfx::ImageSkia cropped = gfx::ImageSkiaOperations::ExtractSubset(image_.AsImageSkia(), rect); - return mate::CreateHandle(isolate, - new NativeImage(isolate, gfx::Image(cropped))); + return gin::CreateHandle(isolate, + new NativeImage(isolate, gfx::Image(cropped))); } void NativeImage::AddRepresentation(const gin_helper::Dictionary& options) { @@ -350,48 +350,48 @@ bool NativeImage::IsTemplateImage() { #endif // static -mate::Handle NativeImage::CreateEmpty(v8::Isolate* isolate) { - return mate::CreateHandle(isolate, new NativeImage(isolate, gfx::Image())); +gin::Handle NativeImage::CreateEmpty(v8::Isolate* isolate) { + return gin::CreateHandle(isolate, new NativeImage(isolate, gfx::Image())); } // static -mate::Handle NativeImage::Create(v8::Isolate* isolate, - const gfx::Image& image) { - return mate::CreateHandle(isolate, new NativeImage(isolate, image)); +gin::Handle NativeImage::Create(v8::Isolate* isolate, + const gfx::Image& image) { + return gin::CreateHandle(isolate, new NativeImage(isolate, image)); } // static -mate::Handle NativeImage::CreateFromPNG(v8::Isolate* isolate, - const char* buffer, - size_t length) { +gin::Handle NativeImage::CreateFromPNG(v8::Isolate* isolate, + const char* buffer, + size_t length) { gfx::Image image = gfx::Image::CreateFrom1xPNGBytes( reinterpret_cast(buffer), length); return Create(isolate, image); } // static -mate::Handle NativeImage::CreateFromJPEG(v8::Isolate* isolate, - const char* buffer, - size_t length) { +gin::Handle NativeImage::CreateFromJPEG(v8::Isolate* isolate, + const char* buffer, + size_t length) { gfx::Image image = gfx::ImageFrom1xJPEGEncodedData( reinterpret_cast(buffer), length); return Create(isolate, image); } // static -mate::Handle NativeImage::CreateFromPath( +gin::Handle NativeImage::CreateFromPath( v8::Isolate* isolate, const base::FilePath& path) { base::FilePath image_path = NormalizePath(path); #if defined(OS_WIN) if (image_path.MatchesExtension(FILE_PATH_LITERAL(".ico"))) { - return mate::CreateHandle(isolate, new NativeImage(isolate, image_path)); + return gin::CreateHandle(isolate, new NativeImage(isolate, image_path)); } #endif gfx::ImageSkia image_skia; electron::util::PopulateImageSkiaRepsFromPath(&image_skia, image_path); gfx::Image image(image_skia); - mate::Handle handle = Create(isolate, image); + gin::Handle handle = Create(isolate, image); #if defined(OS_MACOSX) if (IsTemplateFilename(image_path)) handle->SetTemplateImage(true); @@ -400,13 +400,13 @@ mate::Handle NativeImage::CreateFromPath( } // static -mate::Handle NativeImage::CreateFromBitmap( +gin::Handle NativeImage::CreateFromBitmap( gin_helper::ErrorThrower thrower, v8::Local buffer, const gin_helper::Dictionary& options) { if (!node::Buffer::HasInstance(buffer)) { thrower.ThrowError("buffer must be a node Buffer"); - return mate::Handle(); + return gin::Handle(); } unsigned int width = 0; @@ -415,12 +415,12 @@ mate::Handle NativeImage::CreateFromBitmap( if (!options.Get("width", &width)) { thrower.ThrowError("width is required"); - return mate::Handle(); + return gin::Handle(); } if (!options.Get("height", &height)) { thrower.ThrowError("height is required"); - return mate::Handle(); + return gin::Handle(); } auto info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType); @@ -428,7 +428,7 @@ mate::Handle NativeImage::CreateFromBitmap( if (size_bytes != node::Buffer::Length(buffer)) { thrower.ThrowError("invalid buffer size"); - return mate::Handle(); + return gin::Handle(); } options.Get("scaleFactor", &scale_factor); @@ -448,13 +448,13 @@ mate::Handle NativeImage::CreateFromBitmap( } // static -mate::Handle NativeImage::CreateFromBuffer( +gin::Handle NativeImage::CreateFromBuffer( gin_helper::ErrorThrower thrower, v8::Local buffer, gin::Arguments* args) { if (!node::Buffer::HasInstance(buffer)) { thrower.ThrowError("buffer must be a node Buffer"); - return mate::Handle(); + return gin::Handle(); } int width = 0; @@ -476,8 +476,8 @@ mate::Handle NativeImage::CreateFromBuffer( } // static -mate::Handle NativeImage::CreateFromDataURL(v8::Isolate* isolate, - const GURL& url) { +gin::Handle NativeImage::CreateFromDataURL(v8::Isolate* isolate, + const GURL& url) { std::string mime_type, charset, data; if (net::DataURL::Parse(url, &mime_type, &charset, &data)) { if (mime_type == "image/png") @@ -490,7 +490,7 @@ mate::Handle NativeImage::CreateFromDataURL(v8::Isolate* isolate, } #if !defined(OS_MACOSX) -mate::Handle NativeImage::CreateFromNamedImage( +gin::Handle NativeImage::CreateFromNamedImage( gin::Arguments* args, const std::string& name) { return CreateEmpty(args->isolate()); @@ -526,32 +526,32 @@ void NativeImage::BuildPrototype(v8::Isolate* isolate, namespace gin { -v8::Local Converter>::ToV8( +v8::Local Converter::ToV8( v8::Isolate* isolate, - const mate::Handle& val) { - return val.ToV8(); + electron::api::NativeImage* val) { + if (val) + return val->GetWrapper(); + else + return v8::Null(isolate); } -bool Converter>::FromV8( +bool Converter::FromV8( v8::Isolate* isolate, v8::Local val, - mate::Handle* out) { + electron::api::NativeImage** out) { // Try converting from file path. base::FilePath path; if (ConvertFromV8(isolate, val, &path)) { - *out = electron::api::NativeImage::CreateFromPath(isolate, path); + *out = electron::api::NativeImage::CreateFromPath(isolate, path).get(); // Should throw when failed to initialize from path. return !(*out)->image().IsEmpty(); } - auto* wrapper = static_cast( - mate::internal::FromV8Impl(isolate, val)); - if (!wrapper) - return false; + *out = static_cast( + static_cast( + mate::internal::FromV8Impl(isolate, val))); - *out = mate::CreateHandle(isolate, - static_cast(wrapper)); - return true; + return *out != nullptr; } } // namespace gin diff --git a/shell/common/api/atom_api_native_image.h b/shell/common/api/electron_api_native_image.h similarity index 61% rename from shell/common/api/atom_api_native_image.h rename to shell/common/api/electron_api_native_image.h index dc2c8b1914f39..00c3f63b6e384 100644 --- a/shell/common/api/atom_api_native_image.h +++ b/shell/common/api/electron_api_native_image.h @@ -2,13 +2,14 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_COMMON_API_ATOM_API_NATIVE_IMAGE_H_ -#define SHELL_COMMON_API_ATOM_API_NATIVE_IMAGE_H_ +#ifndef SHELL_COMMON_API_ELECTRON_API_NATIVE_IMAGE_H_ +#define SHELL_COMMON_API_ELECTRON_API_NATIVE_IMAGE_H_ #include #include #include "base/values.h" +#include "gin/handle.h" #include "native_mate/handle.h" #include "native_mate/wrappable.h" #include "shell/common/gin_helper/error_thrower.h" @@ -40,30 +41,29 @@ namespace api { class NativeImage : public mate::Wrappable { public: - static mate::Handle CreateEmpty(v8::Isolate* isolate); - static mate::Handle Create(v8::Isolate* isolate, - const gfx::Image& image); - static mate::Handle CreateFromPNG(v8::Isolate* isolate, + static gin::Handle CreateEmpty(v8::Isolate* isolate); + static gin::Handle Create(v8::Isolate* isolate, + const gfx::Image& image); + static gin::Handle CreateFromPNG(v8::Isolate* isolate, + const char* buffer, + size_t length); + static gin::Handle CreateFromJPEG(v8::Isolate* isolate, const char* buffer, size_t length); - static mate::Handle CreateFromJPEG(v8::Isolate* isolate, - const char* buffer, - size_t length); - static mate::Handle CreateFromPath(v8::Isolate* isolate, - const base::FilePath& path); - static mate::Handle CreateFromBitmap( + static gin::Handle CreateFromPath(v8::Isolate* isolate, + const base::FilePath& path); + static gin::Handle CreateFromBitmap( gin_helper::ErrorThrower thrower, v8::Local buffer, const gin_helper::Dictionary& options); - static mate::Handle CreateFromBuffer( + static gin::Handle CreateFromBuffer( gin_helper::ErrorThrower thrower, v8::Local buffer, gin::Arguments* args); - static mate::Handle CreateFromDataURL(v8::Isolate* isolate, - const GURL& url); - static mate::Handle CreateFromNamedImage( - gin::Arguments* args, - const std::string& name); + static gin::Handle CreateFromDataURL(v8::Isolate* isolate, + const GURL& url); + static gin::Handle CreateFromNamedImage(gin::Arguments* args, + const std::string& name); static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); @@ -87,9 +87,9 @@ class NativeImage : public mate::Wrappable { v8::Local ToBitmap(gin::Arguments* args); v8::Local GetBitmap(gin::Arguments* args); v8::Local GetNativeHandle(gin_helper::ErrorThrower thrower); - mate::Handle Resize(v8::Isolate* isolate, - const base::DictionaryValue& options); - mate::Handle Crop(v8::Isolate* isolate, const gfx::Rect& rect); + gin::Handle Resize(v8::Isolate* isolate, + const base::DictionaryValue& options); + gin::Handle Crop(v8::Isolate* isolate, const gfx::Rect& rect); std::string ToDataURL(gin::Arguments* args); bool IsEmpty(); gfx::Size GetSize(); @@ -119,13 +119,12 @@ namespace gin { // A custom converter that allows converting path to NativeImage. template <> -struct Converter> { - static v8::Local ToV8( - v8::Isolate* isolate, - const mate::Handle& val); +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + electron::api::NativeImage* val); static bool FromV8(v8::Isolate* isolate, v8::Local val, - mate::Handle* out); + electron::api::NativeImage** out); }; } // namespace gin @@ -136,19 +135,18 @@ namespace mate { // // TODO(zcbenz): Remove this after removing native_mate. template <> -struct Converter> { - static v8::Local ToV8( - v8::Isolate* isolate, - const mate::Handle& val) { +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + electron::api::NativeImage* val) { return gin::ConvertToV8(isolate, val); } static bool FromV8(v8::Isolate* isolate, v8::Local val, - mate::Handle* out) { + electron::api::NativeImage** out) { return gin::ConvertFromV8(isolate, val, out); } }; } // namespace mate -#endif // SHELL_COMMON_API_ATOM_API_NATIVE_IMAGE_H_ +#endif // SHELL_COMMON_API_ELECTRON_API_NATIVE_IMAGE_H_ diff --git a/shell/common/api/atom_api_native_image_mac.mm b/shell/common/api/electron_api_native_image_mac.mm similarity index 94% rename from shell/common/api/atom_api_native_image_mac.mm rename to shell/common/api/electron_api_native_image_mac.mm index 5396cbf9cffc3..8d6cab665ec25 100644 --- a/shell/common/api/atom_api_native_image_mac.mm +++ b/shell/common/api/electron_api_native_image_mac.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/common/api/atom_api_native_image.h" +#include "shell/common/api/electron_api_native_image.h" #include #include @@ -33,7 +33,7 @@ return def; } -mate::Handle NativeImage::CreateFromNamedImage( +gin::Handle NativeImage::CreateFromNamedImage( gin::Arguments* args, const std::string& name) { @autoreleasepool { diff --git a/shell/common/api/atom_api_shell.cc b/shell/common/api/electron_api_shell.cc similarity index 100% rename from shell/common/api/atom_api_shell.cc rename to shell/common/api/electron_api_shell.cc diff --git a/shell/common/api/atom_api_v8_util.cc b/shell/common/api/electron_api_v8_util.cc similarity index 84% rename from shell/common/api/atom_api_v8_util.cc rename to shell/common/api/electron_api_v8_util.cc index 67e4ad73be0f5..8ee801453bbc3 100644 --- a/shell/common/api/atom_api_v8_util.cc +++ b/shell/common/api/electron_api_v8_util.cc @@ -14,7 +14,7 @@ #include "v8/include/v8-profiler.h" #if BUILDFLAG(ENABLE_REMOTE_MODULE) -#include "shell/common/api/atom_api_key_weak_map.h" +#include "shell/common/api/electron_api_key_weak_map.h" #include "shell/common/api/remote/remote_callback_freer.h" #include "shell/common/api/remote/remote_object_freer.h" #endif @@ -114,6 +114,29 @@ bool IsSameOrigin(const GURL& l, const GURL& r) { return url::Origin::Create(l).IsSameOriginWith(url::Origin::Create(r)); } +#ifdef DCHECK_IS_ON +std::vector> weakly_tracked_values; + +void WeaklyTrackValue(v8::Isolate* isolate, v8::Local value) { + v8::Global global_value(isolate, value); + global_value.SetWeak(); + weakly_tracked_values.push_back(std::move(global_value)); +} + +void ClearWeaklyTrackedValues() { + weakly_tracked_values.clear(); +} + +std::vector> GetWeaklyTrackedValues(v8::Isolate* isolate) { + std::vector> locals; + for (size_t i = 0; i < weakly_tracked_values.size(); i++) { + if (!weakly_tracked_values[i].IsEmpty()) + locals.push_back(weakly_tracked_values[i].Get(isolate)); + } + return locals; +} +#endif + void Initialize(v8::Local exports, v8::Local unused, v8::Local context, @@ -141,6 +164,11 @@ void Initialize(v8::Local exports, dict.SetMethod("requestGarbageCollectionForTesting", &RequestGarbageCollectionForTesting); dict.SetMethod("isSameOrigin", &IsSameOrigin); +#ifdef DCHECK_IS_ON + dict.SetMethod("getWeaklyTrackedValues", &GetWeaklyTrackedValues); + dict.SetMethod("clearWeaklyTrackedValues", &ClearWeaklyTrackedValues); + dict.SetMethod("weaklyTrackValue", &WeaklyTrackValue); +#endif } } // namespace diff --git a/shell/common/api/electron_bindings.h b/shell/common/api/electron_bindings.h index 98e0d2f18273c..32bdf6d7e398b 100644 --- a/shell/common/api/electron_bindings.h +++ b/shell/common/api/electron_bindings.h @@ -15,7 +15,7 @@ #include "base/strings/string16.h" #include "native_mate/arguments.h" #include "shell/common/promise_util.h" -#include "uv.h" // NOLINT(build/include) +#include "uv.h" // NOLINT(build/include_directory) #include "v8/include/v8.h" namespace mate { diff --git a/shell/common/api/remote/remote_callback_freer.cc b/shell/common/api/remote/remote_callback_freer.cc index 2b977e399b36f..40dd4a706d6cb 100644 --- a/shell/common/api/remote/remote_callback_freer.cc +++ b/shell/common/api/remote/remote_callback_freer.cc @@ -9,6 +9,7 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "electron/shell/common/api/api.mojom.h" +#include "mojo/public/cpp/bindings/associated_remote.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" namespace electron { @@ -16,31 +17,38 @@ namespace electron { // static void RemoteCallbackFreer::BindTo(v8::Isolate* isolate, v8::Local target, + int frame_id, const std::string& context_id, int object_id, content::WebContents* web_contents) { - new RemoteCallbackFreer(isolate, target, context_id, object_id, web_contents); + new RemoteCallbackFreer(isolate, target, frame_id, context_id, object_id, + web_contents); } RemoteCallbackFreer::RemoteCallbackFreer(v8::Isolate* isolate, v8::Local target, + int frame_id, const std::string& context_id, int object_id, content::WebContents* web_contents) : ObjectLifeMonitor(isolate, target), content::WebContentsObserver(web_contents), + frame_id_(frame_id), context_id_(context_id), object_id_(object_id) {} RemoteCallbackFreer::~RemoteCallbackFreer() = default; void RemoteCallbackFreer::RunDestructor() { - auto* frame_host = web_contents()->GetMainFrame(); - if (frame_host) { - mojom::ElectronRendererAssociatedPtr electron_ptr; - frame_host->GetRemoteAssociatedInterfaces()->GetInterface( - mojo::MakeRequest(&electron_ptr)); - electron_ptr->DereferenceRemoteJSCallback(context_id_, object_id_); + auto frames = web_contents()->GetAllFrames(); + auto iter = std::find_if(frames.begin(), frames.end(), [this](auto* f) { + return f->GetRoutingID() == frame_id_; + }); + + if (iter != frames.end() && (*iter)->IsRenderFrameLive()) { + mojo::AssociatedRemote electron_renderer; + (*iter)->GetRemoteAssociatedInterfaces()->GetInterface(&electron_renderer); + electron_renderer->DereferenceRemoteJSCallback(context_id_, object_id_); } Observe(nullptr); diff --git a/shell/common/api/remote/remote_callback_freer.h b/shell/common/api/remote/remote_callback_freer.h index 14c6e6c76357e..512fecf67783c 100644 --- a/shell/common/api/remote/remote_callback_freer.h +++ b/shell/common/api/remote/remote_callback_freer.h @@ -17,6 +17,7 @@ class RemoteCallbackFreer : public ObjectLifeMonitor, public: static void BindTo(v8::Isolate* isolate, v8::Local target, + int frame_id, const std::string& context_id, int object_id, content::WebContents* web_conents); @@ -24,6 +25,7 @@ class RemoteCallbackFreer : public ObjectLifeMonitor, protected: RemoteCallbackFreer(v8::Isolate* isolate, v8::Local target, + int frame_id, const std::string& context_id, int object_id, content::WebContents* web_conents); @@ -35,6 +37,7 @@ class RemoteCallbackFreer : public ObjectLifeMonitor, void RenderViewDeleted(content::RenderViewHost*) override; private: + int frame_id_; std::string context_id_; int object_id_; diff --git a/shell/common/crash_reporter/crash_reporter.cc b/shell/common/crash_reporter/crash_reporter.cc index ec756ed3fe4a2..1fd9a275f7ec1 100644 --- a/shell/common/crash_reporter/crash_reporter.cc +++ b/shell/common/crash_reporter/crash_reporter.cc @@ -15,7 +15,7 @@ #include "content/public/common/content_switches.h" #include "electron/electron_version.h" #include "shell/browser/browser.h" -#include "shell/common/atom_constants.h" +#include "shell/common/electron_constants.h" #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_helper/dictionary.h" diff --git a/shell/common/crash_reporter/crash_reporter_win.cc b/shell/common/crash_reporter/crash_reporter_win.cc index 6231e4b71ba17..0a737901a1e3b 100644 --- a/shell/common/crash_reporter/crash_reporter_win.cc +++ b/shell/common/crash_reporter/crash_reporter_win.cc @@ -13,8 +13,9 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "electron/shell/common/api/api.mojom.h" +#include "mojo/public/cpp/bindings/associated_remote.h" #include "shell/browser/ui/inspectable_web_contents_impl.h" -#include "shell/common/atom_constants.h" +#include "shell/common/electron_constants.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" #include "third_party/crashpad/crashpad/client/crashpad_client.h" #include "third_party/crashpad/crashpad/client/crashpad_info.h" @@ -129,10 +130,10 @@ void CrashReporterWin::UpdatePipeName() { if (!frame_host) continue; - electron::mojom::ElectronRendererAssociatedPtr electron_ptr; + mojo::AssociatedRemote electron_renderer; frame_host->GetRemoteAssociatedInterfaces()->GetInterface( - mojo::MakeRequest(&electron_ptr)); - electron_ptr->UpdateCrashpadPipeName(pipe_name); + &electron_renderer); + electron_renderer->UpdateCrashpadPipeName(pipe_name); } } diff --git a/shell/common/crash_reporter/win/crash_service.cc b/shell/common/crash_reporter/win/crash_service.cc deleted file mode 100644 index c5589dd2e96ed..0000000000000 --- a/shell/common/crash_reporter/win/crash_service.cc +++ /dev/null @@ -1,508 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "shell/common/crash_reporter/win/crash_service.h" - -#include - -#include -#include // NOLINT -#include - -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/stl_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/time/time.h" -#include "base/win/windows_version.h" -#include "breakpad/src/client/windows/crash_generation/client_info.h" -#include "breakpad/src/client/windows/crash_generation/crash_generation_server.h" -#include "breakpad/src/client/windows/sender/crash_report_sender.h" - -namespace breakpad { - -namespace { - -const wchar_t kWaitEventFormat[] = L"$1CrashServiceWaitEvent"; -const wchar_t kClassNameFormat[] = L"$1CrashServiceWindow"; - -const wchar_t kTestPipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; - -const wchar_t kGoogleReportURL[] = L"https://clients2.google.com/cr/report"; -const wchar_t kCheckPointFile[] = L"crash_checkpoint.txt"; - -typedef std::map CrashMap; - -bool CustomInfoToMap(const google_breakpad::ClientInfo* client_info, - const std::wstring& reporter_tag, - CrashMap* map) { - google_breakpad::CustomClientInfo info = client_info->GetCustomInfo(); - - for (uintptr_t i = 0; i < info.count; ++i) { - (*map)[info.entries[i].name] = info.entries[i].value; - } - - (*map)[L"rept"] = reporter_tag; - - return !map->empty(); -} - -bool WriteCustomInfoToFile(const std::wstring& dump_path, const CrashMap& map) { - std::wstring file_path(dump_path); - size_t last_dot = file_path.rfind(L'.'); - if (last_dot == std::wstring::npos) - return false; - file_path.resize(last_dot); - file_path += L".txt"; - - std::wofstream file(file_path.c_str(), std::ios_base::out | - std::ios_base::app | - std::ios::binary); - if (!file.is_open()) - return false; - - CrashMap::const_iterator pos; - for (pos = map.begin(); pos != map.end(); ++pos) { - std::wstring line = pos->first; - line += L':'; - line += pos->second; - line += L'\n'; - file.write(line.c_str(), static_cast(line.length())); - } - return true; -} - -bool WriteReportIDToFile(const std::wstring& dump_path, - const std::wstring& report_id) { - std::wstring file_path(dump_path); - size_t last_slash = file_path.rfind(L'\\'); - if (last_slash == std::wstring::npos) - return false; - file_path.resize(last_slash); - file_path += L"\\uploads.log"; - - std::wofstream file(file_path.c_str(), std::ios_base::out | - std::ios_base::app | - std::ios::binary); - if (!file.is_open()) - return false; - - int64_t seconds_since_epoch = - (base::Time::Now() - base::Time::UnixEpoch()).InSeconds(); - std::wstring line = base::NumberToString16(seconds_since_epoch); - line += L','; - line += report_id; - line += L'\n'; - file.write(line.c_str(), static_cast(line.length())); - return true; -} - -// The window procedure task is to handle when a) the user logs off. -// b) the system shuts down or c) when the user closes the window. -LRESULT __stdcall CrashSvcWndProc(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam) { - switch (message) { - case WM_CLOSE: - case WM_ENDSESSION: - case WM_DESTROY: - PostQuitMessage(0); - break; - default: - return DefWindowProc(hwnd, message, wparam, lparam); - } - return 0; -} - -// This is the main and only application window. -HWND g_top_window = NULL; - -bool CreateTopWindow(HINSTANCE instance, - const base::string16& application_name, - bool visible) { - base::string16 class_name = - base::ReplaceStringPlaceholders(kClassNameFormat, application_name, NULL); - - WNDCLASSEXW wcx = {0}; - wcx.cbSize = sizeof(wcx); - wcx.style = CS_HREDRAW | CS_VREDRAW; - wcx.lpfnWndProc = CrashSvcWndProc; - wcx.hInstance = instance; - wcx.lpszClassName = class_name.c_str(); - ::RegisterClassExW(&wcx); - DWORD style = visible ? WS_POPUPWINDOW | WS_VISIBLE : WS_OVERLAPPED; - - // The window size is zero but being a popup window still shows in the - // task bar and can be closed using the system menu or using task manager. - HWND window = CreateWindowExW(0, wcx.lpszClassName, L"crash service", style, - CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, - instance, NULL); - if (!window) - return false; - - ::UpdateWindow(window); - VLOG(1) << "window handle is " << window; - g_top_window = window; - return true; -} - -// Simple helper class to keep the process alive until the current request -// finishes. -class ProcessingLock { - public: - ProcessingLock() { ::InterlockedIncrement(&op_count_); } - ~ProcessingLock() { ::InterlockedDecrement(&op_count_); } - static bool IsWorking() { return (op_count_ != 0); } - - private: - static volatile LONG op_count_; -}; - -volatile LONG ProcessingLock::op_count_ = 0; - -// This structure contains the information that the worker thread needs to -// send a crash dump to the server. -struct DumpJobInfo { - DWORD pid; - CrashService* self; - CrashMap map; - std::wstring dump_path; - - DumpJobInfo(DWORD process_id, - CrashService* service, - const CrashMap& crash_map, - const std::wstring& path) - : pid(process_id), self(service), map(crash_map), dump_path(path) {} -}; - -} // namespace - -// Command line switches: -const char CrashService::kMaxReports[] = "max-reports"; -const char CrashService::kNoWindow[] = "no-window"; -const char CrashService::kReporterTag[] = "reporter"; -const char CrashService::kDumpsDir[] = "dumps-dir"; -const char CrashService::kPipeName[] = "pipe-name"; -const char CrashService::kReporterURL[] = "reporter-url"; - -CrashService::CrashService() {} - -CrashService::~CrashService() { - base::AutoLock lock(sending_); - delete dumper_; - delete sender_; -} - -bool CrashService::Initialize(const base::string16& application_name, - const base::FilePath& operating_dir, - const base::FilePath& dumps_path) { - using google_breakpad::CrashGenerationServer; - using google_breakpad::CrashReportSender; - - std::wstring pipe_name = kTestPipeName; - int max_reports = -1; - - // The checkpoint file allows CrashReportSender to enforce the maximum - // reports per day quota. Does not seem to serve any other purpose. - base::FilePath checkpoint_path = operating_dir.Append(kCheckPointFile); - - base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess(); - - base::FilePath dumps_path_to_use = dumps_path; - - if (cmd_line.HasSwitch(kDumpsDir)) { - dumps_path_to_use = - base::FilePath(cmd_line.GetSwitchValueNative(kDumpsDir)); - } - - // We can override the send reports quota with a command line switch. - if (cmd_line.HasSwitch(kMaxReports)) - max_reports = _wtoi(cmd_line.GetSwitchValueNative(kMaxReports).c_str()); - - // Allow the global pipe name to be overridden for better testability. - if (cmd_line.HasSwitch(kPipeName)) - pipe_name = cmd_line.GetSwitchValueNative(kPipeName); - - if (max_reports > 0) { - // Create the http sender object. - sender_ = new CrashReportSender(checkpoint_path.value()); - sender_->set_max_reports_per_day(max_reports); - } - - SECURITY_ATTRIBUTES security_attributes = {0}; - SECURITY_DESCRIPTOR* security_descriptor = - reinterpret_cast( - GetSecurityDescriptorForLowIntegrity()); - DCHECK(security_descriptor != NULL); - - security_attributes.nLength = sizeof(security_attributes); - security_attributes.lpSecurityDescriptor = security_descriptor; - security_attributes.bInheritHandle = FALSE; - - // Create the OOP crash generator object. - dumper_ = new CrashGenerationServer( - pipe_name, &security_attributes, &CrashService::OnClientConnected, this, - &CrashService::OnClientDumpRequest, this, &CrashService::OnClientExited, - this, NULL, NULL, true, &dumps_path_to_use.value()); - - if (!dumper_) { - LOG(ERROR) << "could not create dumper"; - if (security_attributes.lpSecurityDescriptor) - LocalFree(security_attributes.lpSecurityDescriptor); - return false; - } - - if (!CreateTopWindow(::GetModuleHandleW(NULL), application_name, - !cmd_line.HasSwitch(kNoWindow))) { - LOG(ERROR) << "could not create window"; - if (security_attributes.lpSecurityDescriptor) - LocalFree(security_attributes.lpSecurityDescriptor); - return false; - } - - reporter_tag_ = L"crash svc"; - if (cmd_line.HasSwitch(kReporterTag)) - reporter_tag_ = cmd_line.GetSwitchValueNative(kReporterTag); - - reporter_url_ = kGoogleReportURL; - if (cmd_line.HasSwitch(kReporterURL)) - reporter_url_ = cmd_line.GetSwitchValueNative(kReporterURL); - - // Log basic information. - VLOG(1) << "pipe name is " << pipe_name << "\ndumps at " - << dumps_path_to_use.value(); - - if (sender_) { - VLOG(1) << "checkpoint is " << checkpoint_path.value() << "\nserver is " - << reporter_url_ << "\nmaximum " << sender_->max_reports_per_day() - << " reports/day" - << "\nreporter is " << reporter_tag_; - } - // Start servicing clients. - if (!dumper_->Start()) { - LOG(ERROR) << "could not start dumper"; - if (security_attributes.lpSecurityDescriptor) - LocalFree(security_attributes.lpSecurityDescriptor); - return false; - } - - if (security_attributes.lpSecurityDescriptor) - LocalFree(security_attributes.lpSecurityDescriptor); - - // Create or open an event to signal the browser process that the crash - // service is initialized. - base::string16 wait_name = - base::ReplaceStringPlaceholders(kWaitEventFormat, application_name, NULL); - HANDLE wait_event = ::CreateEventW(NULL, TRUE, TRUE, wait_name.c_str()); - ::SetEvent(wait_event); - - return true; -} - -void CrashService::OnClientConnected( - void* context, - const google_breakpad::ClientInfo* client_info) { - ProcessingLock lock; - VLOG(1) << "client start. pid = " << client_info->pid(); - CrashService* self = static_cast(context); - ::InterlockedIncrement(&self->clients_connected_); -} - -void CrashService::OnClientExited( - void* context, - const google_breakpad::ClientInfo* client_info) { - ProcessingLock processing_lock; - VLOG(1) << "client end. pid = " << client_info->pid(); - CrashService* self = static_cast(context); - ::InterlockedIncrement(&self->clients_terminated_); - - if (!self->sender_) - return; - - // When we are instructed to send reports we need to exit if there are - // no more clients to service. The next client that runs will start us. - // Only chrome.exe starts crash_service with a non-zero max_reports. - if (self->clients_connected_ > self->clients_terminated_) - return; - if (self->sender_->max_reports_per_day() > 0) { - // Wait for the other thread to send crashes, if applicable. The sender - // thread takes the sending_ lock, so the sleep is just to give it a - // chance to start. - ::Sleep(1000); - base::AutoLock lock(self->sending_); - // Some people can restart chrome very fast, check again if we have - // a new client before exiting for real. - if (self->clients_connected_ == self->clients_terminated_) { - VLOG(1) << "zero clients. exiting"; - ::PostMessage(g_top_window, WM_CLOSE, 0, 0); - } - } -} - -void CrashService::OnClientDumpRequest( - void* context, - const google_breakpad::ClientInfo* client_info, - const std::wstring* file_path) { - ProcessingLock lock; - - if (!file_path) { - LOG(ERROR) << "dump with no file path"; - return; - } - if (!client_info) { - LOG(ERROR) << "dump with no client info"; - return; - } - - CrashService* self = static_cast(context); - if (!self) { - LOG(ERROR) << "dump with no context"; - return; - } - - CrashMap map; - CustomInfoToMap(client_info, self->reporter_tag_, &map); - - // Move dump file to the directory under client breakpad dump location. - base::FilePath dump_location = base::FilePath(*file_path); - CrashMap::const_iterator it = map.find(L"breakpad-dump-location"); - if (it != map.end()) { - base::FilePath alternate_dump_location = base::FilePath(it->second); - base::CreateDirectoryW(alternate_dump_location); - alternate_dump_location = - alternate_dump_location.Append(dump_location.BaseName()); - base::Move(dump_location, alternate_dump_location); - dump_location = alternate_dump_location; - } - - DWORD pid = client_info->pid(); - VLOG(1) << "dump for pid = " << pid << " is " << dump_location.value(); - - if (!WriteCustomInfoToFile(dump_location.value(), map)) { - LOG(ERROR) << "could not write custom info file"; - } - - if (!self->sender_ || map.find(L"skip_upload") != map.end()) - return; - - // Send the crash dump using a worker thread. This operation has retry - // logic in case there is no internet connection at the time. - DumpJobInfo* dump_job = - new DumpJobInfo(pid, self, map, dump_location.value()); - if (!::QueueUserWorkItem(&CrashService::AsyncSendDump, dump_job, - WT_EXECUTELONGFUNCTION)) { - LOG(ERROR) << "could not queue job"; - } -} - -// We are going to try sending the report several times. If we can't send, -// we sleep from one minute to several hours depending on the retry round. -DWORD CrashService::AsyncSendDump(void* context) { - if (!context) - return 0; - - DumpJobInfo* info = static_cast(context); - - std::wstring report_id = L""; - - const DWORD kOneMinute = 60 * 1000; - const DWORD kOneHour = 60 * kOneMinute; - - const DWORD kSleepSchedule[] = {24 * kOneHour, 8 * kOneHour, 4 * kOneHour, - kOneHour, 15 * kOneMinute, 0}; - - int retry_round = base::size(kSleepSchedule) - 1; - - do { - ::Sleep(kSleepSchedule[retry_round]); - { - // Take the server lock while sending. This also prevent early - // termination of the service object. - base::AutoLock lock(info->self->sending_); - VLOG(1) << "trying to send report for pid = " << info->pid; - std::map file_map; - file_map[L"upload_file_minidump"] = info->dump_path; - google_breakpad::ReportResult send_result = - info->self->sender_->SendCrashReport(info->self->reporter_url_, - info->map, file_map, &report_id); - switch (send_result) { - case google_breakpad::RESULT_FAILED: - report_id = L""; - break; - case google_breakpad::RESULT_REJECTED: - report_id = L""; - ++info->self->requests_handled_; - retry_round = 0; - break; - case google_breakpad::RESULT_SUCCEEDED: - ++info->self->requests_sent_; - ++info->self->requests_handled_; - retry_round = 0; - WriteReportIDToFile(info->dump_path, report_id); - break; - case google_breakpad::RESULT_THROTTLED: - report_id = L""; - break; - default: - report_id = L""; - break; - } - } - - VLOG(1) << "dump for pid =" << info->pid << " crash2 id =" << report_id; - --retry_round; - } while (retry_round >= 0); - - if (!::DeleteFileW(info->dump_path.c_str())) - LOG(WARNING) << "could not delete " << info->dump_path; - - delete info; - return 0; -} - -int CrashService::ProcessingLoop() { - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - VLOG(1) << "session ending.."; - while (ProcessingLock::IsWorking()) { - ::Sleep(50); - } - - VLOG(1) << "clients connected :" << clients_connected_ - << "\nclients terminated :" << clients_terminated_ - << "\ndumps serviced :" << requests_handled_ - << "\ndumps reported :" << requests_sent_; - - return static_cast(msg.wParam); -} - -PSECURITY_DESCRIPTOR CrashService::GetSecurityDescriptorForLowIntegrity() { - // Build the SDDL string for the label. - std::wstring sddl = L"S:(ML;;NW;;;S-1-16-4096)"; - - PSECURITY_DESCRIPTOR sec_desc = NULL; - - PACL sacl = NULL; - BOOL sacl_present = FALSE; - BOOL sacl_defaulted = FALSE; - - if (::ConvertStringSecurityDescriptorToSecurityDescriptorW( - sddl.c_str(), SDDL_REVISION, &sec_desc, NULL)) { - if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl, - &sacl_defaulted)) { - return sec_desc; - } - } - - return NULL; -} - -} // namespace breakpad diff --git a/shell/common/crash_reporter/win/crash_service.h b/shell/common/crash_reporter/win/crash_service.h deleted file mode 100644 index 7a9f2b7dc48ed..0000000000000 --- a/shell/common/crash_reporter/win/crash_service.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef SHELL_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_ -#define SHELL_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_ - -#include - -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/synchronization/lock.h" - -#if defined(OS_WIN) -#include -#endif // defined(OS_WIN) - -namespace google_breakpad { - -class CrashReportSender; -class CrashGenerationServer; -class ClientInfo; - -} // namespace google_breakpad - -namespace breakpad { - -// This class implements an out-of-process crash server. It uses breakpad's -// CrashGenerationServer and CrashReportSender to generate and then send the -// crash dumps. Internally, it uses OS specific pipe to allow applications to -// register for crash dumps and later on when a registered application crashes -// it will signal an event that causes this code to wake up and perform a -// crash dump on the signaling process. The dump is then stored on disk and -// possibly sent to the crash2 servers. -class CrashService { - public: - CrashService(); - ~CrashService(); - - // Starts servicing crash dumps. Returns false if it failed. Do not use - // other members in that case. |operating_dir| is where the CrashService - // should store breakpad's checkpoint file. |dumps_path| is the directory - // where the crash dumps should be stored. - bool Initialize(const base::string16& application_name, - const base::FilePath& operating_dir, - const base::FilePath& dumps_path); - - // Command line switches: - // - // --max-reports= - // Allows to override the maximum number for reports per day. Normally - // the crash dumps are never sent so if you want to send any you must - // specify a positive number here. - static const char kMaxReports[]; - // --no-window - // Does not create a visible window on the desktop. The window does not have - // any other functionality other than allowing the crash service to be - // gracefully closed. - static const char kNoWindow[]; - // --reporter= - // Allows to specify a custom string that appears on the detail crash report - // page in the crash server. This should be a 25 chars or less string. - // The default tag if not specified is 'crash svc'. - static const char kReporterTag[]; - // --dumps-dir= - // Override the directory to which crash dump files will be written. - static const char kDumpsDir[]; - // --pipe-name= - // Override the name of the Windows named pipe on which we will - // listen for crash dump request messages. - static const char kPipeName[]; - // --reporter-url= - // Override the URL to which crash reports will be sent to. - static const char kReporterURL[]; - - // Returns number of crash dumps handled. - int requests_handled() const { return requests_handled_; } - // Returns number of crash clients registered. - int clients_connected() const { return clients_connected_; } - // Returns number of crash clients terminated. - int clients_terminated() const { return clients_terminated_; } - - // Starts the processing loop. This function does not return unless the - // user is logging off or the user closes the crash service window. The - // return value is a good number to pass in ExitProcess(). - int ProcessingLoop(); - - private: - static void OnClientConnected(void* context, - const google_breakpad::ClientInfo* client_info); - - static void OnClientDumpRequest( - void* context, - const google_breakpad::ClientInfo* client_info, - const std::wstring* file_path); - - static void OnClientExited(void* context, - const google_breakpad::ClientInfo* client_info); - - // This routine sends the crash dump to the server. It takes the sending_ - // lock when it is performing the send. - static DWORD __stdcall AsyncSendDump(void* context); - - // Returns the security descriptor which access to low integrity processes - // The caller is supposed to free the security descriptor by calling - // LocalFree. - PSECURITY_DESCRIPTOR GetSecurityDescriptorForLowIntegrity(); - - google_breakpad::CrashGenerationServer* dumper_ = nullptr; - google_breakpad::CrashReportSender* sender_ = nullptr; - - // the extra tag sent to the server with each dump. - std::wstring reporter_tag_; - - // receiver URL of crash reports. - std::wstring reporter_url_; - - // clients serviced statistics: - int requests_handled_ = 0; - int requests_sent_ = 0; - volatile LONG clients_connected_ = 0; - volatile LONG clients_terminated_ = 0; - base::Lock sending_; - - DISALLOW_COPY_AND_ASSIGN(CrashService); -}; - -} // namespace breakpad - -#endif // SHELL_COMMON_CRASH_REPORTER_WIN_CRASH_SERVICE_H_ diff --git a/shell/common/crash_reporter/win/crash_service_main.cc b/shell/common/crash_reporter/win/crash_service_main.cc index 63e3a316ec122..cd5fb81fc5457 100644 --- a/shell/common/crash_reporter/win/crash_service_main.cc +++ b/shell/common/crash_reporter/win/crash_service_main.cc @@ -13,7 +13,6 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "shell/common/crash_reporter/crash_reporter.h" -#include "shell/common/crash_reporter/win/crash_service.h" #include "third_party/crashpad/crashpad/handler/handler_main.h" namespace crash_service { diff --git a/shell/common/atom_command_line.cc b/shell/common/electron_command_line.cc similarity index 69% rename from shell/common/atom_command_line.cc rename to shell/common/electron_command_line.cc index e29e2114780d6..765c199b92be4 100644 --- a/shell/common/atom_command_line.cc +++ b/shell/common/electron_command_line.cc @@ -2,18 +2,18 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/common/atom_command_line.h" +#include "shell/common/electron_command_line.h" #include "base/command_line.h" -#include "uv.h" // NOLINT(build/include) +#include "uv.h" // NOLINT(build/include_directory) namespace electron { // static -base::CommandLine::StringVector AtomCommandLine::argv_; +base::CommandLine::StringVector ElectronCommandLine::argv_; // static -void AtomCommandLine::Init(int argc, base::CommandLine::CharType** argv) { +void ElectronCommandLine::Init(int argc, base::CommandLine::CharType** argv) { DCHECK(argv_.empty()); // NOTE: uv_setup_args does nothing on Windows, so we don't need to call it. @@ -28,7 +28,7 @@ void AtomCommandLine::Init(int argc, base::CommandLine::CharType** argv) { #if defined(OS_LINUX) // static -void AtomCommandLine::InitializeFromCommandLine() { +void ElectronCommandLine::InitializeFromCommandLine() { argv_ = base::CommandLine::ForCurrentProcess()->argv(); } #endif diff --git a/shell/common/atom_command_line.h b/shell/common/electron_command_line.h similarity index 77% rename from shell/common/atom_command_line.h rename to shell/common/electron_command_line.h index c84f887735c67..7a3cdc5b92759 100644 --- a/shell/common/atom_command_line.h +++ b/shell/common/electron_command_line.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_COMMON_ATOM_COMMAND_LINE_H_ -#define SHELL_COMMON_ATOM_COMMAND_LINE_H_ +#ifndef SHELL_COMMON_ELECTRON_COMMAND_LINE_H_ +#define SHELL_COMMON_ELECTRON_COMMAND_LINE_H_ #include #include @@ -15,7 +15,7 @@ namespace electron { // Singleton to remember the original "argc" and "argv". -class AtomCommandLine { +class ElectronCommandLine { public: static const base::CommandLine::StringVector& argv() { return argv_; } @@ -30,9 +30,9 @@ class AtomCommandLine { private: static base::CommandLine::StringVector argv_; - DISALLOW_IMPLICIT_CONSTRUCTORS(AtomCommandLine); + DISALLOW_IMPLICIT_CONSTRUCTORS(ElectronCommandLine); }; } // namespace electron -#endif // SHELL_COMMON_ATOM_COMMAND_LINE_H_ +#endif // SHELL_COMMON_ELECTRON_COMMAND_LINE_H_ diff --git a/shell/common/atom_constants.cc b/shell/common/electron_constants.cc similarity index 97% rename from shell/common/atom_constants.cc rename to shell/common/electron_constants.cc index 01426ae054199..48bf7ae523e2b 100644 --- a/shell/common/atom_constants.cc +++ b/shell/common/electron_constants.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/common/atom_constants.h" +#include "shell/common/electron_constants.h" namespace electron { diff --git a/shell/common/atom_constants.h b/shell/common/electron_constants.h similarity index 91% rename from shell/common/atom_constants.h rename to shell/common/electron_constants.h index 79a7416885236..43d8bfe8be6f8 100644 --- a/shell/common/atom_constants.h +++ b/shell/common/electron_constants.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_COMMON_ATOM_CONSTANTS_H_ -#define SHELL_COMMON_ATOM_CONSTANTS_H_ +#ifndef SHELL_COMMON_ELECTRON_CONSTANTS_H_ +#define SHELL_COMMON_ELECTRON_CONSTANTS_H_ #include "build/build_config.h" #include "electron/buildflags/buildflags.h" @@ -49,4 +49,4 @@ extern const char kPdfViewerUIHost[]; } // namespace electron -#endif // SHELL_COMMON_ATOM_CONSTANTS_H_ +#endif // SHELL_COMMON_ELECTRON_CONSTANTS_H_ diff --git a/shell/common/extensions/atom_extensions_api_provider.cc b/shell/common/extensions/electron_extensions_api_provider.cc similarity index 63% rename from shell/common/extensions/atom_extensions_api_provider.cc rename to shell/common/extensions/electron_extensions_api_provider.cc index e77bf5869dde4..e34d3f42d3f30 100644 --- a/shell/common/extensions/atom_extensions_api_provider.cc +++ b/shell/common/extensions/electron_extensions_api_provider.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/common/extensions/atom_extensions_api_provider.h" +#include "shell/common/extensions/electron_extensions_api_provider.h" #include @@ -15,17 +15,17 @@ namespace electron { -AtomExtensionsAPIProvider::AtomExtensionsAPIProvider() = default; -AtomExtensionsAPIProvider::~AtomExtensionsAPIProvider() = default; +ElectronExtensionsAPIProvider::ElectronExtensionsAPIProvider() = default; +ElectronExtensionsAPIProvider::~ElectronExtensionsAPIProvider() = default; // TODO(samuelmaddock): generate API features? -void AtomExtensionsAPIProvider::AddAPIFeatures( +void ElectronExtensionsAPIProvider::AddAPIFeatures( extensions::FeatureProvider* provider) { // AddShellAPIFeatures(provider); } -void AtomExtensionsAPIProvider::AddManifestFeatures( +void ElectronExtensionsAPIProvider::AddManifestFeatures( extensions::FeatureProvider* provider) { #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) // TODO(samuelmaddock): why is the extensions namespace generated? @@ -33,35 +33,36 @@ void AtomExtensionsAPIProvider::AddManifestFeatures( #endif } -void AtomExtensionsAPIProvider::AddPermissionFeatures( +void ElectronExtensionsAPIProvider::AddPermissionFeatures( extensions::FeatureProvider* provider) { // No shell-specific permission features. } -void AtomExtensionsAPIProvider::AddBehaviorFeatures( +void ElectronExtensionsAPIProvider::AddBehaviorFeatures( extensions::FeatureProvider* provider) { // No shell-specific behavior features. } -void AtomExtensionsAPIProvider::AddAPIJSONSources( +void ElectronExtensionsAPIProvider::AddAPIJSONSources( extensions::JSONFeatureProviderSource* json_source) { // json_source->LoadJSON(IDR_SHELL_EXTENSION_API_FEATURES); } -bool AtomExtensionsAPIProvider::IsAPISchemaGenerated(const std::string& name) { +bool ElectronExtensionsAPIProvider::IsAPISchemaGenerated( + const std::string& name) { // return shell::api::ShellGeneratedSchemas::IsGenerated(name); return false; } -base::StringPiece AtomExtensionsAPIProvider::GetAPISchema( +base::StringPiece ElectronExtensionsAPIProvider::GetAPISchema( const std::string& name) { // return shell::api::ShellGeneratedSchemas::Get(name); return ""; } -void AtomExtensionsAPIProvider::RegisterPermissions( +void ElectronExtensionsAPIProvider::RegisterPermissions( extensions::PermissionsInfo* permissions_info) {} -void AtomExtensionsAPIProvider::RegisterManifestHandlers() {} +void ElectronExtensionsAPIProvider::RegisterManifestHandlers() {} } // namespace electron diff --git a/shell/common/extensions/atom_extensions_api_provider.h b/shell/common/extensions/electron_extensions_api_provider.h similarity index 70% rename from shell/common/extensions/atom_extensions_api_provider.h rename to shell/common/extensions/electron_extensions_api_provider.h index 80a6303c61080..5fe9aebd129be 100644 --- a/shell/common/extensions/atom_extensions_api_provider.h +++ b/shell/common/extensions/electron_extensions_api_provider.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_API_PROVIDER_H_ -#define SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_API_PROVIDER_H_ +#ifndef SHELL_COMMON_EXTENSIONS_ELECTRON_EXTENSIONS_API_PROVIDER_H_ +#define SHELL_COMMON_EXTENSIONS_ELECTRON_EXTENSIONS_API_PROVIDER_H_ #include @@ -12,10 +12,10 @@ namespace electron { -class AtomExtensionsAPIProvider : public extensions::ExtensionsAPIProvider { +class ElectronExtensionsAPIProvider : public extensions::ExtensionsAPIProvider { public: - AtomExtensionsAPIProvider(); - ~AtomExtensionsAPIProvider() override; + ElectronExtensionsAPIProvider(); + ~ElectronExtensionsAPIProvider() override; // ExtensionsAPIProvider: void AddAPIFeatures(extensions::FeatureProvider* provider) override; @@ -31,9 +31,9 @@ class AtomExtensionsAPIProvider : public extensions::ExtensionsAPIProvider { void RegisterManifestHandlers() override; private: - DISALLOW_COPY_AND_ASSIGN(AtomExtensionsAPIProvider); + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionsAPIProvider); }; } // namespace electron -#endif // SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_API_PROVIDER_H_ +#endif // SHELL_COMMON_EXTENSIONS_ELECTRON_EXTENSIONS_API_PROVIDER_H_ diff --git a/shell/common/extensions/atom_extensions_client.cc b/shell/common/extensions/electron_extensions_client.cc similarity index 70% rename from shell/common/extensions/atom_extensions_client.cc rename to shell/common/extensions/electron_extensions_client.cc index d1d2d0dfc5c93..fc300ad20d0dd 100644 --- a/shell/common/extensions/atom_extensions_client.cc +++ b/shell/common/extensions/electron_extensions_client.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/common/extensions/atom_extensions_client.h" +#include "shell/common/extensions/electron_extensions_client.h" #include #include @@ -17,7 +17,7 @@ #include "extensions/common/features/simple_feature.h" #include "extensions/common/permissions/permission_message_provider.h" #include "extensions/common/url_pattern_set.h" -#include "shell/common/extensions/atom_extensions_api_provider.h" +#include "shell/common/extensions/electron_extensions_api_provider.h" using extensions::ExtensionsClient; @@ -27,11 +27,11 @@ namespace { // TODO(jamescook): Refactor ChromePermissionsMessageProvider so we can share // code. For now, this implementation does nothing. -class AtomPermissionMessageProvider +class ElectronPermissionMessageProvider : public extensions::PermissionMessageProvider { public: - AtomPermissionMessageProvider() {} - ~AtomPermissionMessageProvider() override {} + ElectronPermissionMessageProvider() {} + ~ElectronPermissionMessageProvider() override {} // PermissionMessageProvider implementation. extensions::PermissionMessages GetPermissionMessages( @@ -60,81 +60,82 @@ class AtomPermissionMessageProvider } private: - DISALLOW_COPY_AND_ASSIGN(AtomPermissionMessageProvider); + DISALLOW_COPY_AND_ASSIGN(ElectronPermissionMessageProvider); }; -base::LazyInstance::DestructorAtExit +base::LazyInstance::DestructorAtExit g_permission_message_provider = LAZY_INSTANCE_INITIALIZER; } // namespace -AtomExtensionsClient::AtomExtensionsClient() +ElectronExtensionsClient::ElectronExtensionsClient() : webstore_base_url_(extension_urls::kChromeWebstoreBaseURL), webstore_update_url_(extension_urls::kChromeWebstoreUpdateURL) { AddAPIProvider(std::make_unique()); - AddAPIProvider(std::make_unique()); + AddAPIProvider(std::make_unique()); } -AtomExtensionsClient::~AtomExtensionsClient() {} +ElectronExtensionsClient::~ElectronExtensionsClient() {} -void AtomExtensionsClient::Initialize() { +void ElectronExtensionsClient::Initialize() { // TODO(jamescook): Do we need to whitelist any extensions? } -void AtomExtensionsClient::InitializeWebStoreUrls( +void ElectronExtensionsClient::InitializeWebStoreUrls( base::CommandLine* command_line) {} const extensions::PermissionMessageProvider& -AtomExtensionsClient::GetPermissionMessageProvider() const { +ElectronExtensionsClient::GetPermissionMessageProvider() const { NOTIMPLEMENTED(); return g_permission_message_provider.Get(); } -const std::string AtomExtensionsClient::GetProductName() { +const std::string ElectronExtensionsClient::GetProductName() { // TODO(samuelmaddock): return "app_shell"; } -void AtomExtensionsClient::FilterHostPermissions( +void ElectronExtensionsClient::FilterHostPermissions( const extensions::URLPatternSet& hosts, extensions::URLPatternSet* new_hosts, extensions::PermissionIDSet* permissions) const { NOTIMPLEMENTED(); } -void AtomExtensionsClient::SetScriptingWhitelist( +void ElectronExtensionsClient::SetScriptingWhitelist( const ExtensionsClient::ScriptingWhitelist& whitelist) { scripting_whitelist_ = whitelist; } const ExtensionsClient::ScriptingWhitelist& -AtomExtensionsClient::GetScriptingWhitelist() const { +ElectronExtensionsClient::GetScriptingWhitelist() const { // TODO(jamescook): Real whitelist. return scripting_whitelist_; } -extensions::URLPatternSet AtomExtensionsClient::GetPermittedChromeSchemeHosts( +extensions::URLPatternSet +ElectronExtensionsClient::GetPermittedChromeSchemeHosts( const extensions::Extension* extension, const extensions::APIPermissionSet& api_permissions) const { NOTIMPLEMENTED(); return extensions::URLPatternSet(); } -bool AtomExtensionsClient::IsScriptableURL(const GURL& url, - std::string* error) const { +bool ElectronExtensionsClient::IsScriptableURL(const GURL& url, + std::string* error) const { // No restrictions on URLs. return true; } -const GURL& AtomExtensionsClient::GetWebstoreBaseURL() const { +const GURL& ElectronExtensionsClient::GetWebstoreBaseURL() const { return webstore_base_url_; } -const GURL& AtomExtensionsClient::GetWebstoreUpdateURL() const { +const GURL& ElectronExtensionsClient::GetWebstoreUpdateURL() const { return webstore_update_url_; } -bool AtomExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const { +bool ElectronExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const { // TODO(rockot): Maybe we want to do something else here. For now we accept // any URL as a blacklist URL because we don't really care. return true; diff --git a/shell/common/extensions/atom_extensions_client.h b/shell/common/extensions/electron_extensions_client.h similarity index 82% rename from shell/common/extensions/atom_extensions_client.h rename to shell/common/extensions/electron_extensions_client.h index 6601b323faf70..7ed155e55bbe1 100644 --- a/shell/common/extensions/atom_extensions_client.h +++ b/shell/common/extensions/electron_extensions_client.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_CLIENT_H_ -#define SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_CLIENT_H_ +#ifndef SHELL_COMMON_EXTENSIONS_ELECTRON_EXTENSIONS_CLIENT_H_ +#define SHELL_COMMON_EXTENSIONS_ELECTRON_EXTENSIONS_CLIENT_H_ #include @@ -24,12 +24,12 @@ class URLPatternSet; namespace electron { // The app_shell implementation of ExtensionsClient. -class AtomExtensionsClient : public extensions::ExtensionsClient { +class ElectronExtensionsClient : public extensions::ExtensionsClient { public: typedef extensions::ExtensionsClient::ScriptingWhitelist ScriptingWhitelist; - AtomExtensionsClient(); - ~AtomExtensionsClient() override; + ElectronExtensionsClient(); + ~ElectronExtensionsClient() override; // ExtensionsClient overrides: void Initialize() override; @@ -57,9 +57,9 @@ class AtomExtensionsClient : public extensions::ExtensionsClient { const GURL webstore_base_url_; const GURL webstore_update_url_; - DISALLOW_COPY_AND_ASSIGN(AtomExtensionsClient); + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionsClient); }; } // namespace electron -#endif // SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_CLIENT_H_ +#endif // SHELL_COMMON_EXTENSIONS_ELECTRON_EXTENSIONS_CLIENT_H_ diff --git a/shell/common/gin_converters/file_dialog_converter.cc b/shell/common/gin_converters/file_dialog_converter.cc index 1d8c27ebd2301..2f12c71a18cb2 100644 --- a/shell/common/gin_converters/file_dialog_converter.cc +++ b/shell/common/gin_converters/file_dialog_converter.cc @@ -5,7 +5,7 @@ #include "shell/common/gin_converters/file_dialog_converter.h" #include "gin/dictionary.h" -#include "shell/browser/api/atom_api_browser_window.h" +#include "shell/browser/api/electron_api_browser_window.h" #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_converters/native_window_converter.h" diff --git a/shell/common/gin_converters/image_converter.cc b/shell/common/gin_converters/image_converter.cc index 78bcfa96bc30d..a0b73e23c4c34 100644 --- a/shell/common/gin_converters/image_converter.cc +++ b/shell/common/gin_converters/image_converter.cc @@ -4,7 +4,7 @@ #include "shell/common/gin_converters/image_converter.h" -#include "shell/common/api/atom_api_native_image.h" +#include "shell/common/api/electron_api_native_image.h" #include "shell/common/gin_converters/file_path_converter.h" #include "ui/gfx/image/image_skia.h" @@ -27,9 +27,8 @@ bool Converter::FromV8(v8::Isolate* isolate, if (val->IsNull()) return true; - // TODO(deermichel): remove mate:: after dropping mate - mate::Handle native_image; - if (!mate::ConvertFromV8(isolate, val, &native_image)) + gin::Handle native_image; + if (!gin::ConvertFromV8(isolate, val, &native_image)) return false; *out = native_image->image(); @@ -38,7 +37,8 @@ bool Converter::FromV8(v8::Isolate* isolate, v8::Local Converter::ToV8(v8::Isolate* isolate, const gfx::Image& val) { - return ConvertToV8(isolate, electron::api::NativeImage::Create(isolate, val)); + return gin::ConvertToV8(isolate, + electron::api::NativeImage::Create(isolate, val)); } } // namespace gin diff --git a/shell/common/gin_converters/message_box_converter.cc b/shell/common/gin_converters/message_box_converter.cc index 92ec509764e38..5403827ed5a32 100644 --- a/shell/common/gin_converters/message_box_converter.cc +++ b/shell/common/gin_converters/message_box_converter.cc @@ -24,11 +24,11 @@ bool Converter::FromV8( dict.Get("buttons", &out->buttons); dict.Get("defaultId", &out->default_id); dict.Get("cancelId", &out->cancel_id); - dict.Get("options", &out->options); dict.Get("title", &out->title); dict.Get("message", &out->message); dict.Get("detail", &out->detail); dict.Get("checkboxLabel", &out->checkbox_label); + dict.Get("noLink", &out->no_link); dict.Get("checkboxChecked", &out->checkbox_checked); dict.Get("icon", &out->icon); return true; diff --git a/shell/common/gin_converters/native_window_converter.h b/shell/common/gin_converters/native_window_converter.h index cb1d3b128607f..9d0f7e882cdcc 100644 --- a/shell/common/gin_converters/native_window_converter.h +++ b/shell/common/gin_converters/native_window_converter.h @@ -6,7 +6,7 @@ #define SHELL_COMMON_GIN_CONVERTERS_NATIVE_WINDOW_CONVERTER_H_ #include "gin/converter.h" -#include "shell/browser/api/atom_api_top_level_window.h" +#include "shell/browser/api/electron_api_top_level_window.h" namespace gin { diff --git a/shell/common/gin_converters/net_converter.cc b/shell/common/gin_converters/net_converter.cc index 4369406a84577..1346ede5d5e45 100644 --- a/shell/common/gin_converters/net_converter.cc +++ b/shell/common/gin_converters/net_converter.cc @@ -21,8 +21,10 @@ #include "net/cert/x509_certificate.h" #include "net/cert/x509_util.h" #include "net/http/http_response_headers.h" +#include "net/http/http_version.h" +#include "net/url_request/redirect_info.h" #include "services/network/public/cpp/resource_request.h" -#include "shell/browser/api/atom_api_data_pipe_holder.h" +#include "shell/browser/api/electron_api_data_pipe_holder.h" #include "shell/common/gin_converters/gurl_converter.h" #include "shell/common/gin_converters/std_converter.h" #include "shell/common/gin_converters/value_converter_gin_adapter.h" @@ -258,14 +260,22 @@ v8::Local Converter::ToV8( gin::Dictionary upload_data(isolate, v8::Object::New(isolate)); switch (element.type()) { case network::mojom::DataElementType::kFile: + upload_data.Set("type", "file"); upload_data.Set("file", element.path().value()); + upload_data.Set("filePath", base::Value(element.path().AsUTF8Unsafe())); + upload_data.Set("offset", static_cast(element.offset())); + upload_data.Set("length", static_cast(element.length())); + upload_data.Set("modificationTime", + element.expected_modification_time().ToDoubleT()); break; case network::mojom::DataElementType::kBytes: + upload_data.Set("type", "rawData"); upload_data.Set("bytes", node::Buffer::Copy(isolate, element.bytes(), element.length()) .ToLocalChecked()); break; case network::mojom::DataElementType::kDataPipe: { + upload_data.Set("type", "blob"); // TODO(zcbenz): After the NetworkService refactor, the old blobUUID API // becomes unecessarily complex, we should deprecate the getBlobData API // and return the DataPipeHolder wrapper directly. @@ -289,7 +299,7 @@ v8::Local Converter::ToV8( v8::Local Converter::ToV8( v8::Isolate* isolate, const network::ResourceRequest& val) { - gin::Dictionary dict(isolate, v8::Object::New(isolate)); + gin::Dictionary dict = gin::Dictionary::CreateEmpty(isolate); dict.Set("method", val.method); dict.Set("url", val.url.spec()); dict.Set("referrer", val.referrer.spec()); @@ -311,6 +321,34 @@ v8::Local Converter::ToV8( return ConvertToV8(isolate, dict); } +// static +v8::Local Converter::ToV8( + v8::Isolate* isolate, + const net::HttpVersion& val) { + gin::Dictionary dict = gin::Dictionary::CreateEmpty(isolate); + dict.Set("major", static_cast(val.major_value())); + dict.Set("minor", static_cast(val.minor_value())); + return ConvertToV8(isolate, dict); +} + +// static +v8::Local Converter::ToV8( + v8::Isolate* isolate, + const net::RedirectInfo& val) { + gin::Dictionary dict = gin::Dictionary::CreateEmpty(isolate); + + dict.Set("statusCode", val.status_code); + dict.Set("newMethod", val.new_method); + dict.Set("newUrl", val.new_url); + dict.Set("newSiteForCookies", val.new_site_for_cookies); + dict.Set("newReferrer", val.new_referrer); + dict.Set("insecureSchemeWasUpgraded", val.insecure_scheme_was_upgraded); + dict.Set("isSignedExchangeFallbackRedirect", + val.is_signed_exchange_fallback_redirect); + + return ConvertToV8(isolate, dict); +} + } // namespace gin namespace electron { diff --git a/shell/common/gin_converters/net_converter.h b/shell/common/gin_converters/net_converter.h index aad273f9cf7aa..117ff6f4a044f 100644 --- a/shell/common/gin_converters/net_converter.h +++ b/shell/common/gin_converters/net_converter.h @@ -5,8 +5,11 @@ #ifndef SHELL_COMMON_GIN_CONVERTERS_NET_CONVERTER_H_ #define SHELL_COMMON_GIN_CONVERTERS_NET_CONVERTER_H_ +#include + #include "base/memory/ref_counted.h" #include "gin/converter.h" +#include "services/network/public/mojom/fetch_api.mojom.h" #include "shell/browser/net/cert_verifier_client.h" namespace base { @@ -20,6 +23,7 @@ class URLRequest; class X509Certificate; class HttpResponseHeaders; struct CertPrincipal; +class HttpVersion; } // namespace net namespace network { @@ -87,6 +91,18 @@ struct Converter { electron::VerifyRequestParams val); }; +template <> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const net::HttpVersion& val); +}; + +template <> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const net::RedirectInfo& val); +}; + } // namespace gin namespace electron { diff --git a/shell/common/gin_helper/function_template.h b/shell/common/gin_helper/function_template.h index 7adbc42422a06..62c5c6c22a217 100644 --- a/shell/common/gin_helper/function_template.h +++ b/shell/common/gin_helper/function_template.h @@ -5,6 +5,8 @@ #ifndef SHELL_COMMON_GIN_HELPER_FUNCTION_TEMPLATE_H_ #define SHELL_COMMON_GIN_HELPER_FUNCTION_TEMPLATE_H_ +#include + #include "base/bind.h" #include "base/callback.h" #include "gin/arguments.h" @@ -199,7 +201,8 @@ class Invoker, ArgTypes...> void DispatchToCallback(base::Callback callback) { v8::MicrotasksScope script_scope(args_->isolate(), v8::MicrotasksScope::kRunMicrotasks); - args_->Return(callback.Run(ArgumentHolder::value...)); + args_->Return( + callback.Run(std::move(ArgumentHolder::value)...)); } // In C++, you can declare the function foo(void), but you can't pass a void @@ -208,7 +211,7 @@ class Invoker, ArgTypes...> void DispatchToCallback(base::Callback callback) { v8::MicrotasksScope script_scope(args_->isolate(), v8::MicrotasksScope::kRunMicrotasks); - callback.Run(ArgumentHolder::value...); + callback.Run(std::move(ArgumentHolder::value)...); } private: diff --git a/shell/common/language_util.h b/shell/common/language_util.h new file mode 100644 index 0000000000000..3e35073c142cc --- /dev/null +++ b/shell/common/language_util.h @@ -0,0 +1,26 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_COMMON_LANGUAGE_UTIL_H_ +#define SHELL_COMMON_LANGUAGE_UTIL_H_ + +#include +#include + +#include "base/strings/string16.h" + +namespace electron { + +// Return a list of user preferred languages from OS. The list doesn't include +// overrides from command line arguments. +std::vector GetPreferredLanguages(); + +#if defined(OS_WIN) +bool GetPreferredLanguagesUsingGlobalization( + std::vector* languages); +#endif + +} // namespace electron + +#endif // SHELL_COMMON_LANGUAGE_UTIL_H_ diff --git a/shell/common/language_util_linux.cc b/shell/common/language_util_linux.cc new file mode 100644 index 0000000000000..76bc557741ba0 --- /dev/null +++ b/shell/common/language_util_linux.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/common/language_util.h" + +#include "ui/base/l10n/l10n_util.h" + +namespace electron { + +std::vector GetPreferredLanguages() { + // Return empty as there's no API to use. You may be able to use + // GetApplicationLocale() of a browser process. + return std::vector{}; +} + +} // namespace electron diff --git a/shell/common/language_util_mac.mm b/shell/common/language_util_mac.mm new file mode 100644 index 0000000000000..205d7087bf1f8 --- /dev/null +++ b/shell/common/language_util_mac.mm @@ -0,0 +1,25 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/common/language_util.h" + +#import +#include +#include + +#include "base/strings/sys_string_conversions.h" + +namespace electron { + +std::vector GetPreferredLanguages() { + __block std::vector languages; + [[NSLocale preferredLanguages] + enumerateObjectsUsingBlock:^(NSString* language, NSUInteger i, + BOOL* stop) { + languages.push_back(base::SysNSStringToUTF8(language)); + }]; + return languages; +} + +} // namespace electron diff --git a/shell/common/language_util_win.cc b/shell/common/language_util_win.cc new file mode 100644 index 0000000000000..d6717b31aec21 --- /dev/null +++ b/shell/common/language_util_win.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2020 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/common/language_util.h" + +#include +#include +#include + +#include "base/strings/sys_string_conversions.h" +#include "base/win/core_winrt_util.h" +#include "base/win/i18n.h" +#include "base/win/win_util.h" +#include "base/win/windows_version.h" + +namespace electron { + +std::vector GetPreferredLanguages() { + std::vector languages16; + + // Attempt to use API available on Windows 10 or later, which + // returns the full list of language preferences. + if (!GetPreferredLanguagesUsingGlobalization(&languages16)) { + base::win::i18n::GetThreadPreferredUILanguageList(&languages16); + } + + std::vector languages; + for (const auto& language : languages16) { + languages.push_back(base::SysWideToUTF8(language)); + } + return languages; +} + +bool GetPreferredLanguagesUsingGlobalization( + std::vector* languages) { + if (base::win::GetVersion() < base::win::Version::WIN10) + return false; + if (!base::win::ResolveCoreWinRTDelayload() || + !base::win::ScopedHString::ResolveCoreWinRTStringDelayload()) + return false; + + base::win::ScopedHString guid = base::win::ScopedHString::Create( + RuntimeClass_Windows_System_UserProfile_GlobalizationPreferences); + Microsoft::WRL::ComPtr< + ABI::Windows::System::UserProfile::IGlobalizationPreferencesStatics> + prefs; + + HRESULT hr = + base::win::RoGetActivationFactory(guid.get(), IID_PPV_ARGS(&prefs)); + if (FAILED(hr)) + return false; + + ABI::Windows::Foundation::Collections::IVectorView* langs; + hr = prefs->get_Languages(&langs); + if (FAILED(hr)) + return false; + + unsigned size; + hr = langs->get_Size(&size); + if (FAILED(hr)) + return false; + + for (unsigned i = 0; i < size; ++i) { + HSTRING hstr; + hr = langs->GetAt(i, &hstr); + if (SUCCEEDED(hr)) { + base::WStringPiece str = base::win::ScopedHString(hstr).Get(); + languages->emplace_back(str.data(), str.size()); + } + } + + return true; +} + +} // namespace electron diff --git a/shell/common/native_mate_converters/blink_converter.cc b/shell/common/native_mate_converters/blink_converter.cc index 4d915809eb69b..3a030bf4d01b9 100644 --- a/shell/common/native_mate_converters/blink_converter.cc +++ b/shell/common/native_mate_converters/blink_converter.cc @@ -19,6 +19,7 @@ #include "shell/common/deprecate_util.h" #include "shell/common/keyboard_util.h" #include "shell/common/native_mate_converters/value_converter.h" +#include "third_party/blink/public/common/context_menu_data/edit_flags.h" #include "third_party/blink/public/platform/web_input_event.h" #include "third_party/blink/public/platform/web_mouse_event.h" #include "third_party/blink/public/platform/web_mouse_wheel_event.h" @@ -297,13 +298,21 @@ bool Converter::FromV8( dict.Get("wheelTicksY", &out->wheel_ticks_y); dict.Get("accelerationRatioX", &out->acceleration_ratio_x); dict.Get("accelerationRatioY", &out->acceleration_ratio_y); - dict.Get("hasPreciseScrollingDeltas", &out->has_precise_scrolling_deltas); + + bool has_precise_scrolling_deltas = false; + dict.Get("hasPreciseScrollingDeltas", &has_precise_scrolling_deltas); + if (has_precise_scrolling_deltas) { + out->delta_units = + ui::input_types::ScrollGranularity::kScrollByPrecisePixel; + } else { + out->delta_units = ui::input_types::ScrollGranularity::kScrollByPixel; + } #if defined(USE_AURA) // Matches the behavior of ui/events/blink/web_input_event_traits.cc: bool can_scroll = true; if (dict.Get("canScroll", &can_scroll) && !can_scroll) { - out->has_precise_scrolling_deltas = false; + out->delta_units = ui::input_types::ScrollGranularity::kScrollByPage; out->SetModifiers(out->GetModifiers() & ~blink::WebInputEvent::kControlKey); } #endif @@ -373,21 +382,21 @@ bool Converter::FromV8( } // static -v8::Local Converter::ToV8( +v8::Local Converter::ToV8( v8::Isolate* isolate, - const blink::WebContextMenuData::MediaType& in) { + const blink::ContextMenuDataMediaType& in) { switch (in) { - case blink::WebContextMenuData::kMediaTypeImage: + case blink::ContextMenuDataMediaType::kImage: return mate::StringToV8(isolate, "image"); - case blink::WebContextMenuData::kMediaTypeVideo: + case blink::ContextMenuDataMediaType::kVideo: return mate::StringToV8(isolate, "video"); - case blink::WebContextMenuData::kMediaTypeAudio: + case blink::ContextMenuDataMediaType::kAudio: return mate::StringToV8(isolate, "audio"); - case blink::WebContextMenuData::kMediaTypeCanvas: + case blink::ContextMenuDataMediaType::kCanvas: return mate::StringToV8(isolate, "canvas"); - case blink::WebContextMenuData::kMediaTypeFile: + case blink::ContextMenuDataMediaType::kFile: return mate::StringToV8(isolate, "file"); - case blink::WebContextMenuData::kMediaTypePlugin: + case blink::ContextMenuDataMediaType::kPlugin: return mate::StringToV8(isolate, "plugin"); default: return mate::StringToV8(isolate, "none"); @@ -395,15 +404,15 @@ v8::Local Converter::ToV8( } // static -v8::Local Converter::ToV8( +v8::Local Converter::ToV8( v8::Isolate* isolate, - const blink::WebContextMenuData::InputFieldType& in) { + const blink::ContextMenuDataInputFieldType& in) { switch (in) { - case blink::WebContextMenuData::kInputFieldTypePlainText: + case blink::ContextMenuDataInputFieldType::kPlainText: return mate::StringToV8(isolate, "plainText"); - case blink::WebContextMenuData::kInputFieldTypePassword: + case blink::ContextMenuDataInputFieldType::kPassword: return mate::StringToV8(isolate, "password"); - case blink::WebContextMenuData::kInputFieldTypeOther: + case blink::ContextMenuDataInputFieldType::kOther: return mate::StringToV8(isolate, "other"); default: return mate::StringToV8(isolate, "none"); @@ -412,13 +421,16 @@ v8::Local Converter::ToV8( v8::Local EditFlagsToV8(v8::Isolate* isolate, int editFlags) { mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate); - dict.Set("canUndo", !!(editFlags & blink::WebContextMenuData::kCanUndo)); - dict.Set("canRedo", !!(editFlags & blink::WebContextMenuData::kCanRedo)); - dict.Set("canCut", !!(editFlags & blink::WebContextMenuData::kCanCut)); - dict.Set("canCopy", !!(editFlags & blink::WebContextMenuData::kCanCopy)); + dict.Set("canUndo", + !!(editFlags & blink::ContextMenuDataEditFlags::kCanUndo)); + dict.Set("canRedo", + !!(editFlags & blink::ContextMenuDataEditFlags::kCanRedo)); + dict.Set("canCut", !!(editFlags & blink::ContextMenuDataEditFlags::kCanCut)); + dict.Set("canCopy", + !!(editFlags & blink::ContextMenuDataEditFlags::kCanCopy)); bool pasteFlag = false; - if (editFlags & blink::WebContextMenuData::kCanPaste) { + if (editFlags & blink::ContextMenuDataEditFlags::kCanPaste) { std::vector types; bool ignore; ui::Clipboard::GetForCurrentThread()->ReadAvailableTypes( @@ -427,9 +439,10 @@ v8::Local EditFlagsToV8(v8::Isolate* isolate, int editFlags) { } dict.Set("canPaste", pasteFlag); - dict.Set("canDelete", !!(editFlags & blink::WebContextMenuData::kCanDelete)); + dict.Set("canDelete", + !!(editFlags & blink::ContextMenuDataEditFlags::kCanDelete)); dict.Set("canSelectAll", - !!(editFlags & blink::WebContextMenuData::kCanSelectAll)); + !!(editFlags & blink::ContextMenuDataEditFlags::kCanSelectAll)); return mate::ConvertToV8(isolate, dict); } diff --git a/shell/common/native_mate_converters/blink_converter.h b/shell/common/native_mate_converters/blink_converter.h index 60d2a323d8e46..2668238aefa21 100644 --- a/shell/common/native_mate_converters/blink_converter.h +++ b/shell/common/native_mate_converters/blink_converter.h @@ -96,17 +96,16 @@ struct Converter { }; template <> -struct Converter { - static v8::Local ToV8( - v8::Isolate* isolate, - const blink::WebContextMenuData::MediaType& in); +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const blink::ContextMenuDataMediaType& in); }; template <> -struct Converter { +struct Converter { static v8::Local ToV8( v8::Isolate* isolate, - const blink::WebContextMenuData::InputFieldType& in); + const blink::ContextMenuDataInputFieldType& in); }; template <> diff --git a/shell/common/native_mate_converters/content_converter.cc b/shell/common/native_mate_converters/content_converter.cc index c07c8014929e9..5a27d2ee5ed05 100644 --- a/shell/common/native_mate_converters/content_converter.cc +++ b/shell/common/native_mate_converters/content_converter.cc @@ -10,7 +10,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/context_menu_params.h" #include "native_mate/dictionary.h" -#include "shell/browser/api/atom_api_web_contents.h" +#include "shell/browser/api/electron_api_web_contents.h" #include "shell/browser/web_contents_permission_helper.h" #include "shell/common/native_mate_converters/blink_converter.h" #include "shell/common/native_mate_converters/callback_converter_deprecated.h" @@ -111,7 +111,7 @@ v8::Local Converter::ToV8( dict.Set("mediaType", params.media_type); dict.Set("mediaFlags", MediaFlagsToV8(isolate, params.media_flags)); bool has_image_contents = - (params.media_type == blink::WebContextMenuData::kMediaTypeImage) && + (params.media_type == blink::ContextMenuDataMediaType::kImage) && params.has_image_contents; dict.Set("hasImageContents", has_image_contents); dict.Set("isEditable", params.is_editable); @@ -119,6 +119,9 @@ v8::Local Converter::ToV8( dict.Set("selectionText", params.selection_text); dict.Set("titleText", params.title_text); dict.Set("misspelledWord", params.misspelled_word); +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + dict.Set("dictionarySuggestions", params.dictionary_suggestions); +#endif dict.Set("frameCharset", params.frame_charset); dict.Set("inputFieldType", params.input_field_type); dict.Set("menuSourceType", params.source_type); diff --git a/shell/common/native_mate_converters/value_converter.cc b/shell/common/native_mate_converters/value_converter.cc index d99b5f28fe4c7..1395375dc8fc9 100644 --- a/shell/common/native_mate_converters/value_converter.cc +++ b/shell/common/native_mate_converters/value_converter.cc @@ -58,7 +58,7 @@ bool Converter::FromV8(v8::Isolate* isolate, electron::V8ValueConverter converter; std::unique_ptr value( converter.FromV8Value(val, isolate->GetCurrentContext())); - if (value->is_list()) { + if (value && value->is_list()) { out->Swap(static_cast(value.get())); return true; } else { diff --git a/shell/common/native_mate_converters/value_converter.h b/shell/common/native_mate_converters/value_converter.h index 5d0495808a0a6..f775395e8bc21 100644 --- a/shell/common/native_mate_converters/value_converter.h +++ b/shell/common/native_mate_converters/value_converter.h @@ -41,10 +41,12 @@ struct Converter> { v8::Local val, base::Optional* out) { if (val->IsNull() || val->IsUndefined()) { + *out = base::nullopt; return true; } T converted; - if (Converter::FromV8(isolate, val, &converted)) { + if (!Converter::FromV8(isolate, val, &converted)) { + *out = base::nullopt; return true; } out->emplace(converted); diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index edf93240b4adc..eb68723edeae0 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -23,7 +24,7 @@ #include "content/public/common/content_paths.h" #include "electron/buildflags/buildflags.h" #include "shell/common/api/locker.h" -#include "shell/common/atom_command_line.h" +#include "shell/common/electron_command_line.h" #include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/event_emitter_caller.h" @@ -122,6 +123,62 @@ void stop_and_close_uv_loop(uv_loop_t* loop) { bool g_is_initialized = false; +bool IsPackagedApp() { + base::FilePath exe_path; + base::PathService::Get(base::FILE_EXE, &exe_path); + base::FilePath::StringType base_name = + base::ToLowerASCII(exe_path.BaseName().value()); + +#if defined(OS_WIN) + return base_name != FILE_PATH_LITERAL("electron.exe"); +#else + return base_name != FILE_PATH_LITERAL("electron"); +#endif +} + +// Initialize NODE_OPTIONS to pass to Node.js +void SetNodeOptions(base::Environment* env) { + // Options that are unilaterally disallowed + const std::set disallowed = { + "--openssl-config", "--use-bundled-ca", "--use-openssl-ca", + "--force-fips", "--enable-fips"}; + + // Subset of options allowed in packaged apps + const std::set allowed_in_packaged = {"--max-http-header-size", + "--http-parser"}; + + if (env->HasVar("NODE_OPTIONS")) { + std::string options; + env->GetVar("NODE_OPTIONS", &options); + std::vector parts = base::SplitString( + options, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + + bool is_packaged_app = IsPackagedApp(); + + for (const auto& part : parts) { + // Strip off values passed to individual NODE_OPTIONs + std::string option = part.substr(0, part.find("=")); + + if (is_packaged_app && + allowed_in_packaged.find(option) == allowed_in_packaged.end()) { + // Explicitly disallow majority of NODE_OPTIONS in packaged apps + LOG(ERROR) << "Most NODE_OPTIONs are not supported in packaged apps." + << " See documentation for more details."; + options.erase(options.find(option), part.length()); + } else if (disallowed.find(option) != disallowed.end()) { + // Remove NODE_OPTIONS specifically disallowed for use in Node.js + // through Electron owing to constraints like BoringSSL. + LOG(ERROR) << "The NODE_OPTION " << option + << " is not supported in Electron"; + options.erase(options.find(option), part.length()); + } + } + + // overwrite new NODE_OPTIONS without unsupported variables + env->SetVar("NODE_OPTIONS", options); + } +} + } // namespace namespace electron { @@ -207,7 +264,7 @@ void NodeBindings::Initialize() { #if defined(OS_LINUX) // Get real command line in renderer process forked by zygote. if (browser_env_ != BrowserEnvironment::BROWSER) - AtomCommandLine::InitializeFromCommandLine(); + ElectronCommandLine::InitializeFromCommandLine(); #endif // Explicitly register electron's builtin modules. @@ -222,55 +279,7 @@ void NodeBindings::Initialize() { const char** exec_argv = nullptr; std::unique_ptr env(base::Environment::Create()); - if (env->HasVar("NODE_OPTIONS")) { - base::FilePath exe_path; - base::PathService::Get(base::FILE_EXE, &exe_path); -#if defined(OS_WIN) - std::string path = base::UTF16ToUTF8(exe_path.value()); -#else - std::string path = exe_path.value(); -#endif - std::transform(path.begin(), path.end(), path.begin(), ::tolower); - -#if defined(OS_WIN) - const bool is_packaged_app = path == "electron.exe"; -#else - const bool is_packaged_app = path == "electron"; -#endif - - // explicitly disallow NODE_OPTIONS in packaged apps - if (is_packaged_app) { - LOG(WARNING) << "NODE_OPTIONs are not supported in packaged apps"; - env->SetVar("NODE_OPTIONS", ""); - } else { - const std::vector disallowed = { - "--openssl-config", "--use-bundled-ca", "--use-openssl-ca", - "--force-fips", "--enable-fips"}; - - std::string options; - env->GetVar("NODE_OPTIONS", &options); - std::vector parts = base::SplitString( - options, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - - // parse passed options for unsupported options - // and remove them from the options list - std::string new_options = options; - for (const auto& disallow : disallowed) { - for (const auto& part : parts) { - if (part.find(disallow) != std::string::npos) { - LOG(WARNING) << "The NODE_OPTION" << disallow - << "is not supported in Electron"; - new_options.erase(new_options.find(part), part.length()); - break; - } - } - } - - // overwrite new NODE_OPTIONS without unsupported variables - if (new_options != options) - env->SetVar("NODE_OPTIONS", new_options); - } - } + SetNodeOptions(env.get()); // TODO(codebytere): this is going to be deprecated in the near future // in favor of Init(std::vector* argv, @@ -293,12 +302,12 @@ node::Environment* NodeBindings::CreateEnvironment( node::MultiIsolatePlatform* platform, bool bootstrap_env) { #if defined(OS_WIN) - auto& atom_args = AtomCommandLine::argv(); + auto& atom_args = ElectronCommandLine::argv(); std::vector args(atom_args.size()); std::transform(atom_args.cbegin(), atom_args.cend(), args.begin(), [](auto& a) { return base::WideToUTF8(a); }); #else - auto args = AtomCommandLine::argv(); + auto args = ElectronCommandLine::argv(); #endif // Feed node the path to initialization script. diff --git a/shell/common/node_bindings.h b/shell/common/node_bindings.h index 97815722444ec..e9aa7ceac5726 100644 --- a/shell/common/node_bindings.h +++ b/shell/common/node_bindings.h @@ -9,7 +9,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" -#include "uv.h" // NOLINT(build/include) +#include "uv.h" // NOLINT(build/include_directory) #include "v8/include/v8.h" namespace base { diff --git a/shell/common/options_switches.cc b/shell/common/options_switches.cc index e409c875f6f95..3056a779156cb 100644 --- a/shell/common/options_switches.cc +++ b/shell/common/options_switches.cc @@ -28,6 +28,7 @@ const char kMaximizable[] = "maximizable"; const char kFullScreenable[] = "fullscreenable"; const char kClosable[] = "closable"; const char kFullscreen[] = "fullscreen"; +const char kTrafficLightPosition[] = "trafficLightPosition"; // Whether the window should show in taskbar. const char kSkipTaskbar[] = "skipTaskbar"; @@ -173,10 +174,16 @@ const char kWebGL[] = "webgl"; // navigation. const char kNavigateOnDragDrop[] = "navigateOnDragDrop"; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +const char kSpellcheck[] = "spellcheck"; +#endif + #if BUILDFLAG(ENABLE_REMOTE_MODULE) const char kEnableRemoteModule[] = "enableRemoteModule"; #endif +const char kEnableWebSQL[] = "enableWebSQL"; + } // namespace options namespace switches { @@ -245,6 +252,10 @@ const char kNodeIntegrationInWorker[] = "node-integration-in-worker"; // environments will be created in sub-frames. const char kNodeIntegrationInSubFrames[] = "node-integration-in-subframes"; +// Command switch passed to render process to control whether WebSQL api +// is allowed. +const char kEnableWebSQL[] = "enable-websql"; + // Widevine options // Path to Widevine CDM binaries. const char kWidevineCdmPath[] = "widevine-cdm-path"; @@ -267,6 +278,10 @@ const char kAuthNegotiateDelegateWhitelist[] = // If set, include the port in generated Kerberos SPNs. const char kEnableAuthNegotiatePort[] = "enable-auth-negotiate-port"; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +const char kEnableSpellcheck[] = "enable-spellcheck"; +#endif + #if BUILDFLAG(ENABLE_REMOTE_MODULE) const char kEnableRemoteModule[] = "enable-remote-module"; #endif diff --git a/shell/common/options_switches.h b/shell/common/options_switches.h index e24dfa2d8f59e..7f4d140d5733c 100644 --- a/shell/common/options_switches.h +++ b/shell/common/options_switches.h @@ -54,6 +54,7 @@ extern const char kOpacity[]; extern const char kFocusable[]; extern const char kWebPreferences[]; extern const char kVibrancyType[]; +extern const char kTrafficLightPosition[]; // WebPreferences. extern const char kZoomFactor[]; @@ -83,6 +84,11 @@ extern const char kImages[]; extern const char kTextAreasAreResizable[]; extern const char kWebGL[]; extern const char kNavigateOnDragDrop[]; +extern const char kEnableWebSQL[]; + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +extern const char kSpellcheck[]; +#endif #if BUILDFLAG(ENABLE_REMOTE_MODULE) extern const char kEnableRemoteModule[]; @@ -124,6 +130,7 @@ extern const char kWebviewTag[]; extern const char kNodeIntegrationInSubFrames[]; extern const char kDisableElectronSiteInstanceOverrides[]; extern const char kEnableNodeLeakageInRenderers[]; +extern const char kEnableWebSQL[]; extern const char kWidevineCdmPath[]; extern const char kWidevineCdmVersion[]; @@ -134,6 +141,10 @@ extern const char kAuthServerWhitelist[]; extern const char kAuthNegotiateDelegateWhitelist[]; extern const char kEnableAuthNegotiatePort[]; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +extern const char kEnableSpellcheck[]; +#endif + #if BUILDFLAG(ENABLE_REMOTE_MODULE) extern const char kEnableRemoteModule[]; #endif diff --git a/shell/common/platform_util_linux.cc b/shell/common/platform_util_linux.cc index 900f90cae1a57..408319e233069 100644 --- a/shell/common/platform_util_linux.cc +++ b/shell/common/platform_util_linux.cc @@ -127,17 +127,7 @@ void Beep() { } bool GetDesktopName(std::string* setme) { - bool found = false; - - std::unique_ptr env(base::Environment::Create()); - std::string desktop_id = libgtkui::GetDesktopName(env.get()); - constexpr char const* libcc_default_id = "chromium-browser.desktop"; - if (!desktop_id.empty() && (desktop_id != libcc_default_id)) { - *setme = desktop_id; - found = true; - } - - return found; + return base::Environment::Create()->GetVar("CHROME_DESKTOP", setme); } } // namespace platform_util diff --git a/shell/common/platform_util_mac.mm b/shell/common/platform_util_mac.mm index f141c4063178c..5f6f03c4a4425 100644 --- a/shell/common/platform_util_mac.mm +++ b/shell/common/platform_util_mac.mm @@ -135,13 +135,15 @@ bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail) { // Handle this by deleting the item as a fallback. if (!did_trash && [err code] == NSFeatureUnsupportedError) { did_trash = [[NSFileManager defaultManager] removeItemAtURL:url - error:nil]; + error:&err]; } } - if (!did_trash) + if (!did_trash) { LOG(WARNING) << "NSWorkspace failed to move file " << full_path.value() - << " to trash"; + << " to trash: " + << base::SysNSStringToUTF8([err localizedDescription]); + } return did_trash; } diff --git a/shell/common/platform_util_win.cc b/shell/common/platform_util_win.cc index 8f5a9dd33052c..3bf0f44e5ca84 100644 --- a/shell/common/platform_util_win.cc +++ b/shell/common/platform_util_win.cc @@ -238,14 +238,11 @@ std::string OpenExternalOnWorkerThread( // Quote the input scheme to be sure that the command does not have // parameters unexpected by the external program. This url should already // have been escaped. - std::string escaped_url = url.spec(); - escaped_url.insert(0, "\""); - escaped_url += "\""; - - std::string working_dir = options.working_dir.AsUTF8Unsafe(); + base::string16 escaped_url = L"\"" + base::UTF8ToUTF16(url.spec()) + L"\""; + base::string16 working_dir = options.working_dir.value(); if (reinterpret_cast( - ShellExecuteA(nullptr, "open", escaped_url.c_str(), nullptr, + ShellExecuteW(nullptr, L"open", escaped_url.c_str(), nullptr, working_dir.empty() ? nullptr : working_dir.c_str(), SW_SHOWNORMAL)) <= 32) { return "Failed to open"; @@ -274,19 +271,15 @@ void ShowItemInFolderOnWorkerThread(const base::FilePath& full_path) { hr = desktop->ParseDisplayName(NULL, NULL, const_cast(dir.value().c_str()), NULL, &dir_item, NULL); - if (FAILED(hr)) { - ui::win::OpenFolderViaShell(dir); + if (FAILED(hr)) return; - } base::win::ScopedCoMem file_item; hr = desktop->ParseDisplayName( NULL, NULL, const_cast(full_path.value().c_str()), NULL, &file_item, NULL); - if (FAILED(hr)) { - ui::win::OpenFolderViaShell(dir); + if (FAILED(hr)) return; - } const ITEMIDLIST* highlight[] = {file_item}; hr = SHOpenFolderAndSelectItems(dir_item, base::size(highlight), highlight, @@ -301,7 +294,6 @@ void ShowItemInFolderOnWorkerThread(const base::FilePath& full_path) { LOG(WARNING) << " " << __func__ << "(): Can't open full_path = \"" << full_path.value() << "\"" << " hr = " << logging::SystemErrorCodeToString(hr); - ui::win::OpenFolderViaShell(dir); } } } diff --git a/shell/renderer/api/atom_api_context_bridge.cc b/shell/renderer/api/atom_api_context_bridge.cc deleted file mode 100644 index 5e1fbb5ac1b6a..0000000000000 --- a/shell/renderer/api/atom_api_context_bridge.cc +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright (c) 2019 Slack Technologies, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "shell/renderer/api/atom_api_context_bridge.h" - -#include -#include -#include - -#include "base/no_destructor.h" -#include "base/strings/string_number_conversions.h" -#include "shell/common/api/remote/object_life_monitor.h" -#include "shell/common/native_mate_converters/blink_converter.h" -#include "shell/common/native_mate_converters/callback_converter_deprecated.h" -#include "shell/common/native_mate_converters/once_callback.h" -#include "shell/common/promise_util.h" - -namespace electron { - -namespace api { - -namespace { - -static int kMaxRecursion = 1000; - -content::RenderFrame* GetRenderFrame(const v8::Local& value) { - v8::Local context = value->CreationContext(); - if (context.IsEmpty()) - return nullptr; - blink::WebLocalFrame* frame = blink::WebLocalFrame::FrameForContext(context); - if (!frame) - return nullptr; - return content::RenderFrame::FromWebFrame(frame); -} - -std::map& -GetStoreMap() { - static base::NoDestructor> - store_map; - return *store_map; -} - -context_bridge::RenderFramePersistenceStore* GetOrCreateStore( - content::RenderFrame* render_frame) { - auto it = GetStoreMap().find(render_frame); - if (it == GetStoreMap().end()) { - auto* store = new context_bridge::RenderFramePersistenceStore(render_frame); - GetStoreMap().emplace(render_frame, store); - return store; - } - return it->second; -} - -// Sourced from "extensions/renderer/v8_schema_registry.cc" -// Recursively freezes every v8 object on |object|. -bool DeepFreeze(const v8::Local& object, - const v8::Local& context, - std::set frozen = std::set()) { - int hash = object->GetIdentityHash(); - if (frozen.find(hash) != frozen.end()) - return true; - frozen.insert(hash); - - v8::Local property_names = - object->GetOwnPropertyNames(context).ToLocalChecked(); - for (uint32_t i = 0; i < property_names->Length(); ++i) { - v8::Local child = - object->Get(context, property_names->Get(context, i).ToLocalChecked()) - .ToLocalChecked(); - if (child->IsObject() && !child->IsTypedArray()) { - if (!DeepFreeze(v8::Local::Cast(child), context, frozen)) - return false; - } - } - return mate::internal::IsTrue( - object->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen)); -} - -bool IsPlainObject(const v8::Local& object) { - if (!object->IsObject()) - return false; - - return !(object->IsNullOrUndefined() || object->IsDate() || - object->IsArgumentsObject() || object->IsBigIntObject() || - object->IsBooleanObject() || object->IsNumberObject() || - object->IsStringObject() || object->IsSymbolObject() || - object->IsNativeError() || object->IsRegExp() || - object->IsPromise() || object->IsMap() || object->IsSet() || - object->IsMapIterator() || object->IsSetIterator() || - object->IsWeakMap() || object->IsWeakSet() || - object->IsArrayBuffer() || object->IsArrayBufferView() || - object->IsArray() || object->IsDataView() || - object->IsSharedArrayBuffer() || object->IsProxy() || - object->IsWebAssemblyCompiledModule() || - object->IsModuleNamespaceObject()); -} - -bool IsPlainArray(const v8::Local& arr) { - if (!arr->IsArray()) - return false; - - return !arr->IsTypedArray(); -} - -class FunctionLifeMonitor final : public ObjectLifeMonitor { - public: - static void BindTo(v8::Isolate* isolate, - v8::Local target, - context_bridge::RenderFramePersistenceStore* store, - size_t func_id) { - new FunctionLifeMonitor(isolate, target, store, func_id); - } - - protected: - FunctionLifeMonitor(v8::Isolate* isolate, - v8::Local target, - context_bridge::RenderFramePersistenceStore* store, - size_t func_id) - : ObjectLifeMonitor(isolate, target), store_(store), func_id_(func_id) {} - ~FunctionLifeMonitor() override = default; - - void RunDestructor() override { store_->functions().erase(func_id_); } - - private: - context_bridge::RenderFramePersistenceStore* store_; - size_t func_id_; -}; - -} // namespace - -template -v8::Local BindRepeatingFunctionToV8( - v8::Isolate* isolate, - const base::RepeatingCallback& val) { - auto translater = - base::BindRepeating(&mate::internal::NativeFunctionInvoker::Go, val); - return mate::internal::CreateFunctionFromTranslater(isolate, translater, - false); -} - -v8::MaybeLocal PassValueToOtherContext( - v8::Local source_context, - v8::Local destination_context, - v8::Local value, - context_bridge::RenderFramePersistenceStore* store, - int recursion_depth) { - if (recursion_depth >= kMaxRecursion) { - v8::Context::Scope source_scope(source_context); - { - source_context->GetIsolate()->ThrowException(v8::Exception::TypeError( - mate::StringToV8(source_context->GetIsolate(), - "Electron contextBridge recursion depth exceeded. " - "Nested objects " - "deeper than 1000 are not supported."))); - return v8::MaybeLocal(); - } - } - // Check Cache - auto cached_value = store->GetCachedProxiedObject(value); - if (!cached_value.IsEmpty()) { - return cached_value; - } - - // Proxy functions and monitor the lifetime in the new context to release - // the global handle at the right time. - if (value->IsFunction()) { - auto func = v8::Local::Cast(value); - v8::Global global_func(source_context->GetIsolate(), func); - v8::Global global_source(source_context->GetIsolate(), - source_context); - - size_t func_id = store->take_func_id(); - store->functions()[func_id] = - std::make_tuple(std::move(global_func), std::move(global_source)); - v8::Context::Scope destination_scope(destination_context); - { - v8::Local proxy_func = BindRepeatingFunctionToV8( - destination_context->GetIsolate(), - base::BindRepeating(&ProxyFunctionWrapper, store, func_id)); - FunctionLifeMonitor::BindTo(destination_context->GetIsolate(), - v8::Local::Cast(proxy_func), - store, func_id); - store->CacheProxiedObject(value, proxy_func); - return v8::MaybeLocal(proxy_func); - } - } - - // Proxy promises as they have a safe and guaranteed memory lifecycle - if (value->IsPromise()) { - v8::Context::Scope destination_scope(destination_context); - { - auto source_promise = v8::Local::Cast(value); - auto* proxied_promise = new util::Promise>( - destination_context->GetIsolate()); - v8::Local proxied_promise_handle = - proxied_promise->GetHandle(); - - auto then_cb = base::BindOnce( - [](util::Promise>* proxied_promise, - v8::Isolate* isolate, - v8::Global global_source_context, - v8::Global global_destination_context, - context_bridge::RenderFramePersistenceStore* store, - v8::Local result) { - auto val = PassValueToOtherContext( - global_source_context.Get(isolate), - global_destination_context.Get(isolate), result, store, 0); - if (!val.IsEmpty()) - proxied_promise->Resolve(val.ToLocalChecked()); - delete proxied_promise; - }, - proxied_promise, destination_context->GetIsolate(), - v8::Global(source_context->GetIsolate(), source_context), - v8::Global(destination_context->GetIsolate(), - destination_context), - store); - auto catch_cb = base::BindOnce( - [](util::Promise>* proxied_promise, - v8::Isolate* isolate, - v8::Global global_source_context, - v8::Global global_destination_context, - context_bridge::RenderFramePersistenceStore* store, - v8::Local result) { - auto val = PassValueToOtherContext( - global_source_context.Get(isolate), - global_destination_context.Get(isolate), result, store, 0); - if (!val.IsEmpty()) - proxied_promise->Reject(val.ToLocalChecked()); - delete proxied_promise; - }, - proxied_promise, destination_context->GetIsolate(), - v8::Global(source_context->GetIsolate(), source_context), - v8::Global(destination_context->GetIsolate(), - destination_context), - store); - - ignore_result(source_promise->Then( - source_context, - v8::Local::Cast( - mate::ConvertToV8(destination_context->GetIsolate(), then_cb)), - v8::Local::Cast( - mate::ConvertToV8(destination_context->GetIsolate(), catch_cb)))); - - store->CacheProxiedObject(value, proxied_promise_handle); - return v8::MaybeLocal(proxied_promise_handle); - } - } - - // Errors aren't serializable currently, we need to pull the message out and - // re-construct in the destination context - if (value->IsNativeError()) { - v8::Context::Scope destination_context_scope(destination_context); - return v8::MaybeLocal(v8::Exception::Error( - v8::Exception::CreateMessage(destination_context->GetIsolate(), value) - ->Get())); - } - - // Manually go through the array and pass each value individually into a new - // array so that functions deep inside arrays get proxied or arrays of - // promises are proxied correctly. - if (IsPlainArray(value)) { - v8::Context::Scope destination_context_scope(destination_context); - { - v8::Local arr = v8::Local::Cast(value); - size_t length = arr->Length(); - v8::Local cloned_arr = - v8::Array::New(destination_context->GetIsolate(), length); - for (size_t i = 0; i < length; i++) { - auto value_for_array = PassValueToOtherContext( - source_context, destination_context, - arr->Get(source_context, i).ToLocalChecked(), store, - recursion_depth + 1); - if (value_for_array.IsEmpty()) - return v8::MaybeLocal(); - - if (!mate::internal::IsTrue( - cloned_arr->Set(destination_context, static_cast(i), - value_for_array.ToLocalChecked()))) { - return v8::MaybeLocal(); - } - } - store->CacheProxiedObject(value, cloned_arr); - return v8::MaybeLocal(cloned_arr); - } - } - - // Proxy all objects - if (IsPlainObject(value)) { - auto object_value = v8::Local::Cast(value); - auto passed_value = - CreateProxyForAPI(object_value, source_context, destination_context, - store, recursion_depth + 1); - if (passed_value.IsEmpty()) - return v8::MaybeLocal(); - return v8::MaybeLocal(passed_value.ToLocalChecked()); - } - - // Serializable objects - blink::CloneableMessage ret; - { - v8::Context::Scope source_context_scope(source_context); - { - // V8 serializer will throw an error if required - if (!mate::ConvertFromV8(source_context->GetIsolate(), value, &ret)) - return v8::MaybeLocal(); - } - } - - v8::Context::Scope destination_context_scope(destination_context); - { - v8::Local cloned_value = - mate::ConvertToV8(destination_context->GetIsolate(), ret); - store->CacheProxiedObject(value, cloned_value); - return v8::MaybeLocal(cloned_value); - } -} - -v8::Local ProxyFunctionWrapper( - context_bridge::RenderFramePersistenceStore* store, - size_t func_id, - mate::Arguments* args) { - // Context the proxy function was called from - v8::Local calling_context = args->isolate()->GetCurrentContext(); - // Context the function was created in - v8::Local func_owning_context = - std::get<1>(store->functions()[func_id]).Get(args->isolate()); - - v8::Context::Scope func_owning_context_scope(func_owning_context); - { - v8::Local func = - (std::get<0>(store->functions()[func_id])).Get(args->isolate()); - - std::vector> original_args; - std::vector> proxied_args; - args->GetRemaining(&original_args); - - for (auto value : original_args) { - auto arg = PassValueToOtherContext(calling_context, func_owning_context, - value, store, 0); - if (arg.IsEmpty()) - return v8::Undefined(args->isolate()); - proxied_args.push_back(arg.ToLocalChecked()); - } - - v8::MaybeLocal maybe_return_value; - bool did_error = false; - std::string error_message; - { - v8::TryCatch try_catch(args->isolate()); - maybe_return_value = func->Call(func_owning_context, func, - proxied_args.size(), proxied_args.data()); - if (try_catch.HasCaught()) { - did_error = true; - auto message = try_catch.Message(); - - if (message.IsEmpty() || - !mate::ConvertFromV8(args->isolate(), message->Get(), - &error_message)) { - error_message = - "An unknown exception occurred in the isolated context, an error " - "occurred but a valid exception was not thrown."; - } - } - } - - if (did_error) { - v8::Context::Scope calling_context_scope(calling_context); - { - args->ThrowError(error_message); - return v8::Local(); - } - } - - if (maybe_return_value.IsEmpty()) - return v8::Undefined(args->isolate()); - - auto ret = - PassValueToOtherContext(func_owning_context, calling_context, - maybe_return_value.ToLocalChecked(), store, 0); - if (ret.IsEmpty()) - return v8::Undefined(args->isolate()); - return ret.ToLocalChecked(); - } -} - -v8::MaybeLocal CreateProxyForAPI( - const v8::Local& api_object, - const v8::Local& source_context, - const v8::Local& destination_context, - context_bridge::RenderFramePersistenceStore* store, - int recursion_depth) { - mate::Dictionary api(source_context->GetIsolate(), api_object); - mate::Dictionary proxy = - mate::Dictionary::CreateEmpty(destination_context->GetIsolate()); - store->CacheProxiedObject(api.GetHandle(), proxy.GetHandle()); - auto maybe_keys = api.GetHandle()->GetOwnPropertyNames( - source_context, - static_cast(v8::ONLY_ENUMERABLE | v8::SKIP_SYMBOLS), - v8::KeyConversionMode::kConvertToString); - if (maybe_keys.IsEmpty()) - return v8::MaybeLocal(proxy.GetHandle()); - auto keys = maybe_keys.ToLocalChecked(); - - v8::Context::Scope destination_context_scope(destination_context); - { - uint32_t length = keys->Length(); - std::string key_str; - for (uint32_t i = 0; i < length; i++) { - v8::Local key = - keys->Get(destination_context, i).ToLocalChecked(); - // Try get the key as a string - if (!mate::ConvertFromV8(api.isolate(), key, &key_str)) { - continue; - } - v8::Local value; - if (!api.Get(key_str, &value)) - continue; - - auto passed_value = - PassValueToOtherContext(source_context, destination_context, value, - store, recursion_depth + 1); - if (passed_value.IsEmpty()) - return v8::MaybeLocal(); - proxy.Set(key_str, passed_value.ToLocalChecked()); - } - - return proxy.GetHandle(); - } -} - -#ifdef DCHECK_IS_ON -mate::Dictionary DebugGC(mate::Dictionary empty) { - auto* render_frame = GetRenderFrame(empty.GetHandle()); - auto* store = GetOrCreateStore(render_frame); - mate::Dictionary ret = mate::Dictionary::CreateEmpty(empty.isolate()); - ret.Set("functionCount", store->functions().size()); - auto* proxy_map = store->proxy_map(); - ret.Set("objectCount", proxy_map->size() * 2); - int live_from = 0; - int live_proxy = 0; - for (auto iter = proxy_map->begin(); iter != proxy_map->end(); iter++) { - auto* node = iter->second; - while (node) { - if (!std::get<0>(node->pair).IsEmpty()) - live_from++; - if (!std::get<1>(node->pair).IsEmpty()) - live_proxy++; - node = node->next; - } - } - ret.Set("liveFromValues", live_from); - ret.Set("liveProxyValues", live_proxy); - return ret; -} -#endif - -void ExposeAPIInMainWorld(const std::string& key, - v8::Local api_object, - mate::Arguments* args) { - auto* render_frame = GetRenderFrame(api_object); - CHECK(render_frame); - context_bridge::RenderFramePersistenceStore* store = - GetOrCreateStore(render_frame); - auto* frame = render_frame->GetWebFrame(); - CHECK(frame); - v8::Local main_context = frame->MainWorldScriptContext(); - mate::Dictionary global(main_context->GetIsolate(), main_context->Global()); - - if (global.Has(key)) { - args->ThrowError( - "Cannot bind an API on top of an existing property on the window " - "object"); - return; - } - - v8::Local isolated_context = - frame->WorldScriptContext(args->isolate(), World::ISOLATED_WORLD); - - v8::Context::Scope main_context_scope(main_context); - { - v8::MaybeLocal maybe_proxy = - CreateProxyForAPI(api_object, isolated_context, main_context, store, 0); - if (maybe_proxy.IsEmpty()) - return; - auto proxy = maybe_proxy.ToLocalChecked(); - if (!DeepFreeze(proxy, main_context)) - return; - - global.SetReadOnlyNonConfigurable(key, proxy); - } -} - -} // namespace api - -} // namespace electron - -namespace { - -void Initialize(v8::Local exports, - v8::Local unused, - v8::Local context, - void* priv) { - v8::Isolate* isolate = context->GetIsolate(); - mate::Dictionary dict(isolate, exports); - dict.SetMethod("exposeAPIInMainWorld", &electron::api::ExposeAPIInMainWorld); -#ifdef DCHECK_IS_ON - dict.SetMethod("_debugGCMaps", &electron::api::DebugGC); -#endif -} - -} // namespace - -NODE_LINKED_MODULE_CONTEXT_AWARE(atom_renderer_context_bridge, Initialize) diff --git a/shell/renderer/api/atom_api_context_bridge.h b/shell/renderer/api/atom_api_context_bridge.h deleted file mode 100644 index a855e68f6eede..0000000000000 --- a/shell/renderer/api/atom_api_context_bridge.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2019 Slack Technologies, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef SHELL_RENDERER_API_ATOM_API_CONTEXT_BRIDGE_H_ -#define SHELL_RENDERER_API_ATOM_API_CONTEXT_BRIDGE_H_ - -#include -#include -#include - -#include "content/public/renderer/render_frame.h" -#include "content/public/renderer/render_frame_observer.h" -#include "native_mate/converter.h" -#include "native_mate/dictionary.h" -#include "shell/common/node_includes.h" -#include "shell/renderer/api/context_bridge/render_frame_context_bridge_store.h" -#include "shell/renderer/atom_render_frame_observer.h" -#include "third_party/blink/public/web/web_local_frame.h" - -namespace electron { - -namespace api { - -v8::Local ProxyFunctionWrapper( - context_bridge::RenderFramePersistenceStore* store, - size_t func_id, - mate::Arguments* args); - -v8::MaybeLocal CreateProxyForAPI( - const v8::Local& api_object, - const v8::Local& source_context, - const v8::Local& target_context, - context_bridge::RenderFramePersistenceStore* store, - int recursion_depth); - -} // namespace api - -} // namespace electron - -#endif // SHELL_RENDERER_API_ATOM_API_CONTEXT_BRIDGE_H_ diff --git a/shell/renderer/api/atom_api_renderer_ipc.cc b/shell/renderer/api/atom_api_renderer_ipc.cc deleted file mode 100644 index 3ed4097e7a19e..0000000000000 --- a/shell/renderer/api/atom_api_renderer_ipc.cc +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include - -#include "base/task/post_task.h" -#include "base/values.h" -#include "content/public/renderer/render_frame.h" -#include "gin/dictionary.h" -#include "gin/handle.h" -#include "gin/object_template_builder.h" -#include "gin/wrappable.h" -#include "services/service_manager/public/cpp/interface_provider.h" -#include "shell/common/api/api.mojom.h" -#include "shell/common/gin_converters/blink_converter_gin_adapter.h" -#include "shell/common/gin_converters/value_converter_gin_adapter.h" -#include "shell/common/node_bindings.h" -#include "shell/common/node_includes.h" -#include "shell/common/promise_util.h" -#include "third_party/blink/public/web/web_local_frame.h" - -using blink::WebLocalFrame; -using content::RenderFrame; - -namespace { - -RenderFrame* GetCurrentRenderFrame() { - WebLocalFrame* frame = WebLocalFrame::FrameForCurrentContext(); - if (!frame) - return nullptr; - - return RenderFrame::FromWebFrame(frame); -} - -class IPCRenderer : public gin::Wrappable { - public: - static gin::WrapperInfo kWrapperInfo; - - static gin::Handle Create(v8::Isolate* isolate) { - return gin::CreateHandle(isolate, new IPCRenderer(isolate)); - } - - explicit IPCRenderer(v8::Isolate* isolate) - : task_runner_(base::CreateSingleThreadTaskRunner({base::ThreadPool()})) { - RenderFrame* render_frame = GetCurrentRenderFrame(); - DCHECK(render_frame); - - // Bind the interface on the background runner. All accesses will be via - // the thread-safe pointer. This is to support our "fake-sync" - // MessageSync() hack; see the comment in IPCRenderer::SendSync. - electron::mojom::ElectronBrowserPtrInfo info; - render_frame->GetRemoteInterfaces()->GetInterface(mojo::MakeRequest(&info)); - electron_browser_ptr_ = - electron::mojom::ThreadSafeElectronBrowserPtr::Create(std::move(info), - task_runner_); - } - - // gin::Wrappable: - gin::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override { - return gin::Wrappable::GetObjectTemplateBuilder(isolate) - .SetMethod("send", &IPCRenderer::Send) - .SetMethod("sendSync", &IPCRenderer::SendSync) - .SetMethod("sendTo", &IPCRenderer::SendTo) - .SetMethod("sendToHost", &IPCRenderer::SendToHost) - .SetMethod("invoke", &IPCRenderer::Invoke); - } - - const char* GetTypeName() override { return "IPCRenderer"; } - - private: - void Send(v8::Isolate* isolate, - bool internal, - const std::string& channel, - v8::Local arguments) { - blink::CloneableMessage message; - if (!mate::ConvertFromV8(isolate, arguments, &message)) { - return; - } - electron_browser_ptr_->get()->Message(internal, channel, - std::move(message)); - } - - v8::Local Invoke(v8::Isolate* isolate, - bool internal, - const std::string& channel, - v8::Local arguments) { - blink::CloneableMessage message; - if (!mate::ConvertFromV8(isolate, arguments, &message)) { - return v8::Local(); - } - electron::util::Promise p(isolate); - auto handle = p.GetHandle(); - - electron_browser_ptr_->get()->Invoke( - internal, channel, std::move(message), - base::BindOnce( - [](electron::util::Promise p, - blink::CloneableMessage result) { p.ResolveWithGin(result); }, - std::move(p))); - - return handle; - } - - void SendTo(v8::Isolate* isolate, - bool internal, - bool send_to_all, - int32_t web_contents_id, - const std::string& channel, - v8::Local arguments) { - blink::CloneableMessage message; - if (!mate::ConvertFromV8(isolate, arguments, &message)) { - return; - } - electron_browser_ptr_->get()->MessageTo( - internal, send_to_all, web_contents_id, channel, std::move(message)); - } - - void SendToHost(v8::Isolate* isolate, - const std::string& channel, - v8::Local arguments) { - blink::CloneableMessage message; - if (!mate::ConvertFromV8(isolate, arguments, &message)) { - return; - } - electron_browser_ptr_->get()->MessageHost(channel, std::move(message)); - } - - blink::CloneableMessage SendSync(v8::Isolate* isolate, - bool internal, - const std::string& channel, - v8::Local arguments) { - blink::CloneableMessage message; - if (!mate::ConvertFromV8(isolate, arguments, &message)) { - return blink::CloneableMessage(); - } - // We aren't using a true synchronous mojo call here. We're calling an - // asynchronous method and blocking on the result. The reason we're doing - // this is a little complicated, so buckle up. - // - // Mojo has a concept of synchronous calls. However, synchronous calls are - // dangerous. In particular, it's quite possible for two processes to call - // synchronous methods on each other and cause a deadlock. Mojo has a - // mechanism to avoid this kind of deadlock: if a process is waiting on the - // result of a synchronous call, and it receives an incoming call for a - // synchronous method, it will process that request immediately, even - // though it's currently blocking. However, if it receives an incoming - // request for an _asynchronous_ method, that can't cause a deadlock, so it - // stashes the request on a queue to be processed once the synchronous - // thing it's waiting on returns. - // - // This behavior is useful for preventing deadlocks, but it is inconvenient - // here because it can result in messages being reordered. If the main - // process is awaiting the result of a synchronous call (which it does only - // very rarely, since it's bad to block the main process), and we send - // first an asynchronous message to the main process, followed by a - // synchronous message, then the main process will process the synchronous - // one first. - // - // It turns out, Electron has some dependency on message ordering, - // especially during window shutdown, and getting messages out of order can - // result in, for example, remote objects disappearing unexpectedly. To - // avoid these issues and guarantee consistent message ordering, we send - // all messages to the main process as asynchronous messages. This causes - // them to always be queued and processed in the same order they were - // received, even if they were received while the main process was waiting - // on a synchronous call. - // - // However, in the calling process, we still need to block on the result, - // because the caller is expecting a result synchronously. So we do a bit - // of a trick: we pass the Mojo handle over to a worker thread, send the - // asynchronous message from that thread, and then block on the result. - // It's important that we pass the handle over to the worker thread, - // because that allows Mojo to process incoming messages (most importantly, - // the response to our request) on that thread. If we didn't pass it to a - // worker thread, and instead sent the call from the main thread, we would - // never receive a response because Mojo wouldn't be able to run its - // message handling code, because the main thread would be tied up blocking - // on the WaitableEvent. - // - // Phew. If you got this far, here's a gold star: ⭐️ - - blink::CloneableMessage result; - - // A task is posted to a worker thread to execute the request so that - // this thread may block on a waitable event. It is safe to pass raw - // pointers to |result| and |response_received_event| as this stack frame - // will survive until the request is complete. - - base::WaitableEvent response_received_event; - task_runner_->PostTask( - FROM_HERE, base::BindOnce(&IPCRenderer::SendMessageSyncOnWorkerThread, - base::Unretained(this), - base::Unretained(&response_received_event), - base::Unretained(&result), internal, channel, - std::move(message))); - response_received_event.Wait(); - return result; - } - - void SendMessageSyncOnWorkerThread(base::WaitableEvent* event, - blink::CloneableMessage* result, - bool internal, - const std::string& channel, - blink::CloneableMessage arguments) { - electron_browser_ptr_->get()->MessageSync( - internal, channel, std::move(arguments), - base::BindOnce(&IPCRenderer::ReturnSyncResponseToMainThread, - base::Unretained(event), base::Unretained(result))); - } - - static void ReturnSyncResponseToMainThread(base::WaitableEvent* event, - blink::CloneableMessage* result, - blink::CloneableMessage response) { - *result = std::move(response); - event->Signal(); - } - - scoped_refptr task_runner_; - scoped_refptr - electron_browser_ptr_; -}; - -gin::WrapperInfo IPCRenderer::kWrapperInfo = {gin::kEmbedderNativeGin}; - -void Initialize(v8::Local exports, - v8::Local unused, - v8::Local context, - void* priv) { - gin::Dictionary dict(context->GetIsolate(), exports); - dict.Set("ipc", IPCRenderer::Create(context->GetIsolate())); -} - -} // namespace - -NODE_LINKED_MODULE_CONTEXT_AWARE(atom_renderer_ipc, Initialize) diff --git a/shell/renderer/api/context_bridge/object_cache.cc b/shell/renderer/api/context_bridge/object_cache.cc new file mode 100644 index 0000000000000..fd060324d41a2 --- /dev/null +++ b/shell/renderer/api/context_bridge/object_cache.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2020 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/renderer/api/context_bridge/object_cache.h" + +#include + +#include "shell/common/api/remote/object_life_monitor.h" + +namespace electron { + +namespace api { + +namespace context_bridge { + +ObjectCachePairNode::ObjectCachePairNode(ObjectCachePair&& pair) { + this->pair = std::move(pair); +} + +ObjectCachePairNode::~ObjectCachePairNode() = default; + +ObjectCache::ObjectCache() {} +ObjectCache::~ObjectCache() = default; + +void ObjectCache::CacheProxiedObject(v8::Local from, + v8::Local proxy_value) { + if (from->IsObject() && !from->IsNullOrUndefined()) { + auto obj = v8::Local::Cast(from); + int hash = obj->GetIdentityHash(); + + auto* node = new ObjectCachePairNode(std::make_pair(from, proxy_value)); + proxy_map_[hash].Append(node); + } +} + +v8::MaybeLocal ObjectCache::GetCachedProxiedObject( + v8::Local from) const { + if (!from->IsObject() || from->IsNullOrUndefined()) + return v8::MaybeLocal(); + + auto obj = v8::Local::Cast(from); + int hash = obj->GetIdentityHash(); + auto iter = proxy_map_.find(hash); + if (iter == proxy_map_.end()) + return v8::MaybeLocal(); + + auto& list = iter->second; + for (auto* node = list.head(); node != list.end(); node = node->next()) { + auto& pair = node->value()->pair; + auto from_cmp = pair.first; + if (from_cmp == from) { + if (pair.second.IsEmpty()) + return v8::MaybeLocal(); + return pair.second; + } + } + return v8::MaybeLocal(); +} + +} // namespace context_bridge + +} // namespace api + +} // namespace electron diff --git a/shell/renderer/api/context_bridge/object_cache.h b/shell/renderer/api/context_bridge/object_cache.h new file mode 100644 index 0000000000000..ff420f7368439 --- /dev/null +++ b/shell/renderer/api/context_bridge/object_cache.h @@ -0,0 +1,53 @@ +// Copyright (c) 2020 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_RENDERER_API_CONTEXT_BRIDGE_OBJECT_CACHE_H_ +#define SHELL_RENDERER_API_CONTEXT_BRIDGE_OBJECT_CACHE_H_ + +#include +#include + +#include "base/containers/linked_list.h" +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_frame_observer.h" +#include "shell/renderer/electron_render_frame_observer.h" +#include "third_party/blink/public/web/web_local_frame.h" + +namespace electron { + +namespace api { + +namespace context_bridge { + +using ObjectCachePair = std::pair, v8::Local>; + +struct ObjectCachePairNode : public base::LinkNode { + explicit ObjectCachePairNode(ObjectCachePair&& pair); + ~ObjectCachePairNode(); + + ObjectCachePair pair; +}; + +class ObjectCache final { + public: + ObjectCache(); + ~ObjectCache(); + + void CacheProxiedObject(v8::Local from, + v8::Local proxy_value); + v8::MaybeLocal GetCachedProxiedObject( + v8::Local from) const; + + private: + // object_identity ==> [from_value, proxy_value] + std::map> proxy_map_; +}; + +} // namespace context_bridge + +} // namespace api + +} // namespace electron + +#endif // SHELL_RENDERER_API_CONTEXT_BRIDGE_OBJECT_CACHE_H_ diff --git a/shell/renderer/api/context_bridge/render_frame_context_bridge_store.cc b/shell/renderer/api/context_bridge/render_frame_context_bridge_store.cc deleted file mode 100644 index 06f6e34789be7..0000000000000 --- a/shell/renderer/api/context_bridge/render_frame_context_bridge_store.cc +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2019 Slack Technologies, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "shell/renderer/api/context_bridge/render_frame_context_bridge_store.h" - -#include - -#include "shell/common/api/remote/object_life_monitor.h" - -namespace electron { - -namespace api { - -namespace context_bridge { - -namespace { - -class CachedProxyLifeMonitor final : public ObjectLifeMonitor { - public: - static void BindTo(v8::Isolate* isolate, - v8::Local target, - RenderFramePersistenceStore* store, - WeakGlobalPairNode* node, - int hash) { - new CachedProxyLifeMonitor(isolate, target, store, node, hash); - } - - protected: - CachedProxyLifeMonitor(v8::Isolate* isolate, - v8::Local target, - RenderFramePersistenceStore* store, - WeakGlobalPairNode* node, - int hash) - : ObjectLifeMonitor(isolate, target), - store_(store), - node_(node), - hash_(hash) {} - - void RunDestructor() override { - if (node_->detached) { - delete node_; - } - if (node_->prev) { - node_->prev->next = node_->next; - } - if (node_->next) { - node_->next->prev = node_->prev; - } - if (!node_->prev && !node_->next) { - // Must be a single length linked list - store_->proxy_map()->erase(hash_); - } - node_->detached = true; - } - - private: - RenderFramePersistenceStore* store_; - WeakGlobalPairNode* node_; - int hash_; -}; - -} // namespace - -WeakGlobalPairNode::WeakGlobalPairNode(WeakGlobalPair pair) { - this->pair = std::move(pair); -} - -WeakGlobalPairNode::~WeakGlobalPairNode() { - if (next) { - delete next; - } -} - -RenderFramePersistenceStore::RenderFramePersistenceStore( - content::RenderFrame* render_frame) - : content::RenderFrameObserver(render_frame) {} - -RenderFramePersistenceStore::~RenderFramePersistenceStore() = default; - -void RenderFramePersistenceStore::OnDestruct() { - delete this; -} - -void RenderFramePersistenceStore::CacheProxiedObject( - v8::Local from, - v8::Local proxy_value) { - if (from->IsObject() && !from->IsNullOrUndefined()) { - auto obj = v8::Local::Cast(from); - int hash = obj->GetIdentityHash(); - auto global_from = v8::Global(v8::Isolate::GetCurrent(), from); - auto global_proxy = - v8::Global(v8::Isolate::GetCurrent(), proxy_value); - // Do not retain - global_from.SetWeak(); - global_proxy.SetWeak(); - auto iter = proxy_map_.find(hash); - auto* node = new WeakGlobalPairNode( - std::make_tuple(std::move(global_from), std::move(global_proxy))); - CachedProxyLifeMonitor::BindTo(v8::Isolate::GetCurrent(), obj, this, node, - hash); - CachedProxyLifeMonitor::BindTo(v8::Isolate::GetCurrent(), - v8::Local::Cast(proxy_value), - this, node, hash); - if (iter == proxy_map_.end()) { - proxy_map_.emplace(hash, node); - } else { - WeakGlobalPairNode* target = iter->second; - while (target->next) { - target = target->next; - } - target->next = node; - node->prev = target; - } - } -} - -v8::MaybeLocal RenderFramePersistenceStore::GetCachedProxiedObject( - v8::Local from) { - if (!from->IsObject() || from->IsNullOrUndefined()) - return v8::MaybeLocal(); - - auto obj = v8::Local::Cast(from); - int hash = obj->GetIdentityHash(); - auto iter = proxy_map_.find(hash); - if (iter == proxy_map_.end()) - return v8::MaybeLocal(); - WeakGlobalPairNode* target = iter->second; - while (target) { - auto from_cmp = std::get<0>(target->pair).Get(v8::Isolate::GetCurrent()); - if (from_cmp == from) { - if (std::get<1>(target->pair).IsEmpty()) - return v8::MaybeLocal(); - return std::get<1>(target->pair).Get(v8::Isolate::GetCurrent()); - } - target = target->next; - } - return v8::MaybeLocal(); -} - -} // namespace context_bridge - -} // namespace api - -} // namespace electron diff --git a/shell/renderer/api/context_bridge/render_frame_context_bridge_store.h b/shell/renderer/api/context_bridge/render_frame_context_bridge_store.h deleted file mode 100644 index 892837b6d83a3..0000000000000 --- a/shell/renderer/api/context_bridge/render_frame_context_bridge_store.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2019 Slack Technologies, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef SHELL_RENDERER_API_CONTEXT_BRIDGE_RENDER_FRAME_CONTEXT_BRIDGE_STORE_H_ -#define SHELL_RENDERER_API_CONTEXT_BRIDGE_RENDER_FRAME_CONTEXT_BRIDGE_STORE_H_ - -#include -#include - -#include "content/public/renderer/render_frame.h" -#include "content/public/renderer/render_frame_observer.h" -#include "shell/renderer/atom_render_frame_observer.h" -#include "third_party/blink/public/web/web_local_frame.h" - -namespace electron { - -namespace api { - -namespace context_bridge { - -using FunctionContextPair = - std::tuple, v8::Global>; - -using WeakGlobalPair = std::tuple, v8::Global>; - -struct WeakGlobalPairNode { - explicit WeakGlobalPairNode(WeakGlobalPair pair_); - ~WeakGlobalPairNode(); - WeakGlobalPair pair; - bool detached = false; - struct WeakGlobalPairNode* prev = nullptr; - struct WeakGlobalPairNode* next = nullptr; -}; - -class RenderFramePersistenceStore final : public content::RenderFrameObserver { - public: - explicit RenderFramePersistenceStore(content::RenderFrame* render_frame); - ~RenderFramePersistenceStore() override; - - // RenderFrameObserver implementation. - void OnDestruct() override; - - size_t take_func_id() { return next_func_id_++; } - - std::map& functions() { return functions_; } - std::map* proxy_map() { return &proxy_map_; } - - void CacheProxiedObject(v8::Local from, - v8::Local proxy_value); - v8::MaybeLocal GetCachedProxiedObject(v8::Local from); - - private: - // func_id ==> { function, owning_context } - std::map functions_; - size_t next_func_id_ = 1; - - // proxy maps are weak globals, i.e. these are not retained beyond - // there normal JS lifetime. You must check IsEmpty() - - // object_identity ==> [from_value, proxy_value] - std::map proxy_map_; -}; - -} // namespace context_bridge - -} // namespace api - -} // namespace electron - -#endif // SHELL_RENDERER_API_CONTEXT_BRIDGE_RENDER_FRAME_CONTEXT_BRIDGE_STORE_H_ diff --git a/shell/renderer/api/electron_api_context_bridge.cc b/shell/renderer/api/electron_api_context_bridge.cc new file mode 100644 index 0000000000000..469ae5899a9da --- /dev/null +++ b/shell/renderer/api/electron_api_context_bridge.cc @@ -0,0 +1,670 @@ +// Copyright (c) 2019 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/renderer/api/electron_api_context_bridge.h" + +#include +#include +#include +#include +#include + +#include "base/no_destructor.h" +#include "base/strings/string_number_conversions.h" +#include "shell/common/api/remote/object_life_monitor.h" +#include "shell/common/native_mate_converters/blink_converter.h" +#include "shell/common/native_mate_converters/callback_converter_deprecated.h" +#include "shell/common/native_mate_converters/once_callback.h" +#include "shell/common/node_includes.h" +#include "shell/common/promise_util.h" +#include "third_party/blink/public/web/web_local_frame.h" + +namespace electron { + +namespace api { + +namespace context_bridge { + +const char* const kProxyFunctionPrivateKey = "electron_contextBridge_proxy_fn"; +const char* const kSupportsDynamicPropertiesPrivateKey = + "electron_contextBridge_supportsDynamicProperties"; + +} // namespace context_bridge + +namespace { + +static int kMaxRecursion = 1000; + +content::RenderFrame* GetRenderFrame(const v8::Local& value) { + v8::Local context = value->CreationContext(); + if (context.IsEmpty()) + return nullptr; + blink::WebLocalFrame* frame = blink::WebLocalFrame::FrameForContext(context); + if (!frame) + return nullptr; + return content::RenderFrame::FromWebFrame(frame); +} + +// Sourced from "extensions/renderer/v8_schema_registry.cc" +// Recursively freezes every v8 object on |object|. +bool DeepFreeze(const v8::Local& object, + const v8::Local& context, + std::set frozen = std::set()) { + int hash = object->GetIdentityHash(); + if (frozen.find(hash) != frozen.end()) + return true; + frozen.insert(hash); + + v8::Local property_names = + object->GetOwnPropertyNames(context).ToLocalChecked(); + for (uint32_t i = 0; i < property_names->Length(); ++i) { + v8::Local child = + object->Get(context, property_names->Get(context, i).ToLocalChecked()) + .ToLocalChecked(); + if (child->IsObject() && !child->IsTypedArray()) { + if (!DeepFreeze(v8::Local::Cast(child), context, frozen)) + return false; + } + } + return mate::internal::IsTrue( + object->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen)); +} + +bool IsPlainObject(const v8::Local& object) { + if (!object->IsObject()) + return false; + + return !(object->IsNullOrUndefined() || object->IsDate() || + object->IsArgumentsObject() || object->IsBigIntObject() || + object->IsBooleanObject() || object->IsNumberObject() || + object->IsStringObject() || object->IsSymbolObject() || + object->IsNativeError() || object->IsRegExp() || + object->IsPromise() || object->IsMap() || object->IsSet() || + object->IsMapIterator() || object->IsSetIterator() || + object->IsWeakMap() || object->IsWeakSet() || + object->IsArrayBuffer() || object->IsArrayBufferView() || + object->IsArray() || object->IsDataView() || + object->IsSharedArrayBuffer() || object->IsProxy() || + object->IsWebAssemblyCompiledModule() || + object->IsModuleNamespaceObject()); +} + +bool IsPlainArray(const v8::Local& arr) { + if (!arr->IsArray()) + return false; + + return !arr->IsTypedArray(); +} + +void SetPrivate(v8::Local context, + v8::Local target, + const std::string& key, + v8::Local value) { + target + ->SetPrivate( + context, + v8::Private::ForApi(context->GetIsolate(), + gin::StringToV8(context->GetIsolate(), key)), + value) + .Check(); +} + +v8::MaybeLocal GetPrivate(v8::Local context, + v8::Local target, + const std::string& key) { + return target->GetPrivate( + context, + v8::Private::ForApi(context->GetIsolate(), + gin::StringToV8(context->GetIsolate(), key))); +} + +} // namespace + +template +v8::Local BindRepeatingFunctionToV8( + v8::Isolate* isolate, + const base::RepeatingCallback& val) { + auto translater = + base::BindRepeating(&mate::internal::NativeFunctionInvoker::Go, val); + return mate::internal::CreateFunctionFromTranslater(isolate, translater, + false); +} + +v8::MaybeLocal PassValueToOtherContext( + v8::Local source_context, + v8::Local destination_context, + v8::Local value, + context_bridge::ObjectCache* object_cache, + bool support_dynamic_properties, + int recursion_depth) { + if (recursion_depth >= kMaxRecursion) { + v8::Context::Scope source_scope(source_context); + { + source_context->GetIsolate()->ThrowException(v8::Exception::TypeError( + mate::StringToV8(source_context->GetIsolate(), + "Electron contextBridge recursion depth exceeded. " + "Nested objects " + "deeper than 1000 are not supported."))); + return v8::MaybeLocal(); + } + } + // Check Cache + auto cached_value = object_cache->GetCachedProxiedObject(value); + if (!cached_value.IsEmpty()) { + return cached_value; + } + + // Proxy functions and monitor the lifetime in the new context to release + // the global handle at the right time. + if (value->IsFunction()) { + auto func = v8::Local::Cast(value); + + { + v8::Context::Scope destination_scope(destination_context); + v8::Local state = + v8::Object::New(destination_context->GetIsolate()); + SetPrivate(destination_context, state, + context_bridge::kProxyFunctionPrivateKey, func); + SetPrivate(destination_context, state, + context_bridge::kSupportsDynamicPropertiesPrivateKey, + gin::ConvertToV8(destination_context->GetIsolate(), + support_dynamic_properties)); + v8::Local proxy_func; + if (!v8::Function::New(destination_context, ProxyFunctionWrapper, state) + .ToLocal(&proxy_func)) + return v8::MaybeLocal(); + object_cache->CacheProxiedObject(value, proxy_func); + return v8::MaybeLocal(proxy_func); + } + } + + // Proxy promises as they have a safe and guaranteed memory lifecycle + if (value->IsPromise()) { + v8::Context::Scope destination_scope(destination_context); + { + auto source_promise = v8::Local::Cast(value); + // Make the promise a shared_ptr so that when the original promise is + // freed the proxy promise is correctly freed as well instead of being + // left dangling + std::shared_ptr>> proxied_promise( + new util::Promise>( + destination_context->GetIsolate())); + v8::Local proxied_promise_handle = + proxied_promise->GetHandle(); + + v8::Global global_then_source_context( + source_context->GetIsolate(), source_context); + v8::Global global_then_destination_context( + destination_context->GetIsolate(), destination_context); + global_then_source_context.SetWeak(); + global_then_destination_context.SetWeak(); + auto then_cb = base::BindOnce( + [](std::shared_ptr>> + proxied_promise, + v8::Isolate* isolate, + v8::Global global_source_context, + v8::Global global_destination_context, + v8::Local result) { + if (global_source_context.IsEmpty() || + global_destination_context.IsEmpty()) + return; + context_bridge::ObjectCache object_cache; + auto val = + PassValueToOtherContext(global_source_context.Get(isolate), + global_destination_context.Get(isolate), + result, &object_cache, false, 0); + if (!val.IsEmpty()) + proxied_promise->Resolve(val.ToLocalChecked()); + }, + proxied_promise, destination_context->GetIsolate(), + std::move(global_then_source_context), + std::move(global_then_destination_context)); + + v8::Global global_catch_source_context( + source_context->GetIsolate(), source_context); + v8::Global global_catch_destination_context( + destination_context->GetIsolate(), destination_context); + global_catch_source_context.SetWeak(); + global_catch_destination_context.SetWeak(); + auto catch_cb = base::BindOnce( + [](std::shared_ptr>> + proxied_promise, + v8::Isolate* isolate, + v8::Global global_source_context, + v8::Global global_destination_context, + v8::Local result) { + if (global_source_context.IsEmpty() || + global_destination_context.IsEmpty()) + return; + context_bridge::ObjectCache object_cache; + auto val = + PassValueToOtherContext(global_source_context.Get(isolate), + global_destination_context.Get(isolate), + result, &object_cache, false, 0); + if (!val.IsEmpty()) + proxied_promise->Reject(val.ToLocalChecked()); + }, + proxied_promise, destination_context->GetIsolate(), + std::move(global_catch_source_context), + std::move(global_catch_destination_context)); + + ignore_result(source_promise->Then( + source_context, + v8::Local::Cast( + mate::ConvertToV8(destination_context->GetIsolate(), then_cb)), + v8::Local::Cast( + mate::ConvertToV8(destination_context->GetIsolate(), catch_cb)))); + + object_cache->CacheProxiedObject(value, proxied_promise_handle); + return v8::MaybeLocal(proxied_promise_handle); + } + } + + // Errors aren't serializable currently, we need to pull the message out and + // re-construct in the destination context + if (value->IsNativeError()) { + v8::Context::Scope destination_context_scope(destination_context); + return v8::MaybeLocal(v8::Exception::Error( + v8::Exception::CreateMessage(destination_context->GetIsolate(), value) + ->Get())); + } + + // Manually go through the array and pass each value individually into a new + // array so that functions deep inside arrays get proxied or arrays of + // promises are proxied correctly. + if (IsPlainArray(value)) { + v8::Context::Scope destination_context_scope(destination_context); + { + v8::Local arr = v8::Local::Cast(value); + size_t length = arr->Length(); + v8::Local cloned_arr = + v8::Array::New(destination_context->GetIsolate(), length); + for (size_t i = 0; i < length; i++) { + auto value_for_array = PassValueToOtherContext( + source_context, destination_context, + arr->Get(source_context, i).ToLocalChecked(), object_cache, + support_dynamic_properties, recursion_depth + 1); + if (value_for_array.IsEmpty()) + return v8::MaybeLocal(); + + if (!mate::internal::IsTrue( + cloned_arr->Set(destination_context, static_cast(i), + value_for_array.ToLocalChecked()))) { + return v8::MaybeLocal(); + } + } + object_cache->CacheProxiedObject(value, cloned_arr); + return v8::MaybeLocal(cloned_arr); + } + } + + // Proxy all objects + if (IsPlainObject(value)) { + auto object_value = v8::Local::Cast(value); + auto passed_value = CreateProxyForAPI( + object_value, source_context, destination_context, object_cache, + support_dynamic_properties, recursion_depth + 1); + if (passed_value.IsEmpty()) + return v8::MaybeLocal(); + return v8::MaybeLocal(passed_value.ToLocalChecked()); + } + + // Serializable objects + blink::CloneableMessage ret; + { + v8::Context::Scope source_context_scope(source_context); + { + // V8 serializer will throw an error if required + if (!mate::ConvertFromV8(source_context->GetIsolate(), value, &ret)) + return v8::MaybeLocal(); + } + } + + v8::Context::Scope destination_context_scope(destination_context); + { + v8::Local cloned_value = + mate::ConvertToV8(destination_context->GetIsolate(), ret); + object_cache->CacheProxiedObject(value, cloned_value); + return v8::MaybeLocal(cloned_value); + } +} + +void ProxyFunctionWrapper(const v8::FunctionCallbackInfo& info) { + CHECK(info.Data()->IsObject()); + v8::Local data = info.Data().As(); + bool support_dynamic_properties = false; + mate::Arguments args(info); + // Context the proxy function was called from + v8::Local calling_context = args.isolate()->GetCurrentContext(); + + // Pull the original function and its context off of the data private key + v8::MaybeLocal sdp_value = + GetPrivate(calling_context, data, + context_bridge::kSupportsDynamicPropertiesPrivateKey); + v8::MaybeLocal maybe_func = GetPrivate( + calling_context, data, context_bridge::kProxyFunctionPrivateKey); + v8::Local func_value; + if (sdp_value.IsEmpty() || maybe_func.IsEmpty() || + !gin::ConvertFromV8(args.isolate(), sdp_value.ToLocalChecked(), + &support_dynamic_properties) || + !maybe_func.ToLocal(&func_value)) + return; + + v8::Local func = v8::Local::Cast(func_value); + v8::Local func_owning_context = func->CreationContext(); + + v8::Context::Scope func_owning_context_scope(func_owning_context); + context_bridge::ObjectCache object_cache; + { + std::vector> original_args; + std::vector> proxied_args; + args.GetRemaining(&original_args); + + for (auto value : original_args) { + auto arg = + PassValueToOtherContext(calling_context, func_owning_context, value, + &object_cache, support_dynamic_properties, 0); + if (arg.IsEmpty()) + return; + proxied_args.push_back(arg.ToLocalChecked()); + } + + v8::MaybeLocal maybe_return_value; + bool did_error = false; + std::string error_message; + { + v8::TryCatch try_catch(args.isolate()); + maybe_return_value = func->Call(func_owning_context, func, + proxied_args.size(), proxied_args.data()); + if (try_catch.HasCaught()) { + did_error = true; + auto message = try_catch.Message(); + + if (message.IsEmpty() || + !mate::ConvertFromV8(args.isolate(), message->Get(), + &error_message)) { + error_message = + "An unknown exception occurred in the isolated context, an " + "error " + "occurred but a valid exception was not thrown."; + } + } + } + + if (did_error) { + v8::Context::Scope calling_context_scope(calling_context); + { + args.isolate()->ThrowException(v8::Exception::Error( + gin::StringToV8(args.isolate(), error_message))); + return; + } + } + + if (maybe_return_value.IsEmpty()) + return; + + auto ret = + PassValueToOtherContext(func_owning_context, calling_context, + maybe_return_value.ToLocalChecked(), + &object_cache, support_dynamic_properties, 0); + if (ret.IsEmpty()) + return; + info.GetReturnValue().Set(ret.ToLocalChecked()); + } +} + +v8::MaybeLocal CreateProxyForAPI( + const v8::Local& api_object, + const v8::Local& source_context, + const v8::Local& destination_context, + context_bridge::ObjectCache* object_cache, + bool support_dynamic_properties, + int recursion_depth) { + mate::Dictionary api(source_context->GetIsolate(), api_object); + v8::Context::Scope destination_context_scope(destination_context); + { + mate::Dictionary proxy = + mate::Dictionary::CreateEmpty(destination_context->GetIsolate()); + object_cache->CacheProxiedObject(api.GetHandle(), proxy.GetHandle()); + auto maybe_keys = api.GetHandle()->GetOwnPropertyNames( + source_context, + static_cast(v8::ONLY_ENUMERABLE | v8::SKIP_SYMBOLS), + v8::KeyConversionMode::kConvertToString); + if (maybe_keys.IsEmpty()) + return v8::MaybeLocal(proxy.GetHandle()); + auto keys = maybe_keys.ToLocalChecked(); + + uint32_t length = keys->Length(); + std::string key_str; + for (uint32_t i = 0; i < length; i++) { + v8::Local key = + keys->Get(destination_context, i).ToLocalChecked(); + // Try get the key as a string + if (!mate::ConvertFromV8(api.isolate(), key, &key_str)) { + continue; + } + if (support_dynamic_properties) { + v8::Context::Scope source_context_scope(source_context); + auto maybe_desc = api.GetHandle()->GetOwnPropertyDescriptor( + source_context, v8::Local::Cast(key)); + v8::Local desc_value; + if (!maybe_desc.ToLocal(&desc_value) || !desc_value->IsObject()) + continue; + mate::Dictionary desc(api.isolate(), desc_value.As()); + if (desc.Has("get") || desc.Has("set")) { + v8::Local getter; + v8::Local setter; + desc.Get("get", &getter); + desc.Get("set", &setter); + + { + v8::Context::Scope destination_context_scope(destination_context); + v8::Local getter_proxy; + v8::Local setter_proxy; + if (!getter.IsEmpty()) { + if (!PassValueToOtherContext(source_context, destination_context, + getter, object_cache, false, 1) + .ToLocal(&getter_proxy)) + continue; + } + if (!setter.IsEmpty()) { + if (!PassValueToOtherContext(source_context, destination_context, + setter, object_cache, false, 1) + .ToLocal(&setter_proxy)) + continue; + } + + v8::PropertyDescriptor desc(getter_proxy, setter_proxy); + ignore_result(proxy.GetHandle()->DefineProperty( + destination_context, gin::StringToV8(api.isolate(), key_str), + desc)); + } + continue; + } + } + v8::Local value; + if (!api.Get(key_str, &value)) + continue; + + auto passed_value = PassValueToOtherContext( + source_context, destination_context, value, object_cache, + support_dynamic_properties, recursion_depth + 1); + if (passed_value.IsEmpty()) + return v8::MaybeLocal(); + proxy.Set(key_str, passed_value.ToLocalChecked()); + } + + return proxy.GetHandle(); + } +} + +void ExposeAPIInMainWorld(const std::string& key, + v8::Local api_object, + mate::Arguments* args) { + auto* render_frame = GetRenderFrame(api_object); + CHECK(render_frame); + auto* frame = render_frame->GetWebFrame(); + CHECK(frame); + v8::Local main_context = frame->MainWorldScriptContext(); + mate::Dictionary global(main_context->GetIsolate(), main_context->Global()); + + if (global.Has(key)) { + args->ThrowError( + "Cannot bind an API on top of an existing property on the window " + "object"); + return; + } + + v8::Local isolated_context = + frame->WorldScriptContext(args->isolate(), World::ISOLATED_WORLD); + + context_bridge::ObjectCache object_cache; + v8::Context::Scope main_context_scope(main_context); + { + v8::MaybeLocal maybe_proxy = CreateProxyForAPI( + api_object, isolated_context, main_context, &object_cache, false, 0); + if (maybe_proxy.IsEmpty()) + return; + auto proxy = maybe_proxy.ToLocalChecked(); + if (!DeepFreeze(proxy, main_context)) + return; + + global.SetReadOnlyNonConfigurable(key, proxy); + } +} + +mate::Dictionary TraceKeyPath(const mate::Dictionary& start, + const std::vector& key_path) { + mate::Dictionary current = start; + for (size_t i = 0; i < key_path.size() - 1; i++) { + CHECK(current.Get(key_path[i], ¤t)); + } + return current; +} + +void OverrideGlobalValueFromIsolatedWorld( + const std::vector& key_path, + v8::Local value, + bool support_dynamic_properties) { + if (key_path.size() == 0) + return; + + auto* render_frame = GetRenderFrame(value); + CHECK(render_frame); + auto* frame = render_frame->GetWebFrame(); + CHECK(frame); + v8::Local main_context = frame->MainWorldScriptContext(); + mate::Dictionary global(main_context->GetIsolate(), main_context->Global()); + + const std::string final_key = key_path[key_path.size() - 1]; + mate::Dictionary target_object = TraceKeyPath(global, key_path); + + { + v8::Context::Scope main_context_scope(main_context); + context_bridge::ObjectCache object_cache; + v8::MaybeLocal maybe_proxy = + PassValueToOtherContext(value->CreationContext(), main_context, value, + &object_cache, support_dynamic_properties, 1); + DCHECK(!maybe_proxy.IsEmpty()); + auto proxy = maybe_proxy.ToLocalChecked(); + + target_object.Set(final_key, proxy); + } +} + +bool OverrideGlobalPropertyFromIsolatedWorld( + const std::vector& key_path, + v8::Local getter, + v8::Local setter, + mate::Arguments* args) { + if (key_path.size() == 0) + return false; + + auto* render_frame = GetRenderFrame(getter); + CHECK(render_frame); + auto* frame = render_frame->GetWebFrame(); + CHECK(frame); + v8::Local main_context = frame->MainWorldScriptContext(); + mate::Dictionary global(main_context->GetIsolate(), main_context->Global()); + + const std::string final_key = key_path[key_path.size() - 1]; + v8::Local target_object = + TraceKeyPath(global, key_path).GetHandle(); + + { + v8::Context::Scope main_context_scope(main_context); + context_bridge::ObjectCache object_cache; + v8::Local getter_proxy; + v8::Local setter_proxy; + if (!getter->IsNullOrUndefined()) { + v8::MaybeLocal maybe_getter_proxy = + PassValueToOtherContext(getter->CreationContext(), main_context, + getter, &object_cache, false, 1); + DCHECK(!maybe_getter_proxy.IsEmpty()); + getter_proxy = maybe_getter_proxy.ToLocalChecked(); + } + if (!setter->IsNullOrUndefined() && setter->IsObject()) { + v8::MaybeLocal maybe_setter_proxy = + PassValueToOtherContext(getter->CreationContext(), main_context, + setter, &object_cache, false, 1); + DCHECK(!maybe_setter_proxy.IsEmpty()); + setter_proxy = maybe_setter_proxy.ToLocalChecked(); + } + + v8::PropertyDescriptor desc(getter_proxy, setter_proxy); + bool success = mate::internal::IsTrue(target_object->DefineProperty( + main_context, gin::StringToV8(args->isolate(), final_key), desc)); + DCHECK(success); + return success; + } +} + +bool IsCalledFromMainWorld(v8::Isolate* isolate) { + auto* render_frame = GetRenderFrame(isolate->GetCurrentContext()->Global()); + CHECK(render_frame); + auto* frame = render_frame->GetWebFrame(); + CHECK(frame); + v8::Local main_context = frame->MainWorldScriptContext(); + return isolate->GetCurrentContext() == main_context; +} + +bool IsCalledFromIsolatedWorld(v8::Isolate* isolate) { + auto* render_frame = GetRenderFrame(isolate->GetCurrentContext()->Global()); + CHECK(render_frame); + auto* frame = render_frame->GetWebFrame(); + CHECK(frame); + v8::Local isolated_context = + frame->WorldScriptContext(isolate, World::ISOLATED_WORLD); + return isolate->GetCurrentContext() == isolated_context; +} + +} // namespace api + +} // namespace electron + +namespace { + +void Initialize(v8::Local exports, + v8::Local unused, + v8::Local context, + void* priv) { + v8::Isolate* isolate = context->GetIsolate(); + mate::Dictionary dict(isolate, exports); + dict.SetMethod("exposeAPIInMainWorld", &electron::api::ExposeAPIInMainWorld); + dict.SetMethod("_overrideGlobalValueFromIsolatedWorld", + &electron::api::OverrideGlobalValueFromIsolatedWorld); + dict.SetMethod("_overrideGlobalPropertyFromIsolatedWorld", + &electron::api::OverrideGlobalPropertyFromIsolatedWorld); + dict.SetMethod("_isCalledFromMainWorld", + &electron::api::IsCalledFromMainWorld); + dict.SetMethod("_isCalledFromIsolatedWorld", + &electron::api::IsCalledFromIsolatedWorld); +#ifdef DCHECK_IS_ON + dict.Set("_isDebug", true); +#endif +} + +} // namespace + +NODE_LINKED_MODULE_CONTEXT_AWARE(atom_renderer_context_bridge, Initialize) diff --git a/shell/renderer/api/electron_api_context_bridge.h b/shell/renderer/api/electron_api_context_bridge.h new file mode 100644 index 0000000000000..84746f6311bda --- /dev/null +++ b/shell/renderer/api/electron_api_context_bridge.h @@ -0,0 +1,31 @@ +// Copyright (c) 2019 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_RENDERER_API_ELECTRON_API_CONTEXT_BRIDGE_H_ +#define SHELL_RENDERER_API_ELECTRON_API_CONTEXT_BRIDGE_H_ + +#include "native_mate/arguments.h" +#include "native_mate/dictionary.h" +#include "shell/renderer/api/context_bridge/object_cache.h" +#include "v8/include/v8.h" + +namespace electron { + +namespace api { + +void ProxyFunctionWrapper(const v8::FunctionCallbackInfo& info); + +v8::MaybeLocal CreateProxyForAPI( + const v8::Local& api_object, + const v8::Local& source_context, + const v8::Local& target_context, + context_bridge::ObjectCache* object_cache, + bool support_dynamic_properties, + int recursion_depth); + +} // namespace api + +} // namespace electron + +#endif // SHELL_RENDERER_API_ELECTRON_API_CONTEXT_BRIDGE_H_ diff --git a/shell/renderer/api/electron_api_renderer_ipc.cc b/shell/renderer/api/electron_api_renderer_ipc.cc new file mode 100644 index 0000000000000..5af1dc5fea96f --- /dev/null +++ b/shell/renderer/api/electron_api_renderer_ipc.cc @@ -0,0 +1,168 @@ +// Copyright (c) 2013 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include + +#include "base/task/post_task.h" +#include "base/values.h" +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_frame_observer.h" +#include "gin/dictionary.h" +#include "gin/handle.h" +#include "gin/object_template_builder.h" +#include "gin/wrappable.h" +#include "services/service_manager/public/cpp/interface_provider.h" +#include "shell/common/api/api.mojom.h" +#include "shell/common/gin_converters/blink_converter_gin_adapter.h" +#include "shell/common/gin_converters/value_converter_gin_adapter.h" +#include "shell/common/node_bindings.h" +#include "shell/common/node_includes.h" +#include "shell/common/promise_util.h" +#include "third_party/blink/public/web/web_local_frame.h" + +using blink::WebLocalFrame; +using content::RenderFrame; + +namespace { + +RenderFrame* GetCurrentRenderFrame() { + WebLocalFrame* frame = WebLocalFrame::FrameForCurrentContext(); + if (!frame) + return nullptr; + + return RenderFrame::FromWebFrame(frame); +} + +class IPCRenderer : public gin::Wrappable, + public content::RenderFrameObserver { + public: + static gin::WrapperInfo kWrapperInfo; + + static gin::Handle Create(v8::Isolate* isolate) { + return gin::CreateHandle(isolate, new IPCRenderer(isolate)); + } + + explicit IPCRenderer(v8::Isolate* isolate) + : content::RenderFrameObserver(GetCurrentRenderFrame()) { + RenderFrame* render_frame = GetCurrentRenderFrame(); + DCHECK(render_frame); + weak_context_ = + v8::Global(isolate, isolate->GetCurrentContext()); + weak_context_.SetWeak(); + + render_frame->GetRemoteInterfaces()->GetInterface( + mojo::MakeRequest(&electron_browser_ptr_)); + } + + void OnDestruct() override { electron_browser_ptr_.reset(); } + + void WillReleaseScriptContext(v8::Local context, + int32_t world_id) override { + if (weak_context_.IsEmpty() || + weak_context_.Get(context->GetIsolate()) == context) + electron_browser_ptr_.reset(); + } + + // gin::Wrappable: + gin::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override { + return gin::Wrappable::GetObjectTemplateBuilder(isolate) + .SetMethod("send", &IPCRenderer::SendMessage) + .SetMethod("sendSync", &IPCRenderer::SendSync) + .SetMethod("sendTo", &IPCRenderer::SendTo) + .SetMethod("sendToHost", &IPCRenderer::SendToHost) + .SetMethod("invoke", &IPCRenderer::Invoke); + } + + const char* GetTypeName() override { return "IPCRenderer"; } + + private: + void SendMessage(v8::Isolate* isolate, + bool internal, + const std::string& channel, + v8::Local arguments) { + blink::CloneableMessage message; + if (!mate::ConvertFromV8(isolate, arguments, &message)) { + return; + } + electron_browser_ptr_->Message(internal, channel, std::move(message)); + } + + v8::Local Invoke(v8::Isolate* isolate, + bool internal, + const std::string& channel, + v8::Local arguments) { + blink::CloneableMessage message; + if (!mate::ConvertFromV8(isolate, arguments, &message)) { + return v8::Local(); + } + electron::util::Promise p(isolate); + auto handle = p.GetHandle(); + + electron_browser_ptr_->Invoke( + internal, channel, std::move(message), + base::BindOnce( + [](electron::util::Promise p, + blink::CloneableMessage result) { p.ResolveWithGin(result); }, + std::move(p))); + + return handle; + } + + void SendTo(v8::Isolate* isolate, + bool internal, + bool send_to_all, + int32_t web_contents_id, + const std::string& channel, + v8::Local arguments) { + blink::CloneableMessage message; + if (!mate::ConvertFromV8(isolate, arguments, &message)) { + return; + } + electron_browser_ptr_->MessageTo(internal, send_to_all, web_contents_id, + channel, std::move(message)); + } + + void SendToHost(v8::Isolate* isolate, + const std::string& channel, + v8::Local arguments) { + blink::CloneableMessage message; + if (!mate::ConvertFromV8(isolate, arguments, &message)) { + return; + } + electron_browser_ptr_->MessageHost(channel, std::move(message)); + } + + blink::CloneableMessage SendSync(v8::Isolate* isolate, + bool internal, + const std::string& channel, + v8::Local arguments) { + blink::CloneableMessage message; + if (!mate::ConvertFromV8(isolate, arguments, &message)) { + return blink::CloneableMessage(); + } + + blink::CloneableMessage result; + electron_browser_ptr_->MessageSync(internal, channel, std::move(message), + &result); + return result; + } + + v8::Global weak_context_; + electron::mojom::ElectronBrowserPtr electron_browser_ptr_; +}; + +gin::WrapperInfo IPCRenderer::kWrapperInfo = {gin::kEmbedderNativeGin}; + +void Initialize(v8::Local exports, + v8::Local unused, + v8::Local context, + void* priv) { + gin::Dictionary dict(context->GetIsolate(), exports); + dict.Set("ipc", IPCRenderer::Create(context->GetIsolate())); +} + +} // namespace + +NODE_LINKED_MODULE_CONTEXT_AWARE(atom_renderer_ipc, Initialize) diff --git a/shell/renderer/api/atom_api_spell_check_client.cc b/shell/renderer/api/electron_api_spell_check_client.cc similarity index 99% rename from shell/renderer/api/atom_api_spell_check_client.cc rename to shell/renderer/api/electron_api_spell_check_client.cc index 4b14c77dc2fbd..839c8bf80035c 100644 --- a/shell/renderer/api/atom_api_spell_check_client.cc +++ b/shell/renderer/api/electron_api_spell_check_client.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/renderer/api/atom_api_spell_check_client.h" +#include "shell/renderer/api/electron_api_spell_check_client.h" #include #include diff --git a/shell/renderer/api/atom_api_spell_check_client.h b/shell/renderer/api/electron_api_spell_check_client.h similarity index 95% rename from shell/renderer/api/atom_api_spell_check_client.h rename to shell/renderer/api/electron_api_spell_check_client.h index 38b8492a4ba14..2c550237e55af 100644 --- a/shell/renderer/api/atom_api_spell_check_client.h +++ b/shell/renderer/api/electron_api_spell_check_client.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_RENDERER_API_ATOM_API_SPELL_CHECK_CLIENT_H_ -#define SHELL_RENDERER_API_ATOM_API_SPELL_CHECK_CLIENT_H_ +#ifndef SHELL_RENDERER_API_ELECTRON_API_SPELL_CHECK_CLIENT_H_ +#define SHELL_RENDERER_API_ELECTRON_API_SPELL_CHECK_CLIENT_H_ #include #include @@ -111,4 +111,4 @@ class SpellCheckClient : public blink::WebSpellCheckPanelHostClient, } // namespace electron -#endif // SHELL_RENDERER_API_ATOM_API_SPELL_CHECK_CLIENT_H_ +#endif // SHELL_RENDERER_API_ELECTRON_API_SPELL_CHECK_CLIENT_H_ diff --git a/shell/renderer/api/atom_api_web_frame.cc b/shell/renderer/api/electron_api_web_frame.cc similarity index 78% rename from shell/renderer/api/atom_api_web_frame.cc rename to shell/renderer/api/electron_api_web_frame.cc index 6aa3c3258f58c..7c028e4afbcba 100644 --- a/shell/renderer/api/atom_api_web_frame.cc +++ b/shell/renderer/api/electron_api_web_frame.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. +#include #include #include #include @@ -15,11 +16,12 @@ #include "native_mate/dictionary.h" #include "services/service_manager/public/cpp/interface_provider.h" #include "shell/common/api/api.mojom.h" +#include "shell/common/gin_helper/error_thrower.h" #include "shell/common/native_mate_converters/blink_converter.h" #include "shell/common/native_mate_converters/string16_converter.h" #include "shell/common/node_includes.h" #include "shell/common/promise_util.h" -#include "shell/renderer/api/atom_api_spell_check_client.h" +#include "shell/renderer/api/electron_api_spell_check_client.h" #include "third_party/blink/public/common/page/page_zoom.h" #include "third_party/blink/public/common/web_cache/web_cache_resource_type_stats.h" #include "third_party/blink/public/platform/web_cache.h" @@ -231,17 +233,34 @@ void SetName(v8::Local window, const std::string& name) { blink::WebString::FromUTF8(name)); } -void SetZoomLevel(v8::Local window, double level) { +void SetZoomLevel(gin_helper::ErrorThrower thrower, + v8::Local window, + double level) { content::RenderFrame* render_frame = GetRenderFrame(window); + if (!render_frame) { + thrower.ThrowError( + "Render frame was torn down before webFrame.setZoomLevel could be " + "executed"); + return; + } + mojom::ElectronBrowserPtr browser_ptr; render_frame->GetRemoteInterfaces()->GetInterface( mojo::MakeRequest(&browser_ptr)); browser_ptr->SetTemporaryZoomLevel(level); } -double GetZoomLevel(v8::Local window) { +double GetZoomLevel(gin_helper::ErrorThrower thrower, + v8::Local window) { double result = 0.0; content::RenderFrame* render_frame = GetRenderFrame(window); + if (!render_frame) { + thrower.ThrowError( + "Render frame was torn down before webFrame.getZoomLevel could be " + "executed"); + return result; + } + mojom::ElectronBrowserPtr browser_ptr; render_frame->GetRemoteInterfaces()->GetInterface( mojo::MakeRequest(&browser_ptr)); @@ -249,43 +268,59 @@ double GetZoomLevel(v8::Local window) { return result; } -void SetZoomFactor(v8::Local window, double factor) { - SetZoomLevel(window, blink::PageZoomFactorToZoomLevel(factor)); -} +void SetZoomFactor(gin_helper::ErrorThrower thrower, + v8::Local window, + double factor) { + if (factor < std::numeric_limits::epsilon()) { + thrower.ThrowError("'zoomFactor' must be a double greater than 0.0"); + return; + } -double GetZoomFactor(v8::Local window) { - return blink::PageZoomLevelToZoomFactor(GetZoomLevel(window)); + SetZoomLevel(thrower, window, blink::PageZoomFactorToZoomLevel(factor)); } -void SetVisualZoomLevelLimits(v8::Local window, - double min_level, - double max_level) { - blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame(); - web_frame->View()->SetDefaultPageScaleLimits(min_level, max_level); - web_frame->View()->SetIgnoreViewportTagScaleLimits(true); +double GetZoomFactor(gin_helper::ErrorThrower thrower, + v8::Local window) { + double zoom_level = GetZoomLevel(thrower, window); + return blink::PageZoomLevelToZoomFactor(zoom_level); } -void SetLayoutZoomLevelLimits(v8::Local window, +void SetVisualZoomLevelLimits(gin_helper::ErrorThrower thrower, + v8::Local window, double min_level, double max_level) { - content::RenderFrame* render_frame = GetRenderFrame(window); - mojom::ElectronBrowserPtr browser_ptr; - render_frame->GetRemoteInterfaces()->GetInterface( - mojo::MakeRequest(&browser_ptr)); + auto* render_frame = GetRenderFrame(window); + if (!render_frame) { + thrower.ThrowError( + "Render frame was torn down before webFrame.setVisualZoomLevelLimits " + "could be executed"); + return; + } - browser_ptr->SetZoomLimits(min_level, max_level); + blink::WebFrame* web_frame = render_frame->GetWebFrame(); + web_frame->View()->SetDefaultPageScaleLimits(min_level, max_level); + web_frame->View()->SetIgnoreViewportTagScaleLimits(true); } -void AllowGuestViewElementDefinition(v8::Isolate* isolate, +void AllowGuestViewElementDefinition(gin_helper::ErrorThrower thrower, v8::Local window, v8::Local context, v8::Local register_cb) { - v8::HandleScope handle_scope(isolate); + v8::HandleScope handle_scope(thrower.isolate()); v8::Context::Scope context_scope(context->CreationContext()); blink::WebCustomElement::EmbedderNamesAllowedScope embedder_names_scope; - GetRenderFrame(context)->GetWebFrame()->RequestExecuteV8Function( - context->CreationContext(), register_cb, v8::Null(isolate), 0, nullptr, - nullptr); + + auto* render_frame = GetRenderFrame(window); + if (!render_frame) { + thrower.ThrowError( + "Render frame was torn down before " + "webFrame.allowGuestViewElementDefinition could be executed"); + return; + } + + render_frame->GetWebFrame()->RequestExecuteV8Function( + context->CreationContext(), register_cb, v8::Null(thrower.isolate()), 0, + nullptr, nullptr); } int GetWebFrameId(v8::Local window, @@ -321,6 +356,13 @@ void SetSpellCheckProvider(mate::Arguments* args, // Remove the old client. content::RenderFrame* render_frame = GetRenderFrame(window); + if (!render_frame) { + args->ThrowError( + "Render frame was torn down before webFrame.setSpellCheckProvider " + "could be executed"); + return; + } + auto* existing = SpellCheckerHolder::FromRenderFrame(render_frame); if (existing) existing->UnsetAndDestroy(); @@ -335,8 +377,18 @@ void SetSpellCheckProvider(mate::Arguments* args, new SpellCheckerHolder(render_frame, std::move(spell_check_client)); } -void InsertText(v8::Local window, const std::string& text) { - blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame(); +void InsertText(gin_helper::ErrorThrower thrower, + v8::Local window, + const std::string& text) { + auto* render_frame = GetRenderFrame(window); + if (!render_frame) { + thrower.ThrowError( + "Render frame was torn down before webFrame.insertText could be " + "executed"); + return; + } + + blink::WebFrame* web_frame = render_frame->GetWebFrame(); if (web_frame->IsWebLocalFrame()) { web_frame->ToWebLocalFrame() ->FrameWidget() @@ -357,7 +409,15 @@ base::string16 InsertCSS(v8::Local window, if (args->GetNext(&options)) options.Get("cssOrigin", &css_origin); - blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame(); + auto* render_frame = GetRenderFrame(window); + if (!render_frame) { + args->ThrowError( + "Render frame was torn down before webFrame.insertCSS could be " + "executed"); + return base::string16(); + } + + blink::WebFrame* web_frame = render_frame->GetWebFrame(); if (web_frame->IsWebLocalFrame()) { return web_frame->ToWebLocalFrame() ->GetDocument() @@ -367,8 +427,18 @@ base::string16 InsertCSS(v8::Local window, return base::string16(); } -void RemoveInsertedCSS(v8::Local window, const base::string16& key) { - blink::WebFrame* web_frame = GetRenderFrame(window)->GetWebFrame(); +void RemoveInsertedCSS(gin_helper::ErrorThrower thrower, + v8::Local window, + const base::string16& key) { + auto* render_frame = GetRenderFrame(window); + if (!render_frame) { + thrower.ThrowError( + "Render frame was torn down before webFrame.removeInsertedCSS could be " + "executed"); + return; + } + + blink::WebFrame* web_frame = render_frame->GetWebFrame(); if (web_frame->IsWebLocalFrame()) { web_frame->ToWebLocalFrame()->GetDocument().RemoveInsertedStyleSheet( blink::WebString::FromUTF16(key)); @@ -382,10 +452,18 @@ v8::Local ExecuteJavaScript(mate::Arguments* args, util::Promise> promise(isolate); v8::Local handle = promise.GetHandle(); + auto* render_frame = GetRenderFrame(window); + if (!render_frame) { + promise.RejectWithErrorMessage( + "Render frame was torn down before webFrame.executeJavaScript could be " + "executed"); + return handle; + } + bool has_user_gesture = false; args->GetNext(&has_user_gesture); - GetRenderFrame(window)->GetWebFrame()->RequestExecuteScriptAndReturnValue( + render_frame->GetWebFrame()->RequestExecuteScriptAndReturnValue( blink::WebScriptSource(blink::WebString::FromUTF16(code)), has_user_gesture, new ScriptExecutionCallback(std::move(promise))); @@ -401,6 +479,14 @@ v8::Local ExecuteJavaScriptInIsolatedWorld( util::Promise> promise(isolate); v8::Local handle = promise.GetHandle(); + auto* render_frame = GetRenderFrame(window); + if (!render_frame) { + promise.RejectWithErrorMessage( + "Render frame was torn down before webFrame.executeJavaScript could be " + "executed"); + return handle; + } + std::vector sources; for (const auto& script : scripts) { @@ -430,7 +516,7 @@ v8::Local ExecuteJavaScriptInIsolatedWorld( // Debugging tip: if you see a crash stack trace beginning from this call, // then it is very likely that some exception happened when executing the // "content_script/init.js" script. - GetRenderFrame(window)->GetWebFrame()->RequestExecuteScriptInIsolatedWorld( + render_frame->GetWebFrame()->RequestExecuteScriptInIsolatedWorld( world_id, &sources.front(), sources.size(), has_user_gesture, scriptExecutionType, new ScriptExecutionCallback(std::move(promise))); @@ -441,6 +527,14 @@ void SetIsolatedWorldInfo(v8::Local window, int world_id, const mate::Dictionary& options, mate::Arguments* args) { + auto* render_frame = GetRenderFrame(window); + if (!render_frame) { + args->ThrowError( + "Render frame was torn down before webFrame.setIsolatedWorldInfo could " + "be executed"); + return; + } + std::string origin_url, security_policy, name; options.Get("securityOrigin", &origin_url); options.Get("csp", &security_policy); @@ -457,7 +551,7 @@ void SetIsolatedWorldInfo(v8::Local window, blink::WebString::FromUTF8(origin_url)); info.content_security_policy = blink::WebString::FromUTF8(security_policy); info.human_readable_name = blink::WebString::FromUTF8(name); - GetRenderFrame(window)->GetWebFrame()->SetIsolatedWorldInfo(world_id, info); + render_frame->GetWebFrame()->SetIsolatedWorldInfo(world_id, info); } blink::WebCacheResourceTypeStats GetResourceUsage(v8::Isolate* isolate) { @@ -486,7 +580,11 @@ v8::Local FindFrameByRoutingId(v8::Isolate* isolate, v8::Local GetOpener(v8::Isolate* isolate, v8::Local window) { - blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->Opener(); + auto* render_frame = GetRenderFrame(window); + if (!render_frame) + return v8::Null(isolate); + + blink::WebFrame* frame = render_frame->GetWebFrame()->Opener(); if (frame && frame->IsWebLocalFrame()) return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); else @@ -504,7 +602,11 @@ v8::Local GetFrameParent(v8::Isolate* isolate, } v8::Local GetTop(v8::Isolate* isolate, v8::Local window) { - blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->Top(); + auto* render_frame = GetRenderFrame(window); + if (!render_frame) + return v8::Null(isolate); + + blink::WebFrame* frame = render_frame->GetWebFrame()->Top(); if (frame && frame->IsWebLocalFrame()) return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); else @@ -513,7 +615,11 @@ v8::Local GetTop(v8::Isolate* isolate, v8::Local window) { v8::Local GetFirstChild(v8::Isolate* isolate, v8::Local window) { - blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->FirstChild(); + auto* render_frame = GetRenderFrame(window); + if (!render_frame) + return v8::Null(isolate); + + blink::WebFrame* frame = render_frame->GetWebFrame()->FirstChild(); if (frame && frame->IsWebLocalFrame()) return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); else @@ -522,7 +628,11 @@ v8::Local GetFirstChild(v8::Isolate* isolate, v8::Local GetNextSibling(v8::Isolate* isolate, v8::Local window) { - blink::WebFrame* frame = GetRenderFrame(window)->GetWebFrame()->NextSibling(); + auto* render_frame = GetRenderFrame(window); + if (!render_frame) + return v8::Null(isolate); + + blink::WebFrame* frame = render_frame->GetWebFrame()->NextSibling(); if (frame && frame->IsWebLocalFrame()) return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); else @@ -548,17 +658,29 @@ v8::Local GetFrameForSelector(v8::Isolate* isolate, v8::Local FindFrameByName(v8::Isolate* isolate, v8::Local window, const std::string& name) { - blink::WebFrame* frame = - GetRenderFrame(window)->GetWebFrame()->FindFrameByName( - blink::WebString::FromUTF8(name)); + auto* render_frame = GetRenderFrame(window); + if (!render_frame) + return v8::Null(isolate); + + blink::WebFrame* frame = render_frame->GetWebFrame()->FindFrameByName( + blink::WebString::FromUTF8(name)); if (frame && frame->IsWebLocalFrame()) return frame->ToWebLocalFrame()->MainWorldScriptContext()->Global(); else return v8::Null(isolate); } -int GetRoutingId(v8::Local window) { - return GetRenderFrame(window)->GetRoutingID(); +int GetRoutingId(gin_helper::ErrorThrower thrower, + v8::Local window) { + auto* render_frame = GetRenderFrame(window); + if (!render_frame) { + thrower.ThrowError( + "Render frame was torn down before webFrame.getRoutingId could be " + "executed"); + return 0; + } + + return render_frame->GetRoutingID(); } } // namespace api @@ -581,7 +703,6 @@ void Initialize(v8::Local exports, dict.SetMethod("setZoomFactor", &SetZoomFactor); dict.SetMethod("getZoomFactor", &GetZoomFactor); dict.SetMethod("setVisualZoomLevelLimits", &SetVisualZoomLevelLimits); - dict.SetMethod("setLayoutZoomLevelLimits", &SetLayoutZoomLevelLimits); dict.SetMethod("allowGuestViewElementDefinition", &AllowGuestViewElementDefinition); dict.SetMethod("getWebFrameId", &GetWebFrameId); diff --git a/shell/renderer/browser_exposed_renderer_interfaces.cc b/shell/renderer/browser_exposed_renderer_interfaces.cc new file mode 100644 index 0000000000000..55781e58edd5d --- /dev/null +++ b/shell/renderer/browser_exposed_renderer_interfaces.cc @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/renderer/browser_exposed_renderer_interfaces.h" + +#include + +#include "base/bind.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "build/build_config.h" +#include "electron/buildflags/buildflags.h" +#include "mojo/public/cpp/bindings/binder_map.h" +#include "shell/renderer/renderer_client_base.h" + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "components/spellcheck/renderer/spellcheck.h" +#endif + +namespace { +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +void BindSpellChecker( + electron::RendererClientBase* client, + mojo::PendingReceiver receiver) { + if (client->GetSpellCheck()) + client->GetSpellCheck()->BindReceiver(std::move(receiver)); +} +#endif + +} // namespace + +void ExposeElectronRendererInterfacesToBrowser( + electron::RendererClientBase* client, + mojo::BinderMap* binders) { +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + binders->Add(base::BindRepeating(&BindSpellChecker, client), + base::SequencedTaskRunnerHandle::Get()); +#endif +} diff --git a/shell/renderer/browser_exposed_renderer_interfaces.h b/shell/renderer/browser_exposed_renderer_interfaces.h new file mode 100644 index 0000000000000..a35745e7b4156 --- /dev/null +++ b/shell/renderer/browser_exposed_renderer_interfaces.h @@ -0,0 +1,22 @@ +// Copyright (c) 2020 Slack Technologies, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_RENDERER_BROWSER_EXPOSED_RENDERER_INTERFACES_H_ +#define SHELL_RENDERER_BROWSER_EXPOSED_RENDERER_INTERFACES_H_ + +namespace mojo { +class BinderMap; +} // namespace mojo + +namespace electron { +class RendererClientBase; +} // namespace electron + +class ChromeContentRendererClient; + +void ExposeElectronRendererInterfacesToBrowser( + electron::RendererClientBase* client, + mojo::BinderMap* binders); + +#endif // SHELL_RENDERER_BROWSER_EXPOSED_RENDERER_INTERFACES_H_ diff --git a/shell/renderer/content_settings_observer.cc b/shell/renderer/content_settings_observer.cc index 6868b0c03cf02..6966fdc3b5e14 100644 --- a/shell/renderer/content_settings_observer.cc +++ b/shell/renderer/content_settings_observer.cc @@ -5,6 +5,7 @@ #include "shell/renderer/content_settings_observer.h" #include "content/public/renderer/render_frame.h" +#include "shell/common/options_switches.h" #include "third_party/blink/public/platform/url_conversion.h" #include "third_party/blink/public/platform/web_security_origin.h" #include "third_party/blink/public/web/web_local_frame.h" @@ -20,9 +21,14 @@ ContentSettingsObserver::ContentSettingsObserver( ContentSettingsObserver::~ContentSettingsObserver() = default; bool ContentSettingsObserver::AllowDatabase() { + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableWebSQL)) { + return false; + } + blink::WebFrame* frame = render_frame()->GetWebFrame(); - if (frame->GetSecurityOrigin().IsUnique() || - frame->Top()->GetSecurityOrigin().IsUnique()) + if (frame->GetSecurityOrigin().IsOpaque() || + frame->Top()->GetSecurityOrigin().IsOpaque()) return false; auto origin = blink::WebStringToGURL(frame->GetSecurityOrigin().ToString()); if (!origin.IsStandard()) @@ -32,8 +38,8 @@ bool ContentSettingsObserver::AllowDatabase() { bool ContentSettingsObserver::AllowStorage(bool local) { blink::WebFrame* frame = render_frame()->GetWebFrame(); - if (frame->GetSecurityOrigin().IsUnique() || - frame->Top()->GetSecurityOrigin().IsUnique()) + if (frame->GetSecurityOrigin().IsOpaque() || + frame->Top()->GetSecurityOrigin().IsOpaque()) return false; auto origin = blink::WebStringToGURL(frame->GetSecurityOrigin().ToString()); if (!origin.IsStandard()) @@ -41,11 +47,10 @@ bool ContentSettingsObserver::AllowStorage(bool local) { return true; } -bool ContentSettingsObserver::AllowIndexedDB( - const blink::WebSecurityOrigin& security_origin) { +bool ContentSettingsObserver::AllowIndexedDB() { blink::WebFrame* frame = render_frame()->GetWebFrame(); - if (frame->GetSecurityOrigin().IsUnique() || - frame->Top()->GetSecurityOrigin().IsUnique()) + if (frame->GetSecurityOrigin().IsOpaque() || + frame->Top()->GetSecurityOrigin().IsOpaque()) return false; auto origin = blink::WebStringToGURL(frame->GetSecurityOrigin().ToString()); if (!origin.IsStandard()) diff --git a/shell/renderer/content_settings_observer.h b/shell/renderer/content_settings_observer.h index 78011bb4ff9f5..f3e4a5015d30a 100644 --- a/shell/renderer/content_settings_observer.h +++ b/shell/renderer/content_settings_observer.h @@ -20,7 +20,7 @@ class ContentSettingsObserver : public content::RenderFrameObserver, // blink::WebContentSettingsClient implementation. bool AllowDatabase() override; bool AllowStorage(bool local) override; - bool AllowIndexedDB(const blink::WebSecurityOrigin& security_origin) override; + bool AllowIndexedDB() override; private: // content::RenderFrameObserver implementation. diff --git a/shell/renderer/electron_api_service_impl.cc b/shell/renderer/electron_api_service_impl.cc index 08ee424bf3afa..b642b4a2fe7a6 100644 --- a/shell/renderer/electron_api_service_impl.cc +++ b/shell/renderer/electron_api_service_impl.cc @@ -12,13 +12,13 @@ #include "base/macros.h" #include "base/threading/thread_restrictions.h" #include "mojo/public/cpp/system/platform_handle.h" -#include "shell/common/atom_constants.h" +#include "shell/common/electron_constants.h" #include "shell/common/gin_converters/blink_converter_gin_adapter.h" #include "shell/common/gin_converters/value_converter_gin_adapter.h" #include "shell/common/heap_snapshot.h" #include "shell/common/node_includes.h" #include "shell/common/options_switches.h" -#include "shell/renderer/atom_render_frame_observer.h" +#include "shell/renderer/electron_render_frame_observer.h" #include "shell/renderer/renderer_client_base.h" #include "third_party/blink/public/web/blink.h" #include "third_party/blink/public/web/web_local_frame.h" @@ -67,7 +67,7 @@ void InvokeIpcCallback(v8::Local context, .ToLocalChecked(); auto callback_value = ipcNative->Get(context, callback_key).ToLocalChecked(); DCHECK(callback_value->IsFunction()); // set by init.ts - auto callback = v8::Local::Cast(callback_value); + auto callback = callback_value.As(); ignore_result(callback->Call(context, ipcNative, args.size(), args.data())); } @@ -98,18 +98,17 @@ ElectronApiServiceImpl::ElectronApiServiceImpl( content::RenderFrame* render_frame, RendererClientBase* renderer_client) : content::RenderFrameObserver(render_frame), - binding_(this), renderer_client_(renderer_client), weak_factory_(this) {} void ElectronApiServiceImpl::BindTo( - mojom::ElectronRendererAssociatedRequest request) { + mojo::PendingAssociatedReceiver receiver) { // Note: BindTo might be called for multiple times. - if (binding_.is_bound()) - binding_.Unbind(); + if (receiver_.is_bound()) + receiver_.reset(); - binding_.Bind(std::move(request)); - binding_.set_connection_error_handler( + receiver_.Bind(std::move(receiver)); + receiver_.set_disconnect_handler( base::BindOnce(&ElectronApiServiceImpl::OnConnectionError, GetWeakPtr())); } @@ -122,8 +121,8 @@ void ElectronApiServiceImpl::OnDestruct() { } void ElectronApiServiceImpl::OnConnectionError() { - if (binding_.is_bound()) - binding_.Unbind(); + if (receiver_.is_bound()) + receiver_.reset(); } void ElectronApiServiceImpl::Message(bool internal, diff --git a/shell/renderer/electron_api_service_impl.h b/shell/renderer/electron_api_service_impl.h index b6cb1dda3556a..f274c5244a233 100644 --- a/shell/renderer/electron_api_service_impl.h +++ b/shell/renderer/electron_api_service_impl.h @@ -12,7 +12,8 @@ #include "content/public/renderer/render_frame_observer.h" #include "electron/buildflags/buildflags.h" #include "electron/shell/common/api/api.mojom.h" -#include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/bindings/associated_receiver.h" +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" namespace electron { @@ -24,7 +25,8 @@ class ElectronApiServiceImpl : public mojom::ElectronRenderer, ElectronApiServiceImpl(content::RenderFrame* render_frame, RendererClientBase* renderer_client); - void BindTo(mojom::ElectronRendererAssociatedRequest request); + void BindTo( + mojo::PendingAssociatedReceiver receiver); void Message(bool internal, bool send_to_all, @@ -55,7 +57,7 @@ class ElectronApiServiceImpl : public mojom::ElectronRenderer, // Whether the DOM document element has been created. bool document_created_ = false; - mojo::AssociatedBinding binding_; + mojo::AssociatedReceiver receiver_{this}; RendererClientBase* renderer_client_; base::WeakPtrFactory weak_factory_; diff --git a/shell/renderer/atom_autofill_agent.cc b/shell/renderer/electron_autofill_agent.cc similarity index 92% rename from shell/renderer/atom_autofill_agent.cc rename to shell/renderer/electron_autofill_agent.cc index 8cd2f7177b000..ee581e193f4b2 100644 --- a/shell/renderer/atom_autofill_agent.cc +++ b/shell/renderer/electron_autofill_agent.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/renderer/atom_autofill_agent.h" +#include "shell/renderer/electron_autofill_agent.h" #include #include @@ -52,19 +52,17 @@ void TrimStringVectorForIPC(std::vector* strings) { AutofillAgent::AutofillAgent(content::RenderFrame* frame, blink::AssociatedInterfaceRegistry* registry) - : content::RenderFrameObserver(frame), - binding_(this), - weak_ptr_factory_(this) { + : content::RenderFrameObserver(frame), weak_ptr_factory_(this) { render_frame()->GetWebFrame()->SetAutofillClient(this); registry->AddInterface( - base::Bind(&AutofillAgent::BindRequest, base::Unretained(this))); + base::Bind(&AutofillAgent::BindReceiver, base::Unretained(this))); } AutofillAgent::~AutofillAgent() = default; -void AutofillAgent::BindRequest( - mojom::ElectronAutofillAgentAssociatedRequest request) { - binding_.Bind(std::move(request)); +void AutofillAgent::BindReceiver( + mojo::PendingAssociatedReceiver receiver) { + receiver_.Bind(std::move(receiver)); } void AutofillAgent::OnDestruct() { @@ -188,8 +186,7 @@ void AutofillAgent::HidePopup() { void AutofillAgent::ShowPopup(const blink::WebFormControlElement& element, const std::vector& values, const std::vector& labels) { - gfx::RectF bounds = - render_frame()->GetRenderView()->ElementBoundsInWindow(element); + gfx::RectF bounds = render_frame()->ElementBoundsInWindow(element); GetAutofillDriver()->ShowAutofillPopup(bounds, values, labels); } @@ -218,11 +215,11 @@ void AutofillAgent::DoFocusChangeComplete() { focused_node_was_last_clicked_ = false; } -const mojom::ElectronAutofillDriverAssociatedPtr& +const mojo::AssociatedRemote& AutofillAgent::GetAutofillDriver() { if (!autofill_driver_) { render_frame()->GetRemoteAssociatedInterfaces()->GetInterface( - mojo::MakeRequest(&autofill_driver_)); + &autofill_driver_); } return autofill_driver_; diff --git a/shell/renderer/atom_autofill_agent.h b/shell/renderer/electron_autofill_agent.h similarity index 81% rename from shell/renderer/atom_autofill_agent.h rename to shell/renderer/electron_autofill_agent.h index 81026ae7c18e0..9d5a0fb98e57e 100644 --- a/shell/renderer/atom_autofill_agent.h +++ b/shell/renderer/electron_autofill_agent.h @@ -2,14 +2,16 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_RENDERER_ATOM_AUTOFILL_AGENT_H_ -#define SHELL_RENDERER_ATOM_AUTOFILL_AGENT_H_ +#ifndef SHELL_RENDERER_ELECTRON_AUTOFILL_AGENT_H_ +#define SHELL_RENDERER_ELECTRON_AUTOFILL_AGENT_H_ #include #include "base/memory/weak_ptr.h" #include "content/public/renderer/render_frame_observer.h" -#include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/bindings/associated_receiver.h" +#include "mojo/public/cpp/bindings/associated_remote.h" +#include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "shell/common/api/api.mojom.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" #include "third_party/blink/public/web/web_autofill_client.h" @@ -27,7 +29,8 @@ class AutofillAgent : public content::RenderFrameObserver, blink::AssociatedInterfaceRegistry* registry); ~AutofillAgent() override; - void BindRequest(mojom::ElectronAutofillAgentAssociatedRequest request); + void BindReceiver( + mojo::PendingAssociatedReceiver receiver); // content::RenderFrameObserver: void OnDestruct() override; @@ -67,8 +70,9 @@ class AutofillAgent : public content::RenderFrameObserver, void DoFocusChangeComplete(); - const mojom::ElectronAutofillDriverAssociatedPtr& GetAutofillDriver(); - mojom::ElectronAutofillDriverAssociatedPtr autofill_driver_; + const mojo::AssociatedRemote& + GetAutofillDriver(); + mojo::AssociatedRemote autofill_driver_; // True when the last click was on the focused node. bool focused_node_was_last_clicked_ = false; @@ -78,7 +82,7 @@ class AutofillAgent : public content::RenderFrameObserver, // already focused, or if it caused the focus to change. bool was_focused_before_now_ = false; - mojo::AssociatedBinding binding_; + mojo::AssociatedReceiver receiver_{this}; base::WeakPtrFactory weak_ptr_factory_; @@ -87,4 +91,4 @@ class AutofillAgent : public content::RenderFrameObserver, } // namespace electron -#endif // SHELL_RENDERER_ATOM_AUTOFILL_AGENT_H_ +#endif // SHELL_RENDERER_ELECTRON_AUTOFILL_AGENT_H_ diff --git a/shell/renderer/atom_render_frame_observer.cc b/shell/renderer/electron_render_frame_observer.cc similarity index 84% rename from shell/renderer/atom_render_frame_observer.cc rename to shell/renderer/electron_render_frame_observer.cc index 2067ebe636289..0cdbbddd0c0a8 100644 --- a/shell/renderer/atom_render_frame_observer.cc +++ b/shell/renderer/electron_render_frame_observer.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/renderer/atom_render_frame_observer.h" +#include "shell/renderer/electron_render_frame_observer.h" #include #include @@ -33,19 +33,17 @@ namespace electron { namespace { -base::StringPiece NetResourceProvider(int key) { +scoped_refptr NetResourceProvider(int key) { if (key == IDR_DIR_HEADER_HTML) { - base::StringPiece html_data = - ui::ResourceBundle::GetSharedInstance().GetRawDataResource( - IDR_DIR_HEADER_HTML); - return html_data; + return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes( + IDR_DIR_HEADER_HTML); } - return base::StringPiece(); + return nullptr; } } // namespace -AtomRenderFrameObserver::AtomRenderFrameObserver( +ElectronRenderFrameObserver::ElectronRenderFrameObserver( content::RenderFrame* frame, RendererClientBase* renderer_client) : content::RenderFrameObserver(frame), @@ -55,11 +53,11 @@ AtomRenderFrameObserver::AtomRenderFrameObserver( net::NetModule::SetResourceProvider(NetResourceProvider); } -void AtomRenderFrameObserver::DidClearWindowObject() { +void ElectronRenderFrameObserver::DidClearWindowObject() { renderer_client_->DidClearWindowObject(render_frame_); } -void AtomRenderFrameObserver::DidCreateScriptContext( +void ElectronRenderFrameObserver::DidCreateScriptContext( v8::Handle context, int world_id) { if (ShouldNotifyClient(world_id)) @@ -100,14 +98,13 @@ void AtomRenderFrameObserver::DidCreateScriptContext( #endif } -void AtomRenderFrameObserver::DraggableRegionsChanged() { +void ElectronRenderFrameObserver::DraggableRegionsChanged() { blink::WebVector webregions = render_frame_->GetWebFrame()->GetDocument().DraggableRegions(); std::vector regions; for (auto& webregion : webregions) { auto region = mojom::DraggableRegion::New(); - render_frame_->GetRenderView()->ConvertViewportToWindowViaWidget( - &webregion.bounds); + render_frame_->ConvertViewportToWindow(&webregion.bounds); region->bounds = webregion.bounds; region->draggable = webregion.draggable; regions.push_back(std::move(region)); @@ -119,18 +116,18 @@ void AtomRenderFrameObserver::DraggableRegionsChanged() { browser_ptr->UpdateDraggableRegions(std::move(regions)); } -void AtomRenderFrameObserver::WillReleaseScriptContext( +void ElectronRenderFrameObserver::WillReleaseScriptContext( v8::Local context, int world_id) { if (ShouldNotifyClient(world_id)) renderer_client_->WillReleaseScriptContext(context, render_frame_); } -void AtomRenderFrameObserver::OnDestruct() { +void ElectronRenderFrameObserver::OnDestruct() { delete this; } -void AtomRenderFrameObserver::CreateIsolatedWorldContext() { +void ElectronRenderFrameObserver::CreateIsolatedWorldContext() { auto* frame = render_frame_->GetWebFrame(); blink::WebIsolatedWorldInfo info; // This maps to the name shown in the context combo box in the Console tab @@ -146,15 +143,15 @@ void AtomRenderFrameObserver::CreateIsolatedWorldContext() { frame->ExecuteScriptInIsolatedWorld(World::ISOLATED_WORLD, source); } -bool AtomRenderFrameObserver::IsMainWorld(int world_id) { +bool ElectronRenderFrameObserver::IsMainWorld(int world_id) { return world_id == World::MAIN_WORLD; } -bool AtomRenderFrameObserver::IsIsolatedWorld(int world_id) { +bool ElectronRenderFrameObserver::IsIsolatedWorld(int world_id) { return world_id == World::ISOLATED_WORLD; } -bool AtomRenderFrameObserver::ShouldNotifyClient(int world_id) { +bool ElectronRenderFrameObserver::ShouldNotifyClient(int world_id) { bool allow_node_in_sub_frames = base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kNodeIntegrationInSubFrames); diff --git a/shell/renderer/atom_render_frame_observer.h b/shell/renderer/electron_render_frame_observer.h similarity index 81% rename from shell/renderer/atom_render_frame_observer.h rename to shell/renderer/electron_render_frame_observer.h index 6118496501741..137025b1c158a 100644 --- a/shell/renderer/atom_render_frame_observer.h +++ b/shell/renderer/electron_render_frame_observer.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_RENDERER_ATOM_RENDER_FRAME_OBSERVER_H_ -#define SHELL_RENDERER_ATOM_RENDER_FRAME_OBSERVER_H_ +#ifndef SHELL_RENDERER_ELECTRON_RENDER_FRAME_OBSERVER_H_ +#define SHELL_RENDERER_ELECTRON_RENDER_FRAME_OBSERVER_H_ #include @@ -38,10 +38,10 @@ enum World { }; // Helper class to forward the messages to the client. -class AtomRenderFrameObserver : public content::RenderFrameObserver { +class ElectronRenderFrameObserver : public content::RenderFrameObserver { public: - AtomRenderFrameObserver(content::RenderFrame* frame, - RendererClientBase* renderer_client); + ElectronRenderFrameObserver(content::RenderFrame* frame, + RendererClientBase* renderer_client); // content::RenderFrameObserver: void DidClearWindowObject() override; @@ -63,9 +63,9 @@ class AtomRenderFrameObserver : public content::RenderFrameObserver { content::RenderFrame* render_frame_; RendererClientBase* renderer_client_; - DISALLOW_COPY_AND_ASSIGN(AtomRenderFrameObserver); + DISALLOW_COPY_AND_ASSIGN(ElectronRenderFrameObserver); }; } // namespace electron -#endif // SHELL_RENDERER_ATOM_RENDER_FRAME_OBSERVER_H_ +#endif // SHELL_RENDERER_ELECTRON_RENDER_FRAME_OBSERVER_H_ diff --git a/shell/renderer/atom_renderer_client.cc b/shell/renderer/electron_renderer_client.cc similarity index 85% rename from shell/renderer/atom_renderer_client.cc rename to shell/renderer/electron_renderer_client.cc index 0f2ad68db6bca..aa1638848c1ba 100644 --- a/shell/renderer/atom_renderer_client.cc +++ b/shell/renderer/electron_renderer_client.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/renderer/atom_renderer_client.h" +#include "shell/renderer/electron_renderer_client.h" #include #include @@ -18,7 +18,7 @@ #include "shell/common/node_includes.h" #include "shell/common/node_util.h" #include "shell/common/options_switches.h" -#include "shell/renderer/atom_render_frame_observer.h" +#include "shell/renderer/electron_render_frame_observer.h" #include "shell/renderer/web_worker_observer.h" #include "third_party/blink/public/web/web_document.h" #include "third_party/blink/public/web/web_local_frame.h" @@ -34,22 +34,22 @@ bool IsDevToolsExtension(content::RenderFrame* render_frame) { } // namespace -AtomRendererClient::AtomRendererClient() +ElectronRendererClient::ElectronRendererClient() : node_bindings_( NodeBindings::Create(NodeBindings::BrowserEnvironment::RENDERER)), electron_bindings_(new ElectronBindings(uv_default_loop())) {} -AtomRendererClient::~AtomRendererClient() { +ElectronRendererClient::~ElectronRendererClient() { asar::ClearArchives(); } -void AtomRendererClient::RenderFrameCreated( +void ElectronRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { - new AtomRenderFrameObserver(render_frame, this); + new ElectronRenderFrameObserver(render_frame, this); RendererClientBase::RenderFrameCreated(render_frame); } -void AtomRendererClient::RunScriptsAtDocumentStart( +void ElectronRendererClient::RunScriptsAtDocumentStart( content::RenderFrame* render_frame) { RendererClientBase::RunScriptsAtDocumentStart(render_frame); // Inform the document start pharse. @@ -60,7 +60,7 @@ void AtomRendererClient::RunScriptsAtDocumentStart( "document-start"); } -void AtomRendererClient::RunScriptsAtDocumentEnd( +void ElectronRendererClient::RunScriptsAtDocumentEnd( content::RenderFrame* render_frame) { RendererClientBase::RunScriptsAtDocumentEnd(render_frame); // Inform the document end pharse. @@ -71,7 +71,7 @@ void AtomRendererClient::RunScriptsAtDocumentEnd( "document-end"); } -void AtomRendererClient::DidCreateScriptContext( +void ElectronRendererClient::DidCreateScriptContext( v8::Handle renderer_context, content::RenderFrame* render_frame) { RendererClientBase::DidCreateScriptContext(renderer_context, render_frame); @@ -121,7 +121,9 @@ void AtomRendererClient::DidCreateScriptContext( node::tracing::TraceEventHelper::SetAgent(node::CreateAgent()); // Setup node environment for each window. - DCHECK(node::InitializeContext(renderer_context)); + bool initialized = node::InitializeContext(renderer_context); + CHECK(initialized); + node::Environment* env = node_bindings_->CreateEnvironment(renderer_context, nullptr, true); @@ -151,7 +153,7 @@ void AtomRendererClient::DidCreateScriptContext( } } -void AtomRendererClient::WillReleaseScriptContext( +void ElectronRendererClient::WillReleaseScriptContext( v8::Handle context, content::RenderFrame* render_frame) { if (injected_frames_.erase(render_frame) == 0) @@ -174,18 +176,21 @@ void AtomRendererClient::WillReleaseScriptContext( // avoid memory leaks auto* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kNodeIntegrationInSubFrames) || - command_line->HasSwitch(switches::kDisableElectronSiteInstanceOverrides)) + command_line->HasSwitch( + switches::kDisableElectronSiteInstanceOverrides)) { + node::RunAtExit(env); node::FreeEnvironment(env); + } // ElectronBindings is tracking node environments. electron_bindings_->EnvironmentDestroyed(env); } -bool AtomRendererClient::ShouldFork(blink::WebLocalFrame* frame, - const GURL& url, - const std::string& http_method, - bool is_initial_navigation, - bool is_server_redirect) { +bool ElectronRendererClient::ShouldFork(blink::WebLocalFrame* frame, + const GURL& url, + const std::string& http_method, + bool is_initial_navigation, + bool is_server_redirect) { // Handle all the navigations and reloads in browser. // FIXME We only support GET here because http method will be ignored when // the OpenURLFromTab is triggered, which means form posting would not work, @@ -193,7 +198,7 @@ bool AtomRendererClient::ShouldFork(blink::WebLocalFrame* frame, return http_method == "GET"; } -void AtomRendererClient::DidInitializeWorkerContextOnWorkerThread( +void ElectronRendererClient::DidInitializeWorkerContextOnWorkerThread( v8::Local context) { if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kNodeIntegrationInWorker)) { @@ -201,7 +206,7 @@ void AtomRendererClient::DidInitializeWorkerContextOnWorkerThread( } } -void AtomRendererClient::WillDestroyWorkerContextOnWorkerThread( +void ElectronRendererClient::WillDestroyWorkerContextOnWorkerThread( v8::Local context) { if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kNodeIntegrationInWorker)) { @@ -209,9 +214,12 @@ void AtomRendererClient::WillDestroyWorkerContextOnWorkerThread( } } -void AtomRendererClient::SetupMainWorldOverrides( +void ElectronRendererClient::SetupMainWorldOverrides( v8::Handle context, content::RenderFrame* render_frame) { + // We only need to run the isolated bundle if webview is enabled + if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kWebviewTag)) + return; // Setup window overrides in the main world context // Wrap the bundle into a function that receives the isolatedWorld as // an argument. @@ -231,7 +239,7 @@ void AtomRendererClient::SetupMainWorldOverrides( &isolated_bundle_params, &isolated_bundle_args, nullptr); } -void AtomRendererClient::SetupExtensionWorldOverrides( +void ElectronRendererClient::SetupExtensionWorldOverrides( v8::Handle context, content::RenderFrame* render_frame, int world_id) { @@ -259,7 +267,7 @@ void AtomRendererClient::SetupExtensionWorldOverrides( #endif } -node::Environment* AtomRendererClient::GetEnvironment( +node::Environment* ElectronRendererClient::GetEnvironment( content::RenderFrame* render_frame) const { if (injected_frames_.find(render_frame) == injected_frames_.end()) return nullptr; diff --git a/shell/renderer/atom_renderer_client.h b/shell/renderer/electron_renderer_client.h similarity index 88% rename from shell/renderer/atom_renderer_client.h rename to shell/renderer/electron_renderer_client.h index c23710bd3ab0c..00acca0e44576 100644 --- a/shell/renderer/atom_renderer_client.h +++ b/shell/renderer/electron_renderer_client.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_RENDERER_ATOM_RENDERER_CLIENT_H_ -#define SHELL_RENDERER_ATOM_RENDERER_CLIENT_H_ +#ifndef SHELL_RENDERER_ELECTRON_RENDERER_CLIENT_H_ +#define SHELL_RENDERER_ELECTRON_RENDERER_CLIENT_H_ #include #include @@ -21,10 +21,10 @@ namespace electron { class ElectronBindings; class NodeBindings; -class AtomRendererClient : public RendererClientBase { +class ElectronRendererClient : public RendererClientBase { public: - AtomRendererClient(); - ~AtomRendererClient() override; + ElectronRendererClient(); + ~ElectronRendererClient() override; // electron::RendererClientBase: void DidCreateScriptContext(v8::Handle context, @@ -70,9 +70,9 @@ class AtomRendererClient : public RendererClientBase { // assertion, so we have to keep a book of injected web frames. std::set injected_frames_; - DISALLOW_COPY_AND_ASSIGN(AtomRendererClient); + DISALLOW_COPY_AND_ASSIGN(ElectronRendererClient); }; } // namespace electron -#endif // SHELL_RENDERER_ATOM_RENDERER_CLIENT_H_ +#endif // SHELL_RENDERER_ELECTRON_RENDERER_CLIENT_H_ diff --git a/shell/renderer/atom_sandboxed_renderer_client.cc b/shell/renderer/electron_sandboxed_renderer_client.cc similarity index 87% rename from shell/renderer/atom_sandboxed_renderer_client.cc rename to shell/renderer/electron_sandboxed_renderer_client.cc index 3ae11753deca0..ee92aceffa179 100644 --- a/shell/renderer/atom_sandboxed_renderer_client.cc +++ b/shell/renderer/electron_sandboxed_renderer_client.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/renderer/atom_sandboxed_renderer_client.h" +#include "shell/renderer/electron_sandboxed_renderer_client.h" #include "base/base_paths.h" #include "base/command_line.h" @@ -22,7 +22,7 @@ #include "shell/common/node_includes.h" #include "shell/common/node_util.h" #include "shell/common/options_switches.h" -#include "shell/renderer/atom_render_frame_observer.h" +#include "shell/renderer/electron_render_frame_observer.h" #include "third_party/blink/public/web/blink.h" #include "third_party/blink/public/web/web_document.h" #include "third_party/electron_node/src/node_binding.h" @@ -119,15 +119,15 @@ void InvokeHiddenCallback(v8::Handle context, } // namespace -AtomSandboxedRendererClient::AtomSandboxedRendererClient() { +ElectronSandboxedRendererClient::ElectronSandboxedRendererClient() { // Explicitly register electron's builtin modules. NodeBindings::RegisterBuiltinModules(); metrics_ = base::ProcessMetrics::CreateCurrentProcessMetrics(); } -AtomSandboxedRendererClient::~AtomSandboxedRendererClient() = default; +ElectronSandboxedRendererClient::~ElectronSandboxedRendererClient() = default; -void AtomSandboxedRendererClient::InitializeBindings( +void ElectronSandboxedRendererClient::InitializeBindings( v8::Local binding, v8::Local context, bool is_main_frame) { @@ -146,26 +146,20 @@ void AtomSandboxedRendererClient::InitializeBindings( process.SetReadOnly("sandboxed", true); process.SetReadOnly("type", "renderer"); process.SetReadOnly("isMainFrame", is_main_frame); - - // Pass in CLI flags needed to setup the renderer - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(switches::kGuestInstanceID)) - b.Set(options::kGuestInstanceID, - command_line->GetSwitchValueASCII(switches::kGuestInstanceID)); } -void AtomSandboxedRendererClient::RenderFrameCreated( +void ElectronSandboxedRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { - new AtomRenderFrameObserver(render_frame, this); + new ElectronRenderFrameObserver(render_frame, this); RendererClientBase::RenderFrameCreated(render_frame); } -void AtomSandboxedRendererClient::RenderViewCreated( +void ElectronSandboxedRendererClient::RenderViewCreated( content::RenderView* render_view) { RendererClientBase::RenderViewCreated(render_view); } -void AtomSandboxedRendererClient::RunScriptsAtDocumentStart( +void ElectronSandboxedRendererClient::RunScriptsAtDocumentStart( content::RenderFrame* render_frame) { RendererClientBase::RunScriptsAtDocumentStart(render_frame); if (injected_frames_.find(render_frame) == injected_frames_.end()) @@ -181,7 +175,7 @@ void AtomSandboxedRendererClient::RunScriptsAtDocumentStart( InvokeHiddenCallback(context, kLifecycleKey, "onDocumentStart"); } -void AtomSandboxedRendererClient::RunScriptsAtDocumentEnd( +void ElectronSandboxedRendererClient::RunScriptsAtDocumentEnd( content::RenderFrame* render_frame) { RendererClientBase::RunScriptsAtDocumentEnd(render_frame); if (injected_frames_.find(render_frame) == injected_frames_.end()) @@ -197,7 +191,7 @@ void AtomSandboxedRendererClient::RunScriptsAtDocumentEnd( InvokeHiddenCallback(context, kLifecycleKey, "onDocumentEnd"); } -void AtomSandboxedRendererClient::DidCreateScriptContext( +void ElectronSandboxedRendererClient::DidCreateScriptContext( v8::Handle context, content::RenderFrame* render_frame) { RendererClientBase::DidCreateScriptContext(context, render_frame); @@ -240,9 +234,13 @@ void AtomSandboxedRendererClient::DidCreateScriptContext( InvokeHiddenCallback(context, kLifecycleKey, "onLoaded"); } -void AtomSandboxedRendererClient::SetupMainWorldOverrides( +void ElectronSandboxedRendererClient::SetupMainWorldOverrides( v8::Handle context, content::RenderFrame* render_frame) { + // We only need to run the isolated bundle if webview is enabled + if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kWebviewTag)) + return; + // Setup window overrides in the main world context // Wrap the bundle into a function that receives the isolatedWorld as // an argument. @@ -263,7 +261,7 @@ void AtomSandboxedRendererClient::SetupMainWorldOverrides( &isolated_bundle_params, &isolated_bundle_args, nullptr); } -void AtomSandboxedRendererClient::SetupExtensionWorldOverrides( +void ElectronSandboxedRendererClient::SetupExtensionWorldOverrides( v8::Handle context, content::RenderFrame* render_frame, int world_id) { @@ -290,7 +288,7 @@ void AtomSandboxedRendererClient::SetupExtensionWorldOverrides( #endif } -void AtomSandboxedRendererClient::WillReleaseScriptContext( +void ElectronSandboxedRendererClient::WillReleaseScriptContext( v8::Handle context, content::RenderFrame* render_frame) { if (injected_frames_.erase(render_frame) == 0) @@ -302,4 +300,12 @@ void AtomSandboxedRendererClient::WillReleaseScriptContext( InvokeHiddenCallback(context, kLifecycleKey, "onExit"); } +bool ElectronSandboxedRendererClient::ShouldFork(blink::WebLocalFrame* frame, + const GURL& url, + const std::string& http_method, + bool is_initial_navigation, + bool is_server_redirect) { + return true; +} + } // namespace electron diff --git a/shell/renderer/atom_sandboxed_renderer_client.h b/shell/renderer/electron_sandboxed_renderer_client.h similarity index 74% rename from shell/renderer/atom_sandboxed_renderer_client.h rename to shell/renderer/electron_sandboxed_renderer_client.h index 4a84ae5121458..1b92c4ed9e4bf 100644 --- a/shell/renderer/atom_sandboxed_renderer_client.h +++ b/shell/renderer/electron_sandboxed_renderer_client.h @@ -1,8 +1,8 @@ // Copyright (c) 2016 GitHub, Inc. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_RENDERER_ATOM_SANDBOXED_RENDERER_CLIENT_H_ -#define SHELL_RENDERER_ATOM_SANDBOXED_RENDERER_CLIENT_H_ +#ifndef SHELL_RENDERER_ELECTRON_SANDBOXED_RENDERER_CLIENT_H_ +#define SHELL_RENDERER_ELECTRON_SANDBOXED_RENDERER_CLIENT_H_ #include #include @@ -14,10 +14,10 @@ namespace electron { -class AtomSandboxedRendererClient : public RendererClientBase { +class ElectronSandboxedRendererClient : public RendererClientBase { public: - AtomSandboxedRendererClient(); - ~AtomSandboxedRendererClient() override; + ElectronSandboxedRendererClient(); + ~ElectronSandboxedRendererClient() override; void InitializeBindings(v8::Local binding, v8::Local context, @@ -37,6 +37,11 @@ class AtomSandboxedRendererClient : public RendererClientBase { void RenderViewCreated(content::RenderView*) override; void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override; void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override; + bool ShouldFork(blink::WebLocalFrame* frame, + const GURL& url, + const std::string& http_method, + bool is_initial_navigation, + bool is_server_redirect) override; private: std::unique_ptr metrics_; @@ -46,9 +51,9 @@ class AtomSandboxedRendererClient : public RendererClientBase { // assertion, so we have to keep a book of injected web frames. std::set injected_frames_; - DISALLOW_COPY_AND_ASSIGN(AtomSandboxedRendererClient); + DISALLOW_COPY_AND_ASSIGN(ElectronSandboxedRendererClient); }; } // namespace electron -#endif // SHELL_RENDERER_ATOM_SANDBOXED_RENDERER_CLIENT_H_ +#endif // SHELL_RENDERER_ELECTRON_SANDBOXED_RENDERER_CLIENT_H_ diff --git a/shell/renderer/extensions/atom_extensions_renderer_client.cc b/shell/renderer/extensions/electron_extensions_renderer_client.cc similarity index 60% rename from shell/renderer/extensions/atom_extensions_renderer_client.cc rename to shell/renderer/extensions/electron_extensions_renderer_client.cc index 337268f9a85a2..2e8a895ad182f 100644 --- a/shell/renderer/extensions/atom_extensions_renderer_client.cc +++ b/shell/renderer/extensions/electron_extensions_renderer_client.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "shell/renderer/extensions/atom_extensions_renderer_client.h" +#include "shell/renderer/extensions/electron_extensions_renderer_client.h" #include "content/public/renderer/render_thread.h" #include "extensions/renderer/dispatcher.h" @@ -10,20 +10,20 @@ namespace electron { -AtomExtensionsRendererClient::AtomExtensionsRendererClient() +ElectronExtensionsRendererClient::ElectronExtensionsRendererClient() : dispatcher_(std::make_unique( std::make_unique())) { dispatcher_->OnRenderThreadStarted(content::RenderThread::Get()); } -AtomExtensionsRendererClient::~AtomExtensionsRendererClient() {} +ElectronExtensionsRendererClient::~ElectronExtensionsRendererClient() {} -bool AtomExtensionsRendererClient::IsIncognitoProcess() const { +bool ElectronExtensionsRendererClient::IsIncognitoProcess() const { // app_shell doesn't support off-the-record contexts. return false; } -int AtomExtensionsRendererClient::GetLowestIsolatedWorldId() const { +int ElectronExtensionsRendererClient::GetLowestIsolatedWorldId() const { // app_shell doesn't need to reserve world IDs for anything other than // extensions, so we always return 1. Note that 0 is reserved for the global // world. @@ -31,33 +31,33 @@ int AtomExtensionsRendererClient::GetLowestIsolatedWorldId() const { return 10; } -extensions::Dispatcher* AtomExtensionsRendererClient::GetDispatcher() { +extensions::Dispatcher* ElectronExtensionsRendererClient::GetDispatcher() { return dispatcher_.get(); } -bool AtomExtensionsRendererClient::ExtensionAPIEnabledForServiceWorkerScript( - const GURL& scope, - const GURL& script_url) const { +bool ElectronExtensionsRendererClient:: + ExtensionAPIEnabledForServiceWorkerScript(const GURL& scope, + const GURL& script_url) const { // TODO(nornagon): adapt logic from chrome's version return true; } -bool AtomExtensionsRendererClient::AllowPopup() { +bool ElectronExtensionsRendererClient::AllowPopup() { // TODO(samuelmaddock): return false; } -void AtomExtensionsRendererClient::RunScriptsAtDocumentStart( +void ElectronExtensionsRendererClient::RunScriptsAtDocumentStart( content::RenderFrame* render_frame) { dispatcher_->RunScriptsAtDocumentStart(render_frame); } -void AtomExtensionsRendererClient::RunScriptsAtDocumentEnd( +void ElectronExtensionsRendererClient::RunScriptsAtDocumentEnd( content::RenderFrame* render_frame) { dispatcher_->RunScriptsAtDocumentEnd(render_frame); } -void AtomExtensionsRendererClient::RunScriptsAtDocumentIdle( +void ElectronExtensionsRendererClient::RunScriptsAtDocumentIdle( content::RenderFrame* render_frame) { dispatcher_->RunScriptsAtDocumentIdle(render_frame); } diff --git a/shell/renderer/extensions/atom_extensions_renderer_client.h b/shell/renderer/extensions/electron_extensions_renderer_client.h similarity index 71% rename from shell/renderer/extensions/atom_extensions_renderer_client.h rename to shell/renderer/extensions/electron_extensions_renderer_client.h index e38db538fba9d..a24c5154b3019 100644 --- a/shell/renderer/extensions/atom_extensions_renderer_client.h +++ b/shell/renderer/extensions/electron_extensions_renderer_client.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_RENDERER_EXTENSIONS_ATOM_EXTENSIONS_RENDERER_CLIENT_H_ -#define SHELL_RENDERER_EXTENSIONS_ATOM_EXTENSIONS_RENDERER_CLIENT_H_ +#ifndef SHELL_RENDERER_EXTENSIONS_ELECTRON_EXTENSIONS_RENDERER_CLIENT_H_ +#define SHELL_RENDERER_EXTENSIONS_ELECTRON_EXTENSIONS_RENDERER_CLIENT_H_ #include @@ -20,11 +20,11 @@ class Dispatcher; namespace electron { -class AtomExtensionsRendererClient +class ElectronExtensionsRendererClient : public extensions::ExtensionsRendererClient { public: - AtomExtensionsRendererClient(); - ~AtomExtensionsRendererClient() override; + ElectronExtensionsRendererClient(); + ~ElectronExtensionsRendererClient() override; // ExtensionsRendererClient implementation. bool IsIncognitoProcess() const override; @@ -43,9 +43,9 @@ class AtomExtensionsRendererClient private: std::unique_ptr dispatcher_; - DISALLOW_COPY_AND_ASSIGN(AtomExtensionsRendererClient); + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionsRendererClient); }; } // namespace electron -#endif // SHELL_RENDERER_EXTENSIONS_ATOM_EXTENSIONS_RENDERER_CLIENT_H_ +#endif // SHELL_RENDERER_EXTENSIONS_ELECTRON_EXTENSIONS_RENDERER_CLIENT_H_ diff --git a/shell/renderer/renderer_client_base.cc b/shell/renderer/renderer_client_base.cc index 87b3f8d503c20..ca63c859a2c64 100644 --- a/shell/renderer/renderer_client_base.cc +++ b/shell/renderer/renderer_client_base.cc @@ -6,12 +6,13 @@ #include #include +#include #include #include "base/command_line.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" -#include "components/network_hints/renderer/prescient_networking_dispatcher.h" +#include "components/network_hints/renderer/web_prescient_networking_impl.h" #include "content/common/buildflags.h" #include "content/public/common/content_constants.h" #include "content/public/common/content_switches.h" @@ -24,10 +25,11 @@ #include "shell/common/gin_helper/dictionary.h" #include "shell/common/native_mate_converters/value_converter.h" #include "shell/common/options_switches.h" -#include "shell/renderer/atom_autofill_agent.h" -#include "shell/renderer/atom_render_frame_observer.h" +#include "shell/renderer/browser_exposed_renderer_interfaces.h" #include "shell/renderer/content_settings_observer.h" #include "shell/renderer/electron_api_service_impl.h" +#include "shell/renderer/electron_autofill_agent.h" +#include "shell/renderer/electron_render_frame_observer.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" #include "third_party/blink/public/web/blink.h" #include "third_party/blink/public/web/web_custom_element.h" // NOLINT(build/include_alpha) @@ -46,8 +48,13 @@ #include #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "components/spellcheck/renderer/spellcheck.h" +#include "components/spellcheck/renderer/spellcheck_provider.h" +#endif + #if BUILDFLAG(ENABLE_PDF_VIEWER) -#include "shell/common/atom_constants.h" +#include "shell/common/electron_constants.h" #endif // BUILDFLAG(ENABLE_PDF_VIEWER) #if BUILDFLAG(ENABLE_PEPPER_FLASH) @@ -67,8 +74,8 @@ #include "extensions/renderer/guest_view/extensions_guest_view_container.h" #include "extensions/renderer/guest_view/extensions_guest_view_container_dispatcher.h" #include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h" -#include "shell/common/extensions/atom_extensions_client.h" -#include "shell/renderer/extensions/atom_extensions_renderer_client.h" +#include "shell/common/extensions/electron_extensions_client.h" +#include "shell/renderer/extensions/electron_extensions_renderer_client.h" #endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) namespace electron { @@ -143,12 +150,17 @@ void RendererClientBase::RenderThreadStarted() { extensions_client_.reset(CreateExtensionsClient()); extensions::ExtensionsClient::Set(extensions_client_.get()); - extensions_renderer_client_.reset(new AtomExtensionsRendererClient); + extensions_renderer_client_.reset(new ElectronExtensionsRendererClient); extensions::ExtensionsRendererClient::Set(extensions_renderer_client_.get()); thread->AddObserver(extensions_renderer_client_->GetDispatcher()); #endif +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + if (command_line->HasSwitch(switches::kEnableSpellcheck)) + spellcheck_ = std::make_unique(this); +#endif + blink::WebCustomElement::AddEmbedderCustomElementName("webview"); blink::WebCustomElement::AddEmbedderCustomElementName("browserplugin"); @@ -198,9 +210,6 @@ void RendererClientBase::RenderThreadStarted() { blink::WebSecurityPolicy::RegisterURLSchemeAsAllowingServiceWorkers("file"); blink::SchemeRegistry::RegisterURLSchemeAsSupportingFetchAPI("file"); - prescient_networking_dispatcher_ = - std::make_unique(); - #if defined(OS_WIN) // Set ApplicationUserModelID in renderer process. base::string16 app_id = @@ -211,6 +220,13 @@ void RendererClientBase::RenderThreadStarted() { #endif } +void RendererClientBase::ExposeInterfacesToBrowser(mojo::BinderMap* binders) { + // NOTE: Do not add binders directly within this method. Instead, modify the + // definition of |ExposeElectronRendererInterfacesToBrowser()| to ensure + // security review coverage. + ExposeElectronRendererInterfacesToBrowser(this, binders); +} + void RendererClientBase::RenderFrameCreated( content::RenderFrame* render_frame) { #if defined(TOOLKIT_VIEWS) @@ -263,8 +279,28 @@ void RendererClientBase::RenderFrameCreated( dispatcher->OnRenderFrameCreated(render_frame); #endif + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + auto* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kEnableSpellcheck)) + new SpellCheckProvider(render_frame, spellcheck_.get(), this); +#endif } +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +void RendererClientBase::GetInterface( + const std::string& interface_name, + mojo::ScopedMessagePipeHandle interface_pipe) { + // TODO(crbug.com/977637): Get rid of the use of this implementation of + // |service_manager::LocalInterfaceProvider|. This was done only to avoid + // churning spellcheck code while eliminating the "chrome" and + // "chrome_renderer" services. Spellcheck is (and should remain) the only + // consumer of this implementation. + content::RenderThread::Get()->BindHostReceiver( + mojo::GenericPendingReceiver(interface_name, std::move(interface_pipe))); +} +#endif + void RendererClientBase::DidClearWindowObject( content::RenderFrame* render_frame) { // Make sure every page will get a script context created. @@ -308,8 +344,11 @@ void RendererClientBase::DidSetUserAgent(const std::string& user_agent) { #endif } -blink::WebPrescientNetworking* RendererClientBase::GetPrescientNetworking() { - return prescient_networking_dispatcher_.get(); +std::unique_ptr +RendererClientBase::CreatePrescientNetworking( + content::RenderFrame* render_frame) { + return std::make_unique( + render_frame); } void RendererClientBase::RunScriptsAtDocumentStart( @@ -354,7 +393,7 @@ v8::Local RendererClientBase::RunScript( #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) extensions::ExtensionsClient* RendererClientBase::CreateExtensionsClient() { - return new AtomExtensionsClient; + return new ElectronExtensionsClient; } #endif diff --git a/shell/renderer/renderer_client_base.h b/shell/renderer/renderer_client_base.h index d06cf2508d34f..07301ef05e55c 100644 --- a/shell/renderer/renderer_client_base.h +++ b/shell/renderer/renderer_client_base.h @@ -13,15 +13,18 @@ #include "electron/buildflags/buildflags.h" #include "third_party/blink/public/web/web_local_frame.h" // In SHARED_INTERMEDIATE_DIR. -#include "widevine_cdm_version.h" // NOLINT(build/include) +#include "widevine_cdm_version.h" // NOLINT(build/include_directory) #if defined(WIDEVINE_CDM_AVAILABLE) #include "chrome/renderer/media/chrome_key_systems_provider.h" // nogncheck #endif -namespace network_hints { -class PrescientNetworkingDispatcher; -} +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) +#include "services/service_manager/public/cpp/binder_registry.h" +#include "services/service_manager/public/cpp/local_interface_provider.h" + +class SpellCheck; +#endif #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) namespace extensions { @@ -32,14 +35,25 @@ class ExtensionsClient; namespace electron { #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) -class AtomExtensionsRendererClient; +class ElectronExtensionsRendererClient; #endif -class RendererClientBase : public content::ContentRendererClient { +class RendererClientBase : public content::ContentRendererClient +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + , + public service_manager::LocalInterfaceProvider +#endif +{ public: RendererClientBase(); ~RendererClientBase() override; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + // service_manager::LocalInterfaceProvider implementation. + void GetInterface(const std::string& name, + mojo::ScopedMessagePipeHandle request_handle) override; +#endif + virtual void DidCreateScriptContext(v8::Handle context, content::RenderFrame* render_frame); virtual void WillReleaseScriptContext(v8::Handle context, @@ -51,7 +65,8 @@ class RendererClientBase : public content::ContentRendererClient { content::RenderFrame* render_frame, int world_id) = 0; - blink::WebPrescientNetworking* GetPrescientNetworking() override; + std::unique_ptr CreatePrescientNetworking( + content::RenderFrame* render_frame) override; bool isolated_world() const { return isolated_world_; } // Get the context that the Electron API is running in. @@ -65,12 +80,17 @@ class RendererClientBase : public content::ContentRendererClient { bool IsWebViewFrame(v8::Handle context, content::RenderFrame* render_frame) const; +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + SpellCheck* GetSpellCheck() { return spellcheck_.get(); } +#endif + protected: void AddRenderBindings(v8::Isolate* isolate, v8::Local binding_object); // content::ContentRendererClient: void RenderThreadStarted() override; + void ExposeInterfacesToBrowser(mojo::BinderMap* binders) override; void RenderFrameCreated(content::RenderFrame*) override; bool OverrideCreatePlugin(content::RenderFrame* render_frame, const blink::WebPluginParams& params, @@ -93,12 +113,9 @@ class RendererClientBase : public content::ContentRendererClient { #endif private: - std::unique_ptr - prescient_networking_dispatcher_; - #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) std::unique_ptr extensions_client_; - std::unique_ptr extensions_renderer_client_; + std::unique_ptr extensions_renderer_client_; #endif #if defined(WIDEVINE_CDM_AVAILABLE) @@ -108,6 +125,10 @@ class RendererClientBase : public content::ContentRendererClient { std::string renderer_client_id_; // An increasing ID used for indentifying an V8 context in this process. int64_t next_context_id_ = 0; + +#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) + std::unique_ptr spellcheck_; +#endif }; } // namespace electron diff --git a/shell/renderer/web_worker_observer.cc b/shell/renderer/web_worker_observer.cc index 5e10ac8244c22..ffc9d2f8b6676 100644 --- a/shell/renderer/web_worker_observer.cc +++ b/shell/renderer/web_worker_observer.cc @@ -48,7 +48,8 @@ void WebWorkerObserver::ContextCreated(v8::Local worker_context) { node_bindings_->PrepareMessageLoop(); // Setup node environment for each window. - DCHECK(node::InitializeContext(worker_context)); + bool initialized = node::InitializeContext(worker_context); + CHECK(initialized); node::Environment* env = node_bindings_->CreateEnvironment(worker_context, nullptr, true); diff --git a/shell/utility/atom_content_utility_client.cc b/shell/utility/electron_content_utility_client.cc similarity index 69% rename from shell/utility/atom_content_utility_client.cc rename to shell/utility/electron_content_utility_client.cc index efce65cd767ad..57bf1bad4b95e 100644 --- a/shell/utility/atom_content_utility_client.cc +++ b/shell/utility/electron_content_utility_client.cc @@ -2,16 +2,13 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "shell/utility/atom_content_utility_client.h" +#include "shell/utility/electron_content_utility_client.h" #include #include "base/command_line.h" #include "base/no_destructor.h" #include "base/threading/sequenced_task_runner_handle.h" -#include "content/public/child/child_thread.h" -#include "content/public/common/service_manager_connection.h" -#include "content/public/common/simple_connection_filter.h" #include "content/public/utility/utility_thread.h" #include "mojo/public/cpp/bindings/service_factory.h" #include "services/proxy_resolver/proxy_resolver_factory_impl.h" @@ -61,51 +58,40 @@ auto RunProxyResolver( } // namespace -AtomContentUtilityClient::AtomContentUtilityClient() : elevated_(false) { +ElectronContentUtilityClient::ElectronContentUtilityClient() + : utility_process_running_elevated_(false) { #if BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN) printing_handler_ = std::make_unique(); #endif } -AtomContentUtilityClient::~AtomContentUtilityClient() = default; +ElectronContentUtilityClient::~ElectronContentUtilityClient() = default; // The guts of this came from the chromium implementation // https://cs.chromium.org/chromium/src/chrome/utility/ // chrome_content_utility_client.cc?sq=package:chromium&dr=CSs&g=0&l=142 -void AtomContentUtilityClient::UtilityThreadStarted() { +void ElectronContentUtilityClient::ExposeInterfacesToBrowser( + mojo::BinderMap* binders) { #if defined(OS_WIN) base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - elevated_ = command_line->HasSwitch( + utility_process_running_elevated_ = command_line->HasSwitch( service_manager::switches::kNoSandboxAndElevatedPrivileges); -#endif - - content::ServiceManagerConnection* connection = - content::ChildThread::Get()->GetServiceManagerConnection(); - // NOTE: Some utility process instances are not connected to the Service - // Manager. Nothing left to do in that case. - if (!connection) - return; - - auto registry = std::make_unique(); // If our process runs with elevated privileges, only add elevated Mojo - // interfaces to the interface registry. - if (!elevated_) { -#if BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN) - // TODO(crbug.com/798782): remove when the Cloud print chrome/service is - // removed. - registry->AddInterface( + // interfaces to the BinderMap. + if (!utility_process_running_elevated_) { +#if BUILDFLAG(ENABLE_PRINTING) + binders->Add( base::BindRepeating(printing::PdfToEmfConverterFactory::Create), base::ThreadTaskRunnerHandle::Get()); #endif } - - connection->AddConnectionFilter( - std::make_unique(std::move(registry))); +#endif } -bool AtomContentUtilityClient::OnMessageReceived(const IPC::Message& message) { - if (elevated_) +bool ElectronContentUtilityClient::OnMessageReceived( + const IPC::Message& message) { + if (utility_process_running_elevated_) return false; #if BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN) @@ -116,7 +102,8 @@ bool AtomContentUtilityClient::OnMessageReceived(const IPC::Message& message) { return false; } -mojo::ServiceFactory* AtomContentUtilityClient::GetMainThreadServiceFactory() { +mojo::ServiceFactory* +ElectronContentUtilityClient::GetMainThreadServiceFactory() { static base::NoDestructor factory { #if BUILDFLAG(ENABLE_PRINTING) RunPdfCompositor, @@ -128,7 +115,8 @@ mojo::ServiceFactory* AtomContentUtilityClient::GetMainThreadServiceFactory() { return factory.get(); } -mojo::ServiceFactory* AtomContentUtilityClient::GetIOThreadServiceFactory() { +mojo::ServiceFactory* +ElectronContentUtilityClient::GetIOThreadServiceFactory() { static base::NoDestructor factory{RunProxyResolver}; return factory.get(); } diff --git a/shell/utility/atom_content_utility_client.h b/shell/utility/electron_content_utility_client.h similarity index 57% rename from shell/utility/atom_content_utility_client.h rename to shell/utility/electron_content_utility_client.h index 90450c14c9206..867a924bb9115 100644 --- a/shell/utility/atom_content_utility_client.h +++ b/shell/utility/electron_content_utility_client.h @@ -2,8 +2,8 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#ifndef SHELL_UTILITY_ATOM_CONTENT_UTILITY_CLIENT_H_ -#define SHELL_UTILITY_ATOM_CONTENT_UTILITY_CLIENT_H_ +#ifndef SHELL_UTILITY_ELECTRON_CONTENT_UTILITY_CLIENT_H_ +#define SHELL_UTILITY_ELECTRON_CONTENT_UTILITY_CLIENT_H_ #include #include @@ -11,6 +11,7 @@ #include "base/compiler_specific.h" #include "content/public/utility/content_utility_client.h" +#include "mojo/public/cpp/bindings/binder_map.h" #include "printing/buildflags/buildflags.h" #if BUILDFLAG(ENABLE_PRINTING) && defined(OS_WIN) @@ -19,12 +20,12 @@ namespace electron { -class AtomContentUtilityClient : public content::ContentUtilityClient { +class ElectronContentUtilityClient : public content::ContentUtilityClient { public: - AtomContentUtilityClient(); - ~AtomContentUtilityClient() override; + ElectronContentUtilityClient(); + ~ElectronContentUtilityClient() override; - void UtilityThreadStarted() override; + void ExposeInterfacesToBrowser(mojo::BinderMap* binders) override; bool OnMessageReceived(const IPC::Message& message) override; mojo::ServiceFactory* GetMainThreadServiceFactory() override; mojo::ServiceFactory* GetIOThreadServiceFactory() override; @@ -34,11 +35,12 @@ class AtomContentUtilityClient : public content::ContentUtilityClient { std::unique_ptr printing_handler_; #endif - bool elevated_; + // True if the utility process runs with elevated privileges. + bool utility_process_running_elevated_; - DISALLOW_COPY_AND_ASSIGN(AtomContentUtilityClient); + DISALLOW_COPY_AND_ASSIGN(ElectronContentUtilityClient); }; } // namespace electron -#endif // SHELL_UTILITY_ATOM_CONTENT_UTILITY_CLIENT_H_ +#endif // SHELL_UTILITY_ELECTRON_CONTENT_UTILITY_CLIENT_H_ diff --git a/spec-main/api-app-spec.ts b/spec-main/api-app-spec.ts index 62946cd0ca0be..726096759b9d3 100644 --- a/spec-main/api-app-spec.ts +++ b/spec-main/api-app-spec.ts @@ -2,14 +2,15 @@ import * as chai from 'chai' import * as chaiAsPromised from 'chai-as-promised' import * as cp from 'child_process' import * as https from 'https' +import * as http from 'http' import * as net from 'net' import * as fs from 'fs' import * as path from 'path' import { homedir } from 'os' import split = require('split') -import { app, BrowserWindow, Menu } from 'electron' +import { app, BrowserWindow, Menu, session } from 'electron' import { emittedOnce } from './events-helpers'; -import { closeWindow } from './window-helpers'; +import { closeWindow, closeAllWindows } from './window-helpers'; import { ifdescribe } from './spec-helpers'; const features = process.electronBinding('features') @@ -316,6 +317,15 @@ describe('app module', () => { }) }) + describe('certificate-error event', () => { + afterEach(closeAllWindows) + it('is emitted when visiting a server with a self-signed cert', async () => { + const w = new BrowserWindow({ show: false }) + w.loadURL(secureUrl) + await emittedOnce(app, 'certificate-error') + }) + }) + // xdescribe('app.importCertificate', () => { // let w = null @@ -726,6 +736,14 @@ describe('app module', () => { }) describe('setPath(name, path)', () => { + it('throws when a relative path is passed', () => { + const badPath = 'hey/hi/hello' + + expect(() => { + app.setPath('music', badPath) + }).to.throw(/Path must be absolute/) + }) + it('does not create a new directory by default', () => { const badPath = path.join(__dirname, 'music') @@ -737,6 +755,16 @@ describe('app module', () => { }) }) + describe('setAppLogsPath(path)', () => { + it('throws when a relative path is passed', () => { + const badPath = 'hey/hi/hello' + + expect(() => { + app.setAppLogsPath(badPath) + }).to.throw(/Path must be absolute/) + }) + }) + describe('select-client-certificate event', () => { let w: BrowserWindow @@ -744,6 +772,7 @@ describe('app module', () => { if (process.platform === 'linux') { this.skip() } + session.fromPartition('empty-certificate').setCertificateVerifyProc((req, cb) => { cb(0) }) }) beforeEach(() => { @@ -758,6 +787,8 @@ describe('app module', () => { afterEach(() => closeWindow(w).then(() => { w = null as any })) + after(() => session.fromPartition('empty-certificate').setCertificateVerifyProc(null)) + it('can respond with empty certificate list', async () => { app.once('select-client-certificate', function (event, webContents, url, list, callback) { console.log('select-client-certificate emitted') @@ -883,6 +914,40 @@ describe('app module', () => { }) }) }) + + it('sets the default client such that getApplicationNameForProtocol returns Electron', () => { + app.setAsDefaultProtocolClient(protocol) + expect(app.getApplicationNameForProtocol(`${protocol}://`)).to.equal('Electron') + }) + }) + + describe('getApplicationNameForProtocol()', () => { + it('returns application names for common protocols', function () { + // We can't expect particular app names here, but these protocols should + // at least have _something_ registered. Except on our Linux CI + // environment apparently. + if (process.platform === 'linux') { + this.skip() + } + + const protocols = [ + 'http://', + 'https://' + ] + protocols.forEach((protocol) => { + expect(app.getApplicationNameForProtocol(protocol)).to.not.equal('') + }) + }) + + it('returns an empty string for a bogus protocol', () => { + expect(app.getApplicationNameForProtocol('bogus-protocol://')).to.equal('') + }) + }) + + describe('isDefaultProtocolClient()', () => { + it('returns false for a bogus protocol', () => { + expect(app.isDefaultProtocolClient('bogus-protocol://')).to.equal(false) + }) }) describe('app launch through uri', () => { @@ -1426,6 +1491,33 @@ describe('default behavior', () => { expect(output[0]).to.equal(output[1]) }) }) + + describe('login event', () => { + afterEach(closeAllWindows) + let server: http.Server + let serverUrl: string + + before((done) => { + server = http.createServer((request, response) => { + if (request.headers.authorization) { + return response.end('ok') + } + response + .writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }) + .end() + }).listen(0, '127.0.0.1', () => { + serverUrl = 'http://127.0.0.1:' + (server.address() as net.AddressInfo).port + done() + }) + }) + + it('should emit a login event on app when a WebContents hits a 401', async () => { + const w = new BrowserWindow({ show: false }) + w.loadURL(serverUrl) + const [, webContents] = await emittedOnce(app, 'login') + expect(webContents).to.equal(w.webContents) + }) + }) }) async function runTestApp (name: string, ...args: any[]) { diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index 53d943c4f0a49..8f6ce7d590a1e 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -18,6 +18,7 @@ const { expect } = chai chai.use(chaiAsPromised) chai.use(dirtyChai) +const features = process.electronBinding('features') const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures') // Is the display's scale factor possibly causing rounding of pixel coordinate @@ -195,7 +196,7 @@ describe('BrowserWindow module', () => { }).to.throw('Object has been destroyed') }) it('should not crash when destroying windows with pending events', () => { - const focusListener = () => {} + const focusListener = () => { } app.on('browser-window-focus', focusListener) const windowCount = 3 const windowOptions = { @@ -266,7 +267,7 @@ describe('BrowserWindow module', () => { fs.readFile(filePath, (err, data) => { if (err) return if (parsedData.username === 'test' && - parsedData.file === data.toString()) { + parsedData.file === data.toString()) { res.end() } }) @@ -299,7 +300,9 @@ describe('BrowserWindow module', () => { w.on('ready-to-show', () => { done() }) w.loadURL('about:blank') }) - it('should emit did-fail-load event for files that do not exist', (done) => { + // TODO(deepak1556): The error code now seems to be `ERR_FAILED`, verify what + // changed and adjust the test. + it.skip('should emit did-fail-load event for files that do not exist', (done) => { w.webContents.on('did-fail-load', (event, code, desc, url, isMainFrame) => { expect(code).to.equal(-6) expect(desc).to.equal('ERR_FILE_NOT_FOUND') @@ -417,110 +420,168 @@ describe('BrowserWindow module', () => { }) }) - describe('navigation events', () => { - let w = null as unknown as BrowserWindow - beforeEach(() => { - w = new BrowserWindow({show: false, webPreferences: {nodeIntegration: true}}) - }) - afterEach(async () => { - await closeWindow(w) - w = null as unknown as BrowserWindow - }) + for (const sandbox of [false, true]) { + describe(`navigation events${sandbox ? ' with sandbox' : ''}`, () => { + let w = null as unknown as BrowserWindow + beforeEach(() => { + w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: false, sandbox } }) + }) + afterEach(async () => { + await closeWindow(w) + w = null as unknown as BrowserWindow + }) - describe('will-navigate event', () => { - it('allows the window to be closed from the event listener', (done) => { - w.webContents.once('will-navigate', () => { - w.close() - done() + describe('will-navigate event', () => { + let server = null as unknown as http.Server + let url = null as unknown as string + before((done) => { + server = http.createServer((req, res) => { res.end('') }) + server.listen(0, '127.0.0.1', () => { + url = `http://127.0.0.1:${(server.address() as AddressInfo).port}/` + done() + }) }) - w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html')) - }) - }) - describe('will-redirect event', () => { - let server = null as unknown as http.Server - let url = null as unknown as string - before((done) => { - server = http.createServer((req, res) => { - if (req.url === '/302') { - res.setHeader('Location', '/200') - res.statusCode = 302 - res.end() - } else if (req.url === '/navigate-302') { - res.end(``) - } else { - res.end() - } + after(() => { + server.close() }) - server.listen(0, '127.0.0.1', () => { - url = `http://127.0.0.1:${(server.address() as AddressInfo).port}` - done() + + it('allows the window to be closed from the event listener', (done) => { + w.webContents.once('will-navigate', () => { + w.close() + done() + }) + w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html')) }) - }) - after(() => { - server.close() - }) - it('is emitted on redirects', (done) => { - w.webContents.on('will-redirect', (event, url) => { - done() + it('can be prevented', (done) => { + let willNavigate = false + w.webContents.once('will-navigate', (e) => { + willNavigate = true + e.preventDefault() + }) + w.webContents.on('did-stop-loading', () => { + if (willNavigate) { + // i.e. it shouldn't have had '?navigated' appended to it. + expect(w.webContents.getURL().endsWith('will-navigate.html')).to.be.true() + done() + } + }) + w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html')) }) - w.loadURL(`${url}/302`) - }) - it('is emitted after will-navigate on redirects', (done) => { - let navigateCalled = false - w.webContents.on('will-navigate', () => { - navigateCalled = true + it('is triggered when navigating from file: to http:', async () => { + await w.loadFile(path.join(fixtures, 'api', 'blank.html')) + w.webContents.executeJavaScript(`location.href = ${JSON.stringify(url)}`) + const navigatedTo = await new Promise(resolve => { + w.webContents.once('will-navigate', (e, url) => { + e.preventDefault() + resolve(url) + }) + }) + expect(navigatedTo).to.equal(url) + expect(w.webContents.getURL()).to.match(/^file:/) }) - w.webContents.on('will-redirect', (event, url) => { - expect(navigateCalled).to.equal(true, 'should have called will-navigate first') - done() + + it('is triggered when navigating from about:blank to http:', async () => { + await w.loadURL('about:blank') + w.webContents.executeJavaScript(`location.href = ${JSON.stringify(url)}`) + const navigatedTo = await new Promise(resolve => { + w.webContents.once('will-navigate', (e, url) => { + e.preventDefault() + resolve(url) + }) + }) + expect(navigatedTo).to.equal(url) + expect(w.webContents.getURL()).to.equal('about:blank') + }) + }) + + describe('will-redirect event', () => { + let server = null as unknown as http.Server + let url = null as unknown as string + before((done) => { + server = http.createServer((req, res) => { + if (req.url === '/302') { + res.setHeader('Location', '/200') + res.statusCode = 302 + res.end() + } else if (req.url === '/navigate-302') { + res.end(``) + } else { + res.end() + } + }) + server.listen(0, '127.0.0.1', () => { + url = `http://127.0.0.1:${(server.address() as AddressInfo).port}` + done() + }) }) - w.loadURL(`${url}/navigate-302`) - }) - it('is emitted before did-stop-loading on redirects', (done) => { - let stopCalled = false - w.webContents.on('did-stop-loading', () => { - stopCalled = true + after(() => { + server.close() }) - w.webContents.on('will-redirect', (event, url) => { - expect(stopCalled).to.equal(false, 'should not have called did-stop-loading first') - done() + it('is emitted on redirects', (done) => { + w.webContents.on('will-redirect', () => { + done() + }) + w.loadURL(`${url}/302`) }) - w.loadURL(`${url}/302`) - }) - it('allows the window to be closed from the event listener', (done) => { - w.webContents.once('will-redirect', (event, input) => { - w.close() - done() + it('is emitted after will-navigate on redirects', (done) => { + let navigateCalled = false + w.webContents.on('will-navigate', () => { + navigateCalled = true + }) + w.webContents.on('will-redirect', () => { + expect(navigateCalled).to.equal(true, 'should have called will-navigate first') + done() + }) + w.loadURL(`${url}/navigate-302`) }) - w.loadURL(`${url}/302`) - }) - it('can be prevented', (done) => { - w.webContents.once('will-redirect', (event) => { - event.preventDefault() - }) - w.webContents.on('will-navigate', (e, u) => { - expect(u).to.equal(`${url}/302`) + it('is emitted before did-stop-loading on redirects', (done) => { + let stopCalled = false + w.webContents.on('did-stop-loading', () => { + stopCalled = true + }) + w.webContents.on('will-redirect', () => { + expect(stopCalled).to.equal(false, 'should not have called did-stop-loading first') + done() + }) + w.loadURL(`${url}/302`) }) - w.webContents.on('did-stop-loading', () => { - expect(w.webContents.getURL()).to.equal( - `${url}/navigate-302`, - 'url should not have changed after navigation event' - ) - done() + + it('allows the window to be closed from the event listener', (done) => { + w.webContents.once('will-redirect', () => { + w.close() + done() + }) + w.loadURL(`${url}/302`) }) - w.webContents.on('will-redirect', (e, u) => { - expect(u).to.equal(`${url}/200`) + + it('can be prevented', (done) => { + w.webContents.once('will-redirect', (event) => { + event.preventDefault() + }) + w.webContents.on('will-navigate', (e, u) => { + expect(u).to.equal(`${url}/302`) + }) + w.webContents.on('did-stop-loading', () => { + expect(w.webContents.getURL()).to.equal( + `${url}/navigate-302`, + 'url should not have changed after navigation event' + ) + done() + }) + w.webContents.on('will-redirect', (e, u) => { + expect(u).to.equal(`${url}/200`) + }) + w.loadURL(`${url}/navigate-302`) }) - w.loadURL(`${url}/navigate-302`) }) }) - }) + } describe('focus and visibility', () => { let w = null as unknown as BrowserWindow @@ -601,13 +662,11 @@ describe('BrowserWindow module', () => { }) describe('BrowserWindow.getFocusedWindow()', () => { - it('returns the opener window when dev tools window is focused', (done) => { + it('returns the opener window when dev tools window is focused', async () => { w.show() - w.webContents.once('devtools-focused', () => { - expect(BrowserWindow.getFocusedWindow()).to.equal(w) - done() - }) w.webContents.openDevTools({ mode: 'undocked' }) + await emittedOnce(w.webContents, 'devtools-focused') + expect(BrowserWindow.getFocusedWindow()).to.equal(w) }) }) @@ -653,7 +712,7 @@ describe('BrowserWindow module', () => { }) }) - describe('BrowserWindow.moveAbove(mediaSourceId)', () => { + ifdescribe(features.isDesktopCapturerEnabled())('BrowserWindow.moveAbove(mediaSourceId)', () => { it('should throw an exception if wrong formatting', async () => { const fakeSourceIds = [ 'none', 'screen:0', 'window:fake', 'window:1234', 'foobar:1:2' @@ -695,6 +754,17 @@ describe('BrowserWindow module', () => { await closeWindow(w2, { assertNotWindows: false }) }) }) + + describe('BrowserWindow.setFocusable()', () => { + it('can set unfocusable window to focusable', async () => { + const w2 = new BrowserWindow({ focusable: false }) + const w2Focused = emittedOnce(w2, 'focus') + w2.setFocusable(true) + w2.focus() + await w2Focused + await closeWindow(w2, { assertNotWindows: false }) + }) + }) }) describe('sizing', () => { @@ -1250,6 +1320,31 @@ describe('BrowserWindow module', () => { }) }) + ifdescribe(process.platform === 'darwin')('BrowserWindow.getTrafficLightPosition(pos)', () => { + afterEach(closeAllWindows) + + it('gets the set traffic light position property', () => { + const pos = { x: 10, y: 10 } + const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos }) + const currentPosition = w.getTrafficLightPosition() + + expect(currentPosition).to.deep.equal(pos) + }) + }) + + ifdescribe(process.platform === 'darwin')('BrowserWindow.setTrafficLightPosition(pos)', () => { + afterEach(closeAllWindows) + + it('can set the traffic light position property', () => { + const pos = { x: 10, y: 10 } + const w = new BrowserWindow({ show: false, titleBarStyle: 'hidden', trafficLightPosition: pos }) + w.setTrafficLightPosition(pos) + const currentPosition = w.getTrafficLightPosition() + + expect(currentPosition).to.deep.equal(pos) + }) + }) + ifdescribe(process.platform === 'win32')('BrowserWindow.setAppDetails(options)', () => { afterEach(closeAllWindows) @@ -1515,7 +1610,7 @@ describe('BrowserWindow module', () => { afterEach(closeAllWindows) describe('"preload" option', () => { - const doesNotLeakSpec = (name: string, webPrefs: {nodeIntegration: boolean, sandbox: boolean, contextIsolation: boolean}) => { + const doesNotLeakSpec = (name: string, webPrefs: { nodeIntegration: boolean, sandbox: boolean, contextIsolation: boolean }) => { it(name, async () => { const w = new BrowserWindow({ webPreferences: { @@ -1553,6 +1648,37 @@ describe('BrowserWindow module', () => { sandbox: true, contextIsolation: true }) + it('does not leak any node globals on the window object with nodeIntegration is disabled', async () => { + let w = new BrowserWindow({ + webPreferences: { + contextIsolation: false, + nodeIntegration: false, + preload: path.resolve(fixtures, 'module', 'empty.js') + }, + show: false + }) + w.loadFile(path.join(fixtures, 'api', 'globals.html')) + const [, notIsolated] = await emittedOnce(ipcMain, 'leak-result') + expect(notIsolated).to.have.property('globals') + + w.destroy() + w = new BrowserWindow({ + webPreferences: { + contextIsolation: true, + nodeIntegration: false, + preload: path.resolve(fixtures, 'module', 'empty.js') + }, + show: false + }) + w.loadFile(path.join(fixtures, 'api', 'globals.html')) + const [, isolated] = await emittedOnce(ipcMain, 'leak-result') + expect(isolated).to.have.property('globals') + const notIsolatedGlobals = new Set(notIsolated.globals) + for (const isolatedGlobal of isolated.globals) { + notIsolatedGlobals.delete(isolatedGlobal) + } + expect([...notIsolatedGlobals]).to.deep.equal([], 'non-isoalted renderer should have no additional globals') + }) it('loads the script before other scripts in window', async () => { const preload = path.join(fixtures, 'module', 'set-global.js') @@ -1730,7 +1856,7 @@ describe('BrowserWindow module', () => { }) describe('"sandbox" option', () => { - function waitForEvents(emitter: {once: Function}, events: string[], callback: () => void) { + function waitForEvents (emitter: { once: Function }, events: string[], callback: () => void) { let count = events.length for (const event of events) { emitter.once(event, () => { @@ -1997,8 +2123,7 @@ describe('BrowserWindow module', () => { 'did-finish-load', 'did-frame-finish-load', 'did-navigate-in-page', - // TODO(nornagon): sandboxed pages should also emit will-navigate - // 'will-navigate', + 'will-navigate', 'did-start-loading', 'did-stop-loading', 'did-frame-finish-load', @@ -2709,7 +2834,7 @@ describe('BrowserWindow module', () => { return } if (rect.height === contentHeight && rect.width === contentWidth && - !gotInitialFullSizeFrame) { + !gotInitialFullSizeFrame) { // The initial frame is full-size, but we're looking for a call // with just the dirty-rect. The next frame should be a smaller // rect. @@ -2794,6 +2919,13 @@ describe('BrowserWindow module', () => { w.restore() expectBoundsEqual(w.getSize(), initialSize) }) + + it('does not crash when restoring hidden minimized window', () => { + const w = new BrowserWindow({}) + w.minimize() + w.hide() + w.show() + }) }) describe('BrowserWindow.unmaximize()', () => { @@ -2944,15 +3076,53 @@ describe('BrowserWindow module', () => { }) // The isEnabled API is not reliable on macOS. - ifdescribe(process.platform !== 'darwin')('modal option', () => { - it('disables parent window', () => { + describe('modal option', () => { + it('does not crash', async () => { + const parentWindow = new BrowserWindow() + + const createTwo = async () => { + const two = new BrowserWindow({ + width: 300, + height: 200, + parent: parentWindow, + modal: true, + show: false + }) + + const twoShown = emittedOnce(two, 'show') + two.show() + await twoShown + setTimeout(() => two.close(), 500) + + await emittedOnce(two, 'closed') + } + + const one = new BrowserWindow({ + width: 600, + height: 400, + parent: parentWindow, + modal: true, + show: false + }) + + const oneShown = emittedOnce(one, 'show') + one.show() + await oneShown + setTimeout(() => one.destroy(), 500) + + await emittedOnce(one, 'closed') + await createTwo() + }) + + ifit(process.platform !== 'darwin')('disables parent window', () => { const w = new BrowserWindow({show: false}) const c = new BrowserWindow({ show: false, parent: w, modal: true }) expect(w.isEnabled()).to.be.true('w.isEnabled') c.show() expect(w.isEnabled()).to.be.false('w.isEnabled') }) - it('re-enables an enabled parent window when closed', (done) => { + + ifit(process.platform !== 'darwin')('re-enables an enabled parent window when closed', (done) => { const w = new BrowserWindow({show: false}) const c = new BrowserWindow({ show: false, parent: w, modal: true }) c.once('closed', () => { @@ -2962,7 +3132,8 @@ describe('BrowserWindow module', () => { c.show() c.close() }) - it('does not re-enable a disabled parent window when closed', (done) => { + + ifit(process.platform !== 'darwin')('does not re-enable a disabled parent window when closed', (done) => { const w = new BrowserWindow({show: false}) const c = new BrowserWindow({ show: false, parent: w, modal: true }) c.once('closed', () => { @@ -2973,7 +3144,8 @@ describe('BrowserWindow module', () => { c.show() c.close() }) - it('disables parent window recursively', () => { + + ifit(process.platform !== 'darwin')('disables parent window recursively', () => { const w = new BrowserWindow({show: false}) const c = new BrowserWindow({ show: false, parent: w, modal: true }) const c2 = new BrowserWindow({ show: false, parent: w, modal: true }) @@ -3829,8 +4001,9 @@ describe('BrowserWindow module', () => { }) }) - const features = process.electronBinding('features') - ifdescribe(features.isOffscreenRenderingEnabled())('offscreen rendering', () => { + const skip = features.isOffscreenRenderingEnabled() && + (process.platform !== 'linux' || process.arch !== 'ia32') + ifdescribe(skip)('offscreen rendering', () => { let w: BrowserWindow beforeEach(function () { w = new BrowserWindow({ diff --git a/spec-main/api-context-bridge-spec.ts b/spec-main/api-context-bridge-spec.ts index 1a424331444b8..8ace14b796e3f 100644 --- a/spec-main/api-context-bridge-spec.ts +++ b/spec-main/api-context-bridge-spec.ts @@ -1,6 +1,8 @@ import { contextBridge, BrowserWindow, ipcMain } from 'electron' import { expect } from 'chai' import * as fs from 'fs-extra' +import * as http from 'http' +import { AddressInfo } from 'net' import * as os from 'os' import * as path from 'path' @@ -12,6 +14,20 @@ const fixturesPath = path.resolve(__dirname, 'fixtures', 'api', 'context-bridge' describe('contextBridge', () => { let w: BrowserWindow let dir: string + let server: http.Server + + before(async () => { + server = http.createServer((req, res) => { + res.setHeader('Content-Type', 'text/html') + res.end('') + }) + await new Promise(resolve => server.listen(0, resolve)) + }) + + after(async () => { + if (server) await new Promise(resolve => server.close(resolve)) + server = null as any + }) afterEach(async () => { await closeWindow(w) @@ -61,10 +77,11 @@ describe('contextBridge', () => { contextIsolation: true, nodeIntegration: true, sandbox: useSandbox, - preload: path.resolve(tmpDir, 'preload.js') + preload: path.resolve(tmpDir, 'preload.js'), + additionalArguments: ['--unsafely-expose-electron-internals-for-testing'] } }) - await w.loadFile(path.resolve(fixturesPath, 'empty.html')) + await w.loadURL(`http://127.0.0.1:${(server.address() as AddressInfo).port}`) } const callWithBindings = async (fn: Function) => { @@ -72,15 +89,20 @@ describe('contextBridge', () => { } const getGCInfo = async (): Promise<{ - functionCount: number - objectCount: number - liveFromValues: number - liveProxyValues: number + trackedValues: number; }> => { - const [,info] = await emittedOnce(ipcMain, 'gc-info', () => w.webContents.send('get-gc-info')) - return info - } - + const [, info] = await emittedOnce(ipcMain, 'gc-info', () => w.webContents.send('get-gc-info')); + return info; + }; + + const forceGCOnWindow = async () => { + w.webContents.debugger.attach(); + await w.webContents.debugger.sendCommand('HeapProfiler.enable'); + await w.webContents.debugger.sendCommand('HeapProfiler.collectGarbage'); + await w.webContents.debugger.sendCommand('HeapProfiler.disable'); + w.webContents.debugger.detach(); + }; + it('should proxy numbers', async () => { await makeBindingWindow(() => { contextBridge.exposeInMainWorld('example', { @@ -317,193 +339,63 @@ describe('contextBridge', () => { }) expect(result).to.deep.equal([135, 135, 135]) }) - - it('it should follow expected simple rules of object identity', async () => { - await makeBindingWindow(() => { - const o: any = { value: 135 } - const sub = { thing: 7 } - o.a = sub - o.b = sub - contextBridge.exposeInMainWorld('example', { - o, - }) - }) - const result = await callWithBindings((root: any) => { - return root.example.a === root.example.b - }) - expect(result).to.equal(true) - }) - - it('it should follow expected complex rules of object identity', async () => { - await makeBindingWindow(() => { - let first: any = null - contextBridge.exposeInMainWorld('example', { - check: (arg: any) => { - if (first === null) { - first = arg - } else { - return first === arg - } - }, - }) - }) - const result = await callWithBindings((root: any) => { - const o = { thing: 123 } - root.example.check(o) - return root.example.check(o) - }) - expect(result).to.equal(true) - }) - // Can only run tests which use the GCRunner in non-sandboxed environments if (!useSandbox) { it('should release the global hold on methods sent across contexts', async () => { await makeBindingWindow(() => { - require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', (contextBridge as any).debugGC())) + require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: process.electronBinding('v8_util').getWeaklyTrackedValues().length })); + const { weaklyTrackValue } = process.electronBinding('v8_util'); contextBridge.exposeInMainWorld('example', { - getFunction: () => () => 123 - }) - }) - expect((await getGCInfo()).functionCount).to.equal(2) + getFunction: () => () => 123, + track: weaklyTrackValue + }); + }); await callWithBindings(async (root: any) => { - root.x = [root.example.getFunction()] - }) - expect((await getGCInfo()).functionCount).to.equal(3) + root.GCRunner.run(); + }); + expect((await getGCInfo()).trackedValues).to.equal(0); await callWithBindings(async (root: any) => { - root.x = [] - root.GCRunner.run() - }) - expect((await getGCInfo()).functionCount).to.equal(2) - }) - - it('should release the global hold on objects sent across contexts when the object proxy is de-reffed', async () => { - await makeBindingWindow(() => { - require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', (contextBridge as any).debugGC())) - let myObj: any - contextBridge.exposeInMainWorld('example', { - setObj: (o: any) => { - myObj = o - }, - getObj: () => myObj - }) - }) - await callWithBindings(async (root: any) => { - root.GCRunner.run() - }) - // Initial Setup - let info = await getGCInfo() - expect(info.liveFromValues).to.equal(3) - expect(info.liveProxyValues).to.equal(3) - expect(info.objectCount).to.equal(6) - - // Create Reference - await callWithBindings(async (root: any) => { - root.x = { value: 123 } - root.example.setObj(root.x) - root.GCRunner.run() - }) - info = await getGCInfo() - expect(info.liveFromValues).to.equal(4) - expect(info.liveProxyValues).to.equal(4) - expect(info.objectCount).to.equal(8) - - // Release Reference - await callWithBindings(async (root: any) => { - root.example.setObj(null) - root.GCRunner.run() - }) - info = await getGCInfo() - expect(info.liveFromValues).to.equal(3) - expect(info.liveProxyValues).to.equal(3) - expect(info.objectCount).to.equal(6) - }) - - it('should release the global hold on objects sent across contexts when the object source is de-reffed', async () => { - await makeBindingWindow(() => { - require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', (contextBridge as any).debugGC())) - let myObj: any; - contextBridge.exposeInMainWorld('example', { - setObj: (o: any) => { - myObj = o - }, - getObj: () => myObj - }) - }) - await callWithBindings(async (root: any) => { - root.GCRunner.run() - }) - // Initial Setup - let info = await getGCInfo() - expect(info.liveFromValues).to.equal(3) - expect(info.liveProxyValues).to.equal(3) - expect(info.objectCount).to.equal(6) - - // Create Reference + const fn = root.example.getFunction(); + root.example.track(fn); + root.x = [fn]; + }); + expect((await getGCInfo()).trackedValues).to.equal(1); await callWithBindings(async (root: any) => { - root.x = { value: 123 } - root.example.setObj(root.x) - root.GCRunner.run() - }) - info = await getGCInfo() - expect(info.liveFromValues).to.equal(4) - expect(info.liveProxyValues).to.equal(4) - expect(info.objectCount).to.equal(8) - - // Release Reference - await callWithBindings(async (root: any) => { - delete root.x - root.GCRunner.run() - }) - info = await getGCInfo() - expect(info.liveFromValues).to.equal(3) - expect(info.liveProxyValues).to.equal(3) - expect(info.objectCount).to.equal(6) - }) + root.x = []; + root.GCRunner.run(); + }); + expect((await getGCInfo()).trackedValues).to.equal(0); + }); + } - it('should not crash when the object source is de-reffed AND the object proxy is de-reffed', async () => { + if (useSandbox) { + it('should not leak the global hold on methods sent across contexts when reloading a sandboxed renderer', async () => { await makeBindingWindow(() => { - require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', (contextBridge as any).debugGC())) - let myObj: any; + require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: process.electronBinding('v8_util').getWeaklyTrackedValues().length })); + const { weaklyTrackValue } = process.electronBinding('v8_util'); contextBridge.exposeInMainWorld('example', { - setObj: (o: any) => { - myObj = o - }, - getObj: () => myObj - }) - }) - await callWithBindings(async (root: any) => { - root.GCRunner.run() - }) - // Initial Setup - let info = await getGCInfo() - expect(info.liveFromValues).to.equal(3) - expect(info.liveProxyValues).to.equal(3) - expect(info.objectCount).to.equal(6) - - // Create Reference - await callWithBindings(async (root: any) => { - root.x = { value: 123 } - root.example.setObj(root.x) - root.GCRunner.run() - }) - info = await getGCInfo() - expect(info.liveFromValues).to.equal(4) - expect(info.liveProxyValues).to.equal(4) - expect(info.objectCount).to.equal(8) - - // Release Reference - await callWithBindings(async (root: any) => { - delete root.x - root.example.setObj(null) - root.GCRunner.run() - }) - info = await getGCInfo() - expect(info.liveFromValues).to.equal(3) - expect(info.liveProxyValues).to.equal(3) - expect(info.objectCount).to.equal(6) - }) + getFunction: () => () => 123, + track: weaklyTrackValue + }); + require('electron').ipcRenderer.send('window-ready-for-tasking'); + }); + const loadPromise = emittedOnce(ipcMain, 'window-ready-for-tasking'); + expect((await getGCInfo()).trackedValues).to.equal(0); + await callWithBindings((root: any) => { + root.example.track(root.example.getFunction()); + }); + expect((await getGCInfo()).trackedValues).to.equal(1); + await callWithBindings((root: any) => { + root.location.reload(); + }); + await loadPromise; + await forceGCOnWindow(); + // If this is ever "2" it means we leaked the exposed function and + // therefore the entire context after a reload + expect((await getGCInfo()).trackedValues).to.equal(0); + }); } - + it('it should not let you overwrite existing exposed things', async () => { await makeBindingWindow(() => { let threw = false @@ -583,6 +475,7 @@ describe('contextBridge', () => { string: 'string', boolean: true, arr: [123, 'string', true, ['foo']], + getObject: () => ({ thing: 123 }), getNumber: () => 123, getString: () => 'string', getBoolean: () => true, @@ -616,6 +509,7 @@ describe('contextBridge', () => { [example.arr[3][0], String], [example.getNumber, Function], [example.getNumber(), Number], + [example.getObject(), Object], [example.getString(), String], [example.getBoolean(), Boolean], [example.getArr(), Array], diff --git a/spec-main/api-debugger-spec.ts b/spec-main/api-debugger-spec.ts index dee681ed5b718..f3f8eae4a3086 100644 --- a/spec-main/api-debugger-spec.ts +++ b/spec-main/api-debugger-spec.ts @@ -165,21 +165,27 @@ describe('debugger module', () => { w.webContents.debugger.detach() }) - it('handles valid unicode characters in message', (done) => { + // TODO(deepak1556): Fix and enable with upgrade + it.skip('handles valid unicode characters in message', (done) => { try { w.webContents.debugger.attach() } catch (err) { done(`unexpected error : ${err}`) } + let requestId : number w.webContents.debugger.on('message', (event, method, params) => { - if (method === 'Network.loadingFinished') { + if (method === 'Network.responseReceived' && + params.response.url.startsWith('http://127.0.0.1')) { + requestId = params.requestId + } else if (method === 'Network.loadingFinished' && + params.requestId === requestId) { w.webContents.debugger.sendCommand('Network.getResponseBody', { requestId: params.requestId }).then(data => { expect(data.body).to.equal('\u0024') done() - }) + }).catch(result => done(result)) } }) diff --git a/spec-main/api-desktop-capturer-spec.ts b/spec-main/api-desktop-capturer-spec.ts index 54c71d6a63594..29e98586d09d5 100644 --- a/spec-main/api-desktop-capturer-spec.ts +++ b/spec-main/api-desktop-capturer-spec.ts @@ -129,7 +129,8 @@ ifdescribe(features.isDesktopCapturerEnabled() && !process.arch.includes('arm') expect(mediaSourceId).to.equal(foundSource!.id) }) - it('moveAbove should move the window at the requested place', async () => { + // TODO(deepak1556): currently fails on all ci, enable it after upgrade. + it.skip('moveAbove should move the window at the requested place', async () => { // DesktopCapturer.getSources() is guaranteed to return in the correct // z-order from foreground to background. const MAX_WIN = 4 diff --git a/spec-main/api-menu-item-spec.ts b/spec-main/api-menu-item-spec.ts index d8495678f50fe..7dc587e6603bc 100644 --- a/spec-main/api-menu-item-spec.ts +++ b/spec-main/api-menu-item-spec.ts @@ -471,9 +471,9 @@ describe('MenuItems', () => { { label: 'text', accelerator: 'Alt+Tab' } ]) - expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? '⌘⇥\u0000' : 'Ctrl+Tab') - expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⇧⇥\u0000' : 'Shift+Tab') - expect(menu.getAcceleratorTextAt(2)).to.equal(isDarwin() ? '⌥⇥\u0000' : 'Alt+Tab') + expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? '⌘⇥' : 'Ctrl+Tab') + expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⇧⇥' : 'Shift+Tab') + expect(menu.getAcceleratorTextAt(2)).to.equal(isDarwin() ? '⌥⇥' : 'Alt+Tab') }) it('should not display modifiers twice', () => { @@ -483,7 +483,7 @@ describe('MenuItems', () => { ]) expect(menu.getAcceleratorTextAt(0)).to.equal(isDarwin() ? '⇧A' : 'Shift+A') - expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⇧⇥\u0000' : 'Shift+Tab') + expect(menu.getAcceleratorTextAt(1)).to.equal(isDarwin() ? '⇧⇥' : 'Shift+Tab') }) it('should display correctly for edge cases', () => { diff --git a/spec-main/api-menu-spec.ts b/spec-main/api-menu-spec.ts index 5ee9bdd2fb8aa..7685fbe1a1e2b 100644 --- a/spec-main/api-menu-spec.ts +++ b/spec-main/api-menu-spec.ts @@ -1,8 +1,14 @@ +import * as cp from 'child_process' +import * as path from 'path' import { expect } from 'chai' import { BrowserWindow, globalShortcut, Menu, MenuItem } from 'electron' import { sortMenuItems } from '../lib/browser/api/menu-utils' +import { emittedOnce } from './events-helpers' +import { ifit } from './spec-helpers' import { closeWindow } from './window-helpers' +const fixturesPath = path.resolve(__dirname, 'fixtures') + describe('Menu module', function () { this.timeout(5000) describe('Menu.buildFromTemplate', () => { @@ -86,6 +92,12 @@ describe('Menu module', function () { }).to.throw(/Invalid template for MenuItem: must have at least one of label, role or type/) }) + it('throws when an non-array is passed as a template', () => { + expect(() => { + Menu.buildFromTemplate('hello' as any); + }).to.throw(/Invalid template for Menu: Menu template must be an array/); + }); + describe('Menu sorting and building', () => { describe('sorts groups', () => { it('does a simple sort', () => { @@ -822,6 +834,31 @@ describe('Menu module', function () { menu.closePopup() }) }) + + it('prevents menu from getting garbage-collected when popuping', (done) => { + let menu = Menu.buildFromTemplate([{role: 'paste'}]) + menu.popup({ window: w }) + + // Keep a weak reference to the menu. + const v8Util = process.electronBinding('v8_util') + const map = (v8Util as any).createIDWeakMap() as any + map.set(0, menu) + + setTimeout(() => { + // Do garbage collection, since |menu| is not referenced in this closure + // it would be gone after next call. + v8Util.requestGarbageCollectionForTesting() + setTimeout(() => { + // Try to receive menu from weak reference. + if (map.has(0)) { + map.get(0).closePopup() + done() + } else { + done('Menu is garbage-collected while popuping') + } + }) + }) + }) }) describe('Menu.setApplicationMenu', () => { @@ -839,6 +876,28 @@ describe('Menu module', function () { Menu.setApplicationMenu(null) expect(Menu.getApplicationMenu()).to.be.null('application menu') }) + + ifit(process.platform !== 'darwin')('does not override menu visibility on startup', async () => { + const appPath = path.join(fixturesPath, 'api', 'test-menu-visibility') + const appProcess = cp.spawn(process.execPath, [appPath]) + + let output = '' + appProcess.stdout.on('data', data => { output += data }) + + await emittedOnce(appProcess, 'close') + expect(output).to.include('Window has no menu') + }) + + ifit(process.platform !== 'darwin')('does not override null menu on startup', async () => { + const appPath = path.join(fixturesPath, 'api', 'test-menu-null') + const appProcess = cp.spawn(process.execPath, [appPath]) + + let output = '' + appProcess.stdout.on('data', data => { output += data }) + + await emittedOnce(appProcess, 'close') + expect(output).to.include('Window has no menu') + }) }) describe('menu accelerators', async () => { diff --git a/spec-main/api-native-theme-spec.ts b/spec-main/api-native-theme-spec.ts index 87e1eb214ed35..df4b139bcb9db 100644 --- a/spec-main/api-native-theme-spec.ts +++ b/spec-main/api-native-theme-spec.ts @@ -1,10 +1,12 @@ import { expect } from 'chai' -import { nativeTheme, systemPreferences } from 'electron' +import { nativeTheme, systemPreferences, BrowserWindow } from 'electron' import * as os from 'os' +import * as path from 'path' import * as semver from 'semver' import { delay, ifdescribe } from './spec-helpers' import { emittedOnce } from './events-helpers'; +import { closeAllWindows } from './window-helpers'; describe('nativeTheme module', () => { describe('nativeTheme.shouldUseDarkColors', () => { @@ -18,7 +20,9 @@ describe('nativeTheme module', () => { nativeTheme.themeSource = 'system' // Wait for any pending events to emit await delay(20) - }) + + closeAllWindows() + }); it('is system by default', () => { expect(nativeTheme.themeSource).to.equal('system') @@ -56,13 +60,33 @@ describe('nativeTheme module', () => { ifdescribe(process.platform === 'darwin' && semver.gte(os.release(), '18.0.0'))('on macOS 10.14', () => { it('should update appLevelAppearance when set', () => { - nativeTheme.themeSource = 'dark' - expect(systemPreferences.appLevelAppearance).to.equal('dark') - nativeTheme.themeSource = 'light' - expect(systemPreferences.appLevelAppearance).to.equal('light') - }) - }) - }) + nativeTheme.themeSource = 'dark'; + expect(systemPreferences.appLevelAppearance).to.equal('dark'); + nativeTheme.themeSource = 'light'; + expect(systemPreferences.appLevelAppearance).to.equal('light'); + }); + }); + + const getPrefersColorSchemeIsDark = async (w: Electron.BrowserWindow) => { + const isDark: boolean = await w.webContents.executeJavaScript( + 'matchMedia("(prefers-color-scheme: dark)").matches' + ); + return isDark; + }; + + it('should override the result of prefers-color-scheme CSS media query', async () => { + const w = new BrowserWindow({ show: false }); + await w.loadFile(path.resolve(__dirname, 'fixtures', 'blank.html')); + const originalSystemIsDark = await getPrefersColorSchemeIsDark(w); + nativeTheme.themeSource = 'dark'; + expect(await getPrefersColorSchemeIsDark(w)).to.equal(true); + nativeTheme.themeSource = 'light'; + expect(await getPrefersColorSchemeIsDark(w)).to.equal(false); + nativeTheme.themeSource = 'system'; + expect(await getPrefersColorSchemeIsDark(w)).to.equal(originalSystemIsDark); + w.close(); + }); + }); describe('nativeTheme.shouldUseInvertedColorScheme', () => { it('returns a boolean', () => { diff --git a/spec-main/api-net-log-spec.ts b/spec-main/api-net-log-spec.ts index bae26a5c01d6c..86220eddc6c00 100644 --- a/spec-main/api-net-log-spec.ts +++ b/spec-main/api-net-log-spec.ts @@ -69,7 +69,8 @@ describe('netLog module', () => { expect(testNetLog().currentlyLoggingPath).to.equal(dumpFileDynamic) - await testNetLog().stopLogging() + const path = await testNetLog().stopLogging() + expect(path).to.equal(dumpFileDynamic) expect(fs.existsSync(dumpFileDynamic)).to.be.true('currently logging') }) diff --git a/spec-main/api-net-spec.ts b/spec-main/api-net-spec.ts index 3a1fb2fb99615..0fce9d7c82aa8 100644 --- a/spec-main/api-net-spec.ts +++ b/spec-main/api-net-spec.ts @@ -1,1328 +1,1530 @@ -import { expect } from 'chai' -import { net as originalNet, session, ClientRequest } from 'electron' -import * as http from 'http' -import * as url from 'url' -import { AddressInfo } from 'net' - -const outstandingRequests: ClientRequest[] = [] -const net: {request: (typeof originalNet)['request']} = { - request: (...args) => { - const r = originalNet.request(...args) - outstandingRequests.push(r) - return r - } -} -const abortOutstandingRequests = () => { - outstandingRequests.forEach(r => r.abort()) - outstandingRequests.length = 0 -} +import { expect } from 'chai'; +import { net, session, ClientRequest, BrowserWindow } from 'electron'; +import * as http from 'http'; +import * as url from 'url'; +import { AddressInfo, Socket } from 'net'; +import { emittedOnce } from './events-helpers'; -const kOneKiloByte = 1024 -const kOneMegaByte = kOneKiloByte * kOneKiloByte +const kOneKiloByte = 1024; +const kOneMegaByte = kOneKiloByte * kOneKiloByte; function randomBuffer (size: number, start: number = 0, end: number = 255) { - const range = 1 + end - start - const buffer = Buffer.allocUnsafe(size) + const range = 1 + end - start; + const buffer = Buffer.allocUnsafe(size); for (let i = 0; i < size; ++i) { - buffer[i] = start + Math.floor(Math.random() * range) + buffer[i] = start + Math.floor(Math.random() * range); } - return buffer + return buffer; } function randomString (length: number) { - const buffer = randomBuffer(length, '0'.charCodeAt(0), 'z'.charCodeAt(0)) - return buffer.toString() + const buffer = randomBuffer(length, '0'.charCodeAt(0), 'z'.charCodeAt(0)); + return buffer.toString(); +} + +const cleanupTasks: (() => void)[] = []; + +function cleanUp () { + cleanupTasks.forEach(t => t()); + cleanupTasks.length = 0; +} + +async function getResponse (urlRequest: Electron.ClientRequest) { + return new Promise((resolve, reject) => { + urlRequest.on('error', reject); + urlRequest.on('abort', reject); + urlRequest.on('response', (response) => resolve(response)); + urlRequest.end(); + }); +} + +async function collectStreamBody (response: Electron.IncomingMessage | http.IncomingMessage) { + return (await collectStreamBodyBuffer(response)).toString(); +} + +function collectStreamBodyBuffer (response: Electron.IncomingMessage | http.IncomingMessage) { + return new Promise((resolve, reject) => { + response.on('error', reject); + (response as NodeJS.EventEmitter).on('aborted', reject); + const data: Buffer[] = []; + response.on('data', (chunk) => data.push(chunk)); + response.on('end', (chunk?: Buffer) => { + if (chunk) data.push(chunk); + resolve(Buffer.concat(data)); + }); + }); } -function respondOnce(fn: http.RequestListener): Promise { +function respondNTimes (fn: http.RequestListener, n: number): Promise { return new Promise((resolve) => { const server = http.createServer((request, response) => { - fn(request, response) + fn(request, response); // don't close if a redirect was returned - if (response.statusCode < 300 || response.statusCode >= 399) - server.close() - }) + if ((response.statusCode < 300 || response.statusCode >= 399) && n <= 0) { + n--; + server.close(); + } + }); server.listen(0, '127.0.0.1', () => { - resolve(`http://127.0.0.1:${(server.address() as AddressInfo).port}`) - }) - }) + resolve(`http://127.0.0.1:${(server.address() as AddressInfo).port}`); + }); + const sockets: Socket[] = []; + server.on('connection', s => sockets.push(s)); + cleanupTasks.push(() => { + server.close(); + sockets.forEach(s => s.destroy()); + }); + }); } -respondOnce.toRoutes = (routes: Record) => { - return respondOnce((request, response) => { +function respondOnce (fn: http.RequestListener) { + return respondNTimes(fn, 1); +} + +let routeFailure = false; + +respondNTimes.toRoutes = (routes: Record, n: number) => { + return respondNTimes((request, response) => { if (routes.hasOwnProperty(request.url || '')) { - routes[request.url || ''](request, response) + (async () => { + await Promise.resolve(routes[request.url || ''](request, response)); + })().catch((err) => { + routeFailure = true; + console.error('Route handler failed, this is probably why your test failed', err); + response.statusCode = 500; + response.end(); + }); } else { - response.statusCode = 500 - response.end() - expect.fail(`Unexpected URL: ${request.url}`) + response.statusCode = 500; + response.end(); + expect.fail(`Unexpected URL: ${request.url}`); } - }) -} + }, n); +}; +respondOnce.toRoutes = (routes: Record) => respondNTimes.toRoutes(routes, 1); -respondOnce.toURL = (url: string, fn: http.RequestListener) => { - return respondOnce.toRoutes({[url]: fn}) -} +respondNTimes.toURL = (url: string, fn: http.RequestListener, n: number) => { + return respondNTimes.toRoutes({ [url]: fn }, n); +}; +respondOnce.toURL = (url: string, fn: http.RequestListener) => respondNTimes.toURL(url, fn, 1); -respondOnce.toSingleURL = (fn: http.RequestListener) => { - const requestUrl = '/requestUrl' - return respondOnce.toURL(requestUrl, fn).then(url => `${url}${requestUrl}`) -} +respondNTimes.toSingleURL = (fn: http.RequestListener, n: number) => { + const requestUrl = '/requestUrl'; + return respondNTimes.toURL(requestUrl, fn, n).then(url => `${url}${requestUrl}`); +}; +respondOnce.toSingleURL = (fn: http.RequestListener) => respondNTimes.toSingleURL(fn, 1); describe('net module', () => { - afterEach(abortOutstandingRequests) + beforeEach(() => { + routeFailure = false; + }); + afterEach(cleanUp); + afterEach(async function () { + await session.defaultSession.clearCache(); + if (routeFailure && this.test) { + if (!this.test.isFailed()) { + throw new Error('Failing this test due an unhandled error in the respondOnce route handler, check the logs above for the actual error'); + } + } + }); + describe('HTTP basics', () => { - it('should be able to issue a basic GET request', (done) => { - respondOnce.toSingleURL((request, response) => { - expect(request.method).to.equal('GET') - response.end() - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - expect(response.statusCode).to.equal(200) - response.on('data', () => {}) - response.on('end', () => { - done() - }) - }) - urlRequest.end() - }) - }) - - it('should be able to issue a basic POST request', (done) => { - respondOnce.toSingleURL((request, response) => { - expect(request.method).to.equal('POST') - response.end() - }).then(serverUrl => { - const urlRequest = net.request({ - method: 'POST', - url: serverUrl - }) - urlRequest.on('response', (response) => { - expect(response.statusCode).to.equal(200) - response.on('data', () => { }) - response.on('end', () => { - done() - }) - }) - urlRequest.end() - }) - }) - - it('should fetch correct data in a GET request', (done) => { - const bodyData = 'Hello World!' - respondOnce.toSingleURL((request, response) => { - expect(request.method).to.equal('GET') - response.end(bodyData) - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - let expectedBodyData = '' - expect(response.statusCode).to.equal(200) - response.on('data', (chunk) => { - expectedBodyData += chunk.toString() - }) - response.on('end', () => { - expect(expectedBodyData).to.equal(bodyData) - done() - }) - }) - urlRequest.end() - }) - }) - - it('should post the correct data in a POST request', (done) => { - const bodyData = 'Hello World!' - respondOnce.toSingleURL((request, response) => { - let postedBodyData = '' - expect(request.method).to.equal('POST') - request.on('data', (chunk: Buffer) => { - postedBodyData += chunk.toString() - }) - request.on('end', () => { - expect(postedBodyData).to.equal(bodyData) - response.end() - }) - }).then(serverUrl => { - const urlRequest = net.request({ - method: 'POST', - url: serverUrl - }) - urlRequest.on('response', (response) => { - expect(response.statusCode).to.equal(200) - response.on('data', () => {}) - response.on('end', () => { - done() - }) - }) - urlRequest.write(bodyData) - urlRequest.end() - }) - }) - - it('should support chunked encoding', (done) => { - respondOnce.toSingleURL((request, response) => { - response.statusCode = 200 - response.statusMessage = 'OK' - response.chunkedEncoding = true - expect(request.method).to.equal('POST') - expect(request.headers['transfer-encoding']).to.equal('chunked') - expect(request.headers['content-length']).to.equal(undefined) + it('should be able to issue a basic GET request', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + expect(request.method).to.equal('GET'); + response.end(); + }); + const urlRequest = net.request(serverUrl); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + }); + + it('should be able to issue a basic POST request', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + expect(request.method).to.equal('POST'); + response.end(); + }); + const urlRequest = net.request({ + method: 'POST', + url: serverUrl + }); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + }); + + it('should fetch correct data in a GET request', async () => { + const expectedBodyData = 'Hello World!'; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + expect(request.method).to.equal('GET'); + response.end(expectedBodyData); + }); + const urlRequest = net.request(serverUrl); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + const body = await collectStreamBody(response); + expect(body).to.equal(expectedBodyData); + }); + + it('should post the correct data in a POST request', async () => { + const bodyData = 'Hello World!'; + const serverUrl = await respondOnce.toSingleURL(async (request, response) => { + const postedBodyData = await collectStreamBody(request); + expect(postedBodyData).to.equal(bodyData); + response.end(); + }); + const urlRequest = net.request({ + method: 'POST', + url: serverUrl + }); + urlRequest.write(bodyData); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + }); + + it('should support chunked encoding', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.statusCode = 200; + response.statusMessage = 'OK'; + response.chunkedEncoding = true; + expect(request.method).to.equal('POST'); + expect(request.headers['transfer-encoding']).to.equal('chunked'); + expect(request.headers['content-length']).to.equal(undefined); request.on('data', (chunk: Buffer) => { - response.write(chunk) - }) + response.write(chunk); + }); request.on('end', (chunk: Buffer) => { - response.end(chunk) - }) - }).then(serverUrl => { - const urlRequest = net.request({ - method: 'POST', - url: serverUrl - }) - - let chunkIndex = 0 - const chunkCount = 100 - const sentChunks: Array = [] - const receivedChunks: Array = [] - urlRequest.on('response', (response) => { - expect(response.statusCode).to.equal(200) - response.on('data', (chunk) => { - receivedChunks.push(chunk) - }) - response.on('end', () => { - const sentData = Buffer.concat(sentChunks) - const receivedData = Buffer.concat(receivedChunks) - expect(sentData.toString()).to.equal(receivedData.toString()) - expect(chunkIndex).to.be.equal(chunkCount) - done() - }) - }) - urlRequest.chunkedEncoding = true - while (chunkIndex < chunkCount) { - chunkIndex += 1 - const chunk = randomBuffer(kOneKiloByte) - sentChunks.push(chunk) - expect(urlRequest.write(chunk)).to.equal(true) + response.end(chunk); + }); + }); + const urlRequest = net.request({ + method: 'POST', + url: serverUrl + }); + + let chunkIndex = 0; + const chunkCount = 100; + let sent = Buffer.alloc(0); + + urlRequest.chunkedEncoding = true; + while (chunkIndex < chunkCount) { + chunkIndex += 1; + const chunk = randomBuffer(kOneKiloByte); + sent = Buffer.concat([sent, chunk]); + urlRequest.write(chunk); + } + + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + const received = await collectStreamBodyBuffer(response); + expect(sent.equals(received)).to.be.true(); + expect(chunkIndex).to.be.equal(chunkCount); + }); + + it('should emit the login event when 401', async () => { + const [user, pass] = ['user', 'pass']; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + if (!request.headers.authorization) { + return response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }).end(); } - urlRequest.end() - }) - }) - }) - - describe('ClientRequest API', () => { - it('request/response objects should emit expected events', (done) => { - const bodyData = randomString(kOneKiloByte) - respondOnce.toSingleURL((request, response) => { - response.end(bodyData) - }).then(serverUrl => { - let requestResponseEventEmitted = false - let requestFinishEventEmitted = false - let requestCloseEventEmitted = false - let responseDataEventEmitted = false - let responseEndEventEmitted = false - - function maybeDone (done: () => void) { - if (!requestCloseEventEmitted || !responseEndEventEmitted) { - return - } - - expect(requestResponseEventEmitted).to.equal(true) - expect(requestFinishEventEmitted).to.equal(true) - expect(requestCloseEventEmitted).to.equal(true) - expect(responseDataEventEmitted).to.equal(true) - expect(responseEndEventEmitted).to.equal(true) - done() + response.writeHead(200).end('ok'); + }); + let loginAuthInfo: Electron.AuthInfo; + const request = net.request({ method: 'GET', url: serverUrl }); + request.on('login', (authInfo, cb) => { + loginAuthInfo = authInfo; + cb(user, pass); + }); + const response = await getResponse(request); + expect(response.statusCode).to.equal(200); + expect(loginAuthInfo!.realm).to.equal('Foo'); + expect(loginAuthInfo!.scheme).to.equal('basic'); + }); + + it('should response when cancelling authentication', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + if (!request.headers.authorization) { + response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }); + response.end('unauthenticated'); + } else { + response.writeHead(200).end('ok'); } + }); + const request = net.request({ method: 'GET', url: serverUrl }); + request.on('login', (authInfo, cb) => { + cb(); + }); + const response = await getResponse(request); + const body = await collectStreamBody(response); + expect(body).to.equal('unauthenticated'); + }); + + it('should share credentials with WebContents', async () => { + const [user, pass] = ['user', 'pass']; + const serverUrl = await respondNTimes.toSingleURL((request, response) => { + if (!request.headers.authorization) { + return response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }).end(); + } + return response.writeHead(200).end('ok'); + }, 2); + const bw = new BrowserWindow({ show: false }); + bw.webContents.on('login', (event, details, authInfo, cb) => { + event.preventDefault(); + cb(user, pass); + }); + await bw.loadURL(serverUrl); + bw.close(); + const request = net.request({ method: 'GET', url: serverUrl }); + let logInCount = 0; + request.on('login', () => { + logInCount++; + }); + const response = await getResponse(request); + await collectStreamBody(response); + expect(logInCount).to.equal(0, 'should not receive a login event, credentials should be cached'); + }); + + it('should share proxy credentials with WebContents', async () => { + const [user, pass] = ['user', 'pass']; + const proxyUrl = await respondNTimes((request, response) => { + if (!request.headers['proxy-authorization']) { + return response.writeHead(407, { 'Proxy-Authenticate': 'Basic realm="Foo"' }).end(); + } + return response.writeHead(200).end('ok'); + }, 2); + const customSession = session.fromPartition(`net-proxy-test-${Math.random()}`); + await customSession.setProxy({ proxyRules: proxyUrl.replace('http://', ''), proxyBypassRules: '<-loopback>' }); + const bw = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); + bw.webContents.on('login', (event, details, authInfo, cb) => { + event.preventDefault(); + cb(user, pass); + }); + await bw.loadURL('http://127.0.0.1:9999'); + bw.close(); + const request = net.request({ method: 'GET', url: 'http://127.0.0.1:9999', session: customSession }); + let logInCount = 0; + request.on('login', () => { + logInCount++; + }); + const response = await getResponse(request); + const body = await collectStreamBody(response); + expect(response.statusCode).to.equal(200); + expect(body).to.equal('ok'); + expect(logInCount).to.equal(0, 'should not receive a login event, credentials should be cached'); + }); + + it('should upload body when 401', async () => { + const [user, pass] = ['user', 'pass']; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + if (!request.headers.authorization) { + return response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }).end(); + } + response.writeHead(200); + request.on('data', (chunk) => response.write(chunk)); + request.on('end', () => response.end()); + }); + const requestData = randomString(kOneKiloByte); + const request = net.request({ method: 'GET', url: serverUrl }); + request.on('login', (authInfo, cb) => { + cb(user, pass); + }); + request.write(requestData); + const response = await getResponse(request); + const responseData = await collectStreamBody(response); + expect(responseData).to.equal(requestData); + }); + }); - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - requestResponseEventEmitted = true - const statusCode = response.statusCode - expect(statusCode).to.equal(200) - const buffers: Buffer[] = [] - response.on('data', (chunk) => { - buffers.push(chunk) - responseDataEventEmitted = true - }) - response.on('end', () => { - const receivedBodyData = Buffer.concat(buffers) - expect(receivedBodyData.toString()).to.equal(bodyData) - responseEndEventEmitted = true - maybeDone(done) - }) - response.on('error', (error: Error) => { - expect(error).to.be.an('Error') - }) - response.on('aborted', () => { - expect.fail('response aborted') - }) - }) - urlRequest.on('finish', () => { - requestFinishEventEmitted = true - }) - urlRequest.on('error', (error) => { - expect(error).to.be.an('Error') - }) - urlRequest.on('abort', () => { - expect.fail('request aborted') - }) - urlRequest.on('close', () => { - requestCloseEventEmitted = true - maybeDone(done) - }) - urlRequest.end() - }) - }) - - it('should be able to set a custom HTTP request header before first write', (done) => { - const customHeaderName = 'Some-Custom-Header-Name' - const customHeaderValue = 'Some-Customer-Header-Value' - respondOnce.toSingleURL((request, response) => { - expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue) - response.statusCode = 200 - response.statusMessage = 'OK' - response.end() - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - const statusCode = response.statusCode - expect(statusCode).to.equal(200) - response.on('data', () => { - }) - response.on('end', () => { - done() - }) - }) - urlRequest.setHeader(customHeaderName, customHeaderValue) - expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue) - expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue) - urlRequest.write('') - expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue) - expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue) - urlRequest.end() - }) - }) - - it('should be able to set a non-string object as a header value', (done) => { - const customHeaderName = 'Some-Integer-Value' - const customHeaderValue = 900 - respondOnce.toSingleURL((request, response) => { - expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue.toString()) - response.statusCode = 200 - response.statusMessage = 'OK' - response.end() - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - const statusCode = response.statusCode - expect(statusCode).to.equal(200) - response.on('data', () => {}) - response.on('end', () => { - done() - }) - }) - urlRequest.setHeader(customHeaderName, customHeaderValue as any) - expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue) - expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue) - urlRequest.write('') - expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue) - expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue) - urlRequest.end() - }) - }) - - it('should not be able to set a custom HTTP request header after first write', (done) => { - const customHeaderName = 'Some-Custom-Header-Name' - const customHeaderValue = 'Some-Customer-Header-Value' - respondOnce.toSingleURL((request, response) => { - expect(request.headers[customHeaderName.toLowerCase()]).to.equal(undefined) - response.statusCode = 200 - response.statusMessage = 'OK' - response.end() - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - const statusCode = response.statusCode - expect(statusCode).to.equal(200) - response.on('data', () => {}) - response.on('end', () => { - done() - }) - }) - urlRequest.write('') - expect(() => { - urlRequest.setHeader(customHeaderName, customHeaderValue) - }).to.throw() - expect(urlRequest.getHeader(customHeaderName)).to.equal(undefined) - urlRequest.end() - }) - }) - - it('should be able to remove a custom HTTP request header before first write', (done) => { - const customHeaderName = 'Some-Custom-Header-Name' - const customHeaderValue = 'Some-Customer-Header-Value' - respondOnce.toSingleURL((request, response) => { - expect(request.headers[customHeaderName.toLowerCase()]).to.equal(undefined) - response.statusCode = 200 - response.statusMessage = 'OK' - response.end() - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - const statusCode = response.statusCode - expect(statusCode).to.equal(200) - response.on('data', () => {}) - response.on('end', () => { - done() - }) - }) - urlRequest.setHeader(customHeaderName, customHeaderValue) - expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue) - urlRequest.removeHeader(customHeaderName) - expect(urlRequest.getHeader(customHeaderName)).to.equal(undefined) - urlRequest.write('') - urlRequest.end() - }) - }) - - it('should not be able to remove a custom HTTP request header after first write', (done) => { - const customHeaderName = 'Some-Custom-Header-Name' - const customHeaderValue = 'Some-Customer-Header-Value' - respondOnce.toSingleURL((request, response) => { - expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue) - response.statusCode = 200 - response.statusMessage = 'OK' - response.end() - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - const statusCode = response.statusCode - expect(statusCode).to.equal(200) - response.on('data', () => {}) - response.on('end', () => { - done() - }) - }) - urlRequest.setHeader(customHeaderName, customHeaderValue) - expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue) - urlRequest.write('') - expect(() => { - urlRequest.removeHeader(customHeaderName) - }).to.throw() - expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue) - urlRequest.end() - }) - }) - - it('should be able to set cookie header line', (done) => { - const cookieHeaderName = 'Cookie' - const cookieHeaderValue = 'test=12345' - const customSession = session.fromPartition('test-cookie-header') - respondOnce.toSingleURL((request, response) => { - expect(request.headers[cookieHeaderName.toLowerCase()]).to.equal(cookieHeaderValue) - response.statusCode = 200 - response.statusMessage = 'OK' - response.end() - }).then(serverUrl => { - customSession.cookies.set({ - url: `${serverUrl}`, - name: 'test', - value: '11111' - }).then(() => { // resolved - const urlRequest = net.request({ - method: 'GET', - url: serverUrl, - session: customSession - }) - urlRequest.on('response', (response) => { - const statusCode = response.statusCode - expect(statusCode).to.equal(200) - response.on('data', () => {}) - response.on('end', () => { - done() - }) - }) - urlRequest.setHeader(cookieHeaderName, cookieHeaderValue) - expect(urlRequest.getHeader(cookieHeaderName)).to.equal(cookieHeaderValue) - urlRequest.end() - }, (error) => { - done(error) - }) - }) - }) - - it('should be able to abort an HTTP request before first write', (done) => { - respondOnce.toSingleURL((request, response) => { - response.end() - expect.fail('Unexpected request event') - }).then(serverUrl => { - let requestAbortEventEmitted = false - - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - expect.fail('Unexpected response event') - }) - urlRequest.on('finish', () => { - expect.fail('Unexpected finish event') - }) - urlRequest.on('error', () => { - expect.fail('Unexpected error event') - }) - urlRequest.on('abort', () => { - requestAbortEventEmitted = true - }) - urlRequest.on('close', () => { - expect(requestAbortEventEmitted).to.equal(true) - done() - }) - urlRequest.abort() - expect(urlRequest.write('')).to.equal(false) - urlRequest.end() - }) - }) - - it('it should be able to abort an HTTP request before request end', (done) => { - let requestReceivedByServer = false - let urlRequest: ClientRequest | null = null - respondOnce.toSingleURL((request, response) => { - requestReceivedByServer = true - urlRequest!.abort() - }).then(serverUrl => { - let requestAbortEventEmitted = false - - urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - expect.fail('Unexpected response event') - }) - urlRequest.on('finish', () => { - expect.fail('Unexpected finish event') - }) - urlRequest.on('error', () => { - expect.fail('Unexpected error event') - }) - urlRequest.on('abort', () => { - requestAbortEventEmitted = true - }) - urlRequest.on('close', () => { - expect(requestReceivedByServer).to.equal(true) - expect(requestAbortEventEmitted).to.equal(true) - done() - }) - - urlRequest.chunkedEncoding = true - urlRequest.write(randomString(kOneKiloByte)) - }) - }) - - it('it should be able to abort an HTTP request after request end and before response', (done) => { - let requestReceivedByServer = false - let urlRequest: ClientRequest | null = null - respondOnce.toSingleURL((request, response) => { - requestReceivedByServer = true - urlRequest!.abort() + describe('ClientRequest API', () => { + it('request/response objects should emit expected events', async () => { + const bodyData = randomString(kOneKiloByte); + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.end(bodyData); + }); + + const urlRequest = net.request(serverUrl); + // request close event + const closePromise = emittedOnce(urlRequest, 'close'); + // request finish event + const finishPromise = emittedOnce(urlRequest, 'close'); + // request "response" event + const response = await getResponse(urlRequest); + response.on('error', (error: Error) => { + expect(error).to.be.an('Error'); + }); + const statusCode = response.statusCode; + expect(statusCode).to.equal(200); + // response data event + // respond end event + const body = await collectStreamBody(response); + expect(body).to.equal(bodyData); + urlRequest.on('error', (error) => { + expect(error).to.be.an('Error'); + }); + await Promise.all([closePromise, finishPromise]); + }); + + it('should be able to set a custom HTTP request header before first write', async () => { + const customHeaderName = 'Some-Custom-Header-Name'; + const customHeaderValue = 'Some-Customer-Header-Value'; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue); + response.statusCode = 200; + response.statusMessage = 'OK'; + response.end(); + }); + const urlRequest = net.request(serverUrl); + urlRequest.setHeader(customHeaderName, customHeaderValue); + expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue); + expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue); + urlRequest.write(''); + expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue); + expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + }); + + it('should be able to set a non-string object as a header value', async () => { + const customHeaderName = 'Some-Integer-Value'; + const customHeaderValue = 900; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue.toString()); + response.statusCode = 200; + response.statusMessage = 'OK'; + response.end(); + }); + + const urlRequest = net.request(serverUrl); + urlRequest.setHeader(customHeaderName, customHeaderValue as any); + expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue); + expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue); + urlRequest.write(''); + expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue); + expect(urlRequest.getHeader(customHeaderName.toLowerCase())).to.equal(customHeaderValue); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + }); + + it('should not be able to set a custom HTTP request header after first write', async () => { + const customHeaderName = 'Some-Custom-Header-Name'; + const customHeaderValue = 'Some-Customer-Header-Value'; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + expect(request.headers[customHeaderName.toLowerCase()]).to.equal(undefined); + response.statusCode = 200; + response.statusMessage = 'OK'; + response.end(); + }); + const urlRequest = net.request(serverUrl); + urlRequest.write(''); + expect(() => { + urlRequest.setHeader(customHeaderName, customHeaderValue); + }).to.throw(); + expect(urlRequest.getHeader(customHeaderName)).to.equal(undefined); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + }); + + it('should be able to remove a custom HTTP request header before first write', async () => { + const customHeaderName = 'Some-Custom-Header-Name'; + const customHeaderValue = 'Some-Customer-Header-Value'; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + expect(request.headers[customHeaderName.toLowerCase()]).to.equal(undefined); + response.statusCode = 200; + response.statusMessage = 'OK'; + response.end(); + }); + const urlRequest = net.request(serverUrl); + urlRequest.setHeader(customHeaderName, customHeaderValue); + expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue); + urlRequest.removeHeader(customHeaderName); + expect(urlRequest.getHeader(customHeaderName)).to.equal(undefined); + urlRequest.write(''); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + }); + + it('should not be able to remove a custom HTTP request header after first write', async () => { + const customHeaderName = 'Some-Custom-Header-Name'; + const customHeaderValue = 'Some-Customer-Header-Value'; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue); + response.statusCode = 200; + response.statusMessage = 'OK'; + response.end(); + }); + const urlRequest = net.request(serverUrl); + urlRequest.setHeader(customHeaderName, customHeaderValue); + expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue); + urlRequest.write(''); + expect(() => { + urlRequest.removeHeader(customHeaderName); + }).to.throw(); + expect(urlRequest.getHeader(customHeaderName)).to.equal(customHeaderValue); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + }); + + it('should be able to set cookie header line', async () => { + const cookieHeaderName = 'Cookie'; + const cookieHeaderValue = 'test=12345'; + const customSession = session.fromPartition('test-cookie-header'); + const serverUrl = await respondOnce.toSingleURL((request, response) => { + expect(request.headers[cookieHeaderName.toLowerCase()]).to.equal(cookieHeaderValue); + response.statusCode = 200; + response.statusMessage = 'OK'; + response.end(); + }); + await customSession.cookies.set({ + url: `${serverUrl}`, + name: 'test', + value: '11111', + expirationDate: 0 + }); + const urlRequest = net.request({ + method: 'GET', + url: serverUrl, + session: customSession + }); + urlRequest.setHeader(cookieHeaderName, cookieHeaderValue); + expect(urlRequest.getHeader(cookieHeaderName)).to.equal(cookieHeaderValue); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + }); + + it('should be able to receive cookies', async () => { + const cookie = ['cookie1', 'cookie2']; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.statusCode = 200; + response.statusMessage = 'OK'; + response.setHeader('set-cookie', cookie); + response.end(); + }); + const urlRequest = net.request(serverUrl); + const response = await getResponse(urlRequest); + expect(response.headers['set-cookie']).to.have.same.members(cookie); + }); + + it('should not use the sessions cookie store by default', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.statusCode = 200; + response.statusMessage = 'OK'; + response.setHeader('x-cookie', `${request.headers.cookie!}`); + response.end(); + }); + const sess = session.fromPartition('cookie-tests-1'); + const cookieVal = `${Date.now()}`; + await sess.cookies.set({ + url: serverUrl, + name: 'wild_cookie', + value: cookieVal + }); + const urlRequest = net.request({ + url: serverUrl, + session: sess + }); + const response = await getResponse(urlRequest); + expect(response.headers['x-cookie']).to.equal(`undefined`); + }); + + it('should be able to use the sessions cookie store', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.statusCode = 200; + response.statusMessage = 'OK'; + response.setHeader('x-cookie', request.headers.cookie!); + response.end(); + }); + const sess = session.fromPartition('cookie-tests-2'); + const cookieVal = `${Date.now()}`; + await sess.cookies.set({ + url: serverUrl, + name: 'wild_cookie', + value: cookieVal + }); + const urlRequest = net.request({ + url: serverUrl, + session: sess, + useSessionCookies: true + }); + const response = await getResponse(urlRequest); + expect(response.headers['x-cookie']).to.equal(`wild_cookie=${cookieVal}`); + }); + + it('should be able to use the sessions cookie store with set-cookie', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.statusCode = 200; + response.statusMessage = 'OK'; + response.setHeader('set-cookie', 'foo=bar'); + response.end(); + }); + const sess = session.fromPartition('cookie-tests-3'); + let cookies = await sess.cookies.get({}); + expect(cookies).to.have.lengthOf(0); + const urlRequest = net.request({ + url: serverUrl, + session: sess, + useSessionCookies: true + }); + await collectStreamBody(await getResponse(urlRequest)); + cookies = await sess.cookies.get({}); + expect(cookies).to.have.lengthOf(1); + expect(cookies[0]).to.deep.equal({ + name: 'foo', + value: 'bar', + domain: '127.0.0.1', + hostOnly: true, + path: '/', + secure: false, + httpOnly: false, + session: true + }); + }); + + ['Lax', 'Strict'].forEach((mode) => { + it(`should be able to use the sessions cookie store with same-site ${mode} cookies`, async () => { + const serverUrl = await respondNTimes.toSingleURL((request, response) => { + response.statusCode = 200; + response.statusMessage = 'OK'; + response.setHeader('set-cookie', `same=site; SameSite=${mode}`); + response.setHeader('x-cookie', `${request.headers.cookie}`); + response.end(); + }, 2); + const sess = session.fromPartition(`cookie-tests-same-site-${mode}`); + let cookies = await sess.cookies.get({}); + expect(cookies).to.have.lengthOf(0); + const urlRequest = net.request({ + url: serverUrl, + session: sess, + useSessionCookies: true + }); + const response = await getResponse(urlRequest); + expect(response.headers['x-cookie']).to.equal('undefined'); + await collectStreamBody(response); + cookies = await sess.cookies.get({}); + expect(cookies).to.have.lengthOf(1); + expect(cookies[0]).to.deep.equal({ + name: 'same', + value: 'site', + domain: '127.0.0.1', + hostOnly: true, + path: '/', + secure: false, + httpOnly: false, + session: true + }); + const urlRequest2 = net.request({ + url: serverUrl, + session: sess, + useSessionCookies: true + }); + const response2 = await getResponse(urlRequest2); + expect(response2.headers['x-cookie']).to.equal('same=site'); + }); + }); + + it('should be able to use the sessions cookie store safely across redirects', async () => { + const serverUrl = await respondOnce.toSingleURL(async (request, response) => { + response.statusCode = 302; + response.statusMessage = 'Moved'; + const newUrl = await respondOnce.toSingleURL((req, res) => { + res.statusCode = 200; + res.statusMessage = 'OK'; + res.setHeader('x-cookie', req.headers.cookie!); + res.end(); + }); + response.setHeader('x-cookie', request.headers.cookie!); + response.setHeader('location', newUrl.replace('127.0.0.1', 'localhost')); + response.end(); + }); + const sess = session.fromPartition('cookie-tests-4'); + const cookie127Val = `${Date.now()}-127`; + const cookieLocalVal = `${Date.now()}-local`; + const localhostUrl = serverUrl.replace('127.0.0.1', 'localhost'); + expect(localhostUrl).to.not.equal(serverUrl); + await Promise.all([ + sess.cookies.set({ + url: serverUrl, + name: 'wild_cookie', + value: cookie127Val + }), sess.cookies.set({ + url: localhostUrl, + name: 'wild_cookie', + value: cookieLocalVal + }) + ]); + const urlRequest = net.request({ + url: serverUrl, + session: sess, + useSessionCookies: true + }); + urlRequest.on('redirect', (status, method, url, headers) => { + // The initial redirect response should have received the 127 value here + expect(headers['x-cookie'][0]).to.equal(`wild_cookie=${cookie127Val}`); + urlRequest.followRedirect(); + }); + const response = await getResponse(urlRequest); + // We expect the server to have received the localhost value here + // The original request was to a 127.0.0.1 URL + // That request would have the cookie127Val cookie attached + // The request is then redirect to a localhost URL (different site) + // Because we are using the session cookie store it should do the safe / secure thing + // and attach the cookies for the new target domain + expect(response.headers['x-cookie']).to.equal(`wild_cookie=${cookieLocalVal}`); + }); + + it('should be able to abort an HTTP request before first write', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.end(); + expect.fail('Unexpected request event'); + }); + + const urlRequest = net.request(serverUrl); + urlRequest.on('response', () => { + expect.fail('unexpected response event'); + }); + const aborted = emittedOnce(urlRequest, 'abort'); + urlRequest.abort(); + urlRequest.write(''); + urlRequest.end(); + await aborted; + }); + + it('it should be able to abort an HTTP request before request end', async () => { + let requestReceivedByServer = false; + let urlRequest: ClientRequest | null = null; + const serverUrl = await respondOnce.toSingleURL(() => { + requestReceivedByServer = true; + urlRequest!.abort(); + }); + let requestAbortEventEmitted = false; + + urlRequest = net.request(serverUrl); + urlRequest.on('response', () => { + expect.fail('Unexpected response event'); + }); + urlRequest.on('finish', () => { + expect.fail('Unexpected finish event'); + }); + urlRequest.on('error', () => { + expect.fail('Unexpected error event'); + }); + urlRequest.on('abort', () => { + requestAbortEventEmitted = true; + }); + + await emittedOnce(urlRequest, 'close', () => { + urlRequest!.chunkedEncoding = true; + urlRequest!.write(randomString(kOneKiloByte)); + }); + expect(requestReceivedByServer).to.equal(true); + expect(requestAbortEventEmitted).to.equal(true); + }); + + it('it should be able to abort an HTTP request after request end and before response', async () => { + let requestReceivedByServer = false; + let urlRequest: ClientRequest | null = null; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + requestReceivedByServer = true; + urlRequest!.abort(); process.nextTick(() => { - response.statusCode = 200 - response.statusMessage = 'OK' - response.end() - }) - }).then(serverUrl => { - let requestAbortEventEmitted = false - let requestFinishEventEmitted = false - - urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - expect.fail('Unexpected response event') - }) - urlRequest.on('finish', () => { - requestFinishEventEmitted = true - }) - urlRequest.on('error', () => { - expect.fail('Unexpected error event') - }) - urlRequest.on('abort', () => { - requestAbortEventEmitted = true - }) - urlRequest.on('close', () => { - expect(requestFinishEventEmitted).to.equal(true) - expect(requestReceivedByServer).to.equal(true) - expect(requestAbortEventEmitted).to.equal(true) - done() - }) - - urlRequest.end(randomString(kOneKiloByte)) - }) - }) - - it('it should be able to abort an HTTP request after response start', (done) => { - let requestReceivedByServer = false - respondOnce.toSingleURL((request, response) => { - requestReceivedByServer = true - response.statusCode = 200 - response.statusMessage = 'OK' - response.write(randomString(kOneKiloByte)) - }).then(serverUrl => { - let requestFinishEventEmitted = false - let requestResponseEventEmitted = false - let requestAbortEventEmitted = false - let responseAbortedEventEmitted = false - - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - requestResponseEventEmitted = true - const statusCode = response.statusCode - expect(statusCode).to.equal(200) - response.on('data', (chunk) => { - }) - response.on('end', () => { - expect.fail('Unexpected end event') - }) - response.on('error', () => { - expect.fail('Unexpected error event') - }) - response.on('aborted', () => { - responseAbortedEventEmitted = true - }) - urlRequest.abort() - }) - urlRequest.on('finish', () => { - requestFinishEventEmitted = true - }) - urlRequest.on('error', () => { - expect.fail('Unexpected error event') - }) - urlRequest.on('abort', () => { - requestAbortEventEmitted = true - }) - urlRequest.on('close', () => { - expect(requestFinishEventEmitted).to.be.true('request should emit "finish" event') - expect(requestReceivedByServer).to.be.true('request should be received by the server') - expect(requestResponseEventEmitted).to.be.true('"response" event should be emitted') - expect(requestAbortEventEmitted).to.be.true('request should emit "abort" event') - expect(responseAbortedEventEmitted).to.be.true('response should emit "aborted" event') - done() - }) - urlRequest.end(randomString(kOneKiloByte)) - }) - }) - - it('abort event should be emitted at most once', (done) => { - let requestReceivedByServer = false - let urlRequest: ClientRequest | null = null - respondOnce.toSingleURL((request, response) => { - requestReceivedByServer = true - urlRequest!.abort() - urlRequest!.abort() - }).then(serverUrl => { - let requestFinishEventEmitted = false - let abortEmitted = false - - urlRequest = net.request(serverUrl) - urlRequest.on('response', () => { - expect.fail('Unexpected response event') - }) - urlRequest.on('finish', () => { - requestFinishEventEmitted = true - }) - urlRequest.on('error', () => { - expect.fail('Unexpected error event') - }) - urlRequest.on('abort', () => { - expect(abortEmitted).to.be.false('abort event should not be emitted more than once') - abortEmitted = true - urlRequest!.abort() - }) - urlRequest.on('close', () => { - expect(requestFinishEventEmitted).to.be.true('request should emit "finish" event') - expect(requestReceivedByServer).to.be.true('request should be received by server') - expect(abortEmitted).to.be.true('request should emit "abort" event') - done() - }) - - urlRequest.end(randomString(kOneKiloByte)) - }) - }) + response.statusCode = 200; + response.statusMessage = 'OK'; + response.end(); + }); + }); + let requestFinishEventEmitted = false; + + urlRequest = net.request(serverUrl); + urlRequest.on('response', () => { + expect.fail('Unexpected response event'); + }); + urlRequest.on('finish', () => { + requestFinishEventEmitted = true; + }); + urlRequest.on('error', () => { + expect.fail('Unexpected error event'); + }); + urlRequest.end(randomString(kOneKiloByte)); + await emittedOnce(urlRequest, 'abort'); + expect(requestFinishEventEmitted).to.equal(true); + expect(requestReceivedByServer).to.equal(true); + }); + + it('it should be able to abort an HTTP request after response start', async () => { + let requestReceivedByServer = false; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + requestReceivedByServer = true; + response.statusCode = 200; + response.statusMessage = 'OK'; + response.write(randomString(kOneKiloByte)); + }); + let requestFinishEventEmitted = false; + let requestResponseEventEmitted = false; + let responseCloseEventEmitted = false; + + const urlRequest = net.request(serverUrl); + urlRequest.on('response', (response) => { + requestResponseEventEmitted = true; + const statusCode = response.statusCode; + expect(statusCode).to.equal(200); + response.on('data', () => {}); + response.on('end', () => { + expect.fail('Unexpected end event'); + }); + response.on('error', () => { + expect.fail('Unexpected error event'); + }); + response.on('close' as any, () => { + responseCloseEventEmitted = true; + }); + urlRequest.abort(); + }); + urlRequest.on('finish', () => { + requestFinishEventEmitted = true; + }); + urlRequest.on('error', () => { + expect.fail('Unexpected error event'); + }); + urlRequest.end(randomString(kOneKiloByte)); + await emittedOnce(urlRequest, 'abort'); + expect(requestFinishEventEmitted).to.be.true('request should emit "finish" event'); + expect(requestReceivedByServer).to.be.true('request should be received by the server'); + expect(requestResponseEventEmitted).to.be.true('"response" event should be emitted'); + expect(responseCloseEventEmitted).to.be.true('response should emit "close" event'); + }); + + it('abort event should be emitted at most once', async () => { + let requestReceivedByServer = false; + let urlRequest: ClientRequest | null = null; + const serverUrl = await respondOnce.toSingleURL(() => { + requestReceivedByServer = true; + urlRequest!.abort(); + urlRequest!.abort(); + }); + let requestFinishEventEmitted = false; + let abortsEmitted = 0; + + urlRequest = net.request(serverUrl); + urlRequest.on('response', () => { + expect.fail('Unexpected response event'); + }); + urlRequest.on('finish', () => { + requestFinishEventEmitted = true; + }); + urlRequest.on('error', () => { + expect.fail('Unexpected error event'); + }); + urlRequest.on('abort', () => { + abortsEmitted++; + }); + urlRequest.end(randomString(kOneKiloByte)); + await emittedOnce(urlRequest, 'abort'); + expect(requestFinishEventEmitted).to.be.true('request should emit "finish" event'); + expect(requestReceivedByServer).to.be.true('request should be received by server'); + expect(abortsEmitted).to.equal(1, 'request should emit exactly 1 "abort" event'); + }); + + it('should allow to read response body from non-2xx response', async () => { + const bodyData = randomString(kOneKiloByte); + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.statusCode = 404; + response.end(bodyData); + }); + + const urlRequest = net.request(serverUrl); + const bodyCheckPromise = getResponse(urlRequest).then(r => { + expect(r.statusCode).to.equal(404); + return r; + }).then(collectStreamBody).then(receivedBodyData => { + expect(receivedBodyData.toString()).to.equal(bodyData); + }); + const eventHandlers = Promise.all([ + bodyCheckPromise, + emittedOnce(urlRequest, 'close') + ]); + + urlRequest.end(); + + await eventHandlers; + }); describe('webRequest', () => { afterEach(() => { - session.defaultSession.webRequest.onBeforeRequest(null) - }) + session.defaultSession.webRequest.onBeforeRequest(null); + }); it('Should throw when invalid filters are passed', () => { expect(() => { session.defaultSession.webRequest.onBeforeRequest( { urls: ['*://www.googleapis.com'] }, - (details, callback) => { callback({ cancel: false }) } - ) - }).to.throw('Invalid url pattern *://www.googleapis.com: Empty path.') + (details, callback) => { callback({ cancel: false }); } + ); + }).to.throw('Invalid url pattern *://www.googleapis.com: Empty path.'); expect(() => { session.defaultSession.webRequest.onBeforeRequest( { urls: [ '*://www.googleapis.com/', '*://blahblah.dev' ] }, - (details, callback) => { callback({ cancel: false }) } - ) - }).to.throw('Invalid url pattern *://blahblah.dev: Empty path.') - }) + (details, callback) => { callback({ cancel: false }); } + ); + }).to.throw('Invalid url pattern *://blahblah.dev: Empty path.'); + }); it('Should not throw when valid filters are passed', () => { expect(() => { session.defaultSession.webRequest.onBeforeRequest( { urls: ['*://www.googleapis.com/'] }, - (details, callback) => { callback({ cancel: false }) } - ) - }).to.not.throw() - }) - - it('Requests should be intercepted by webRequest module', (done) => { - const requestUrl = '/requestUrl' - const redirectUrl = '/redirectUrl' - let requestIsRedirected = false - respondOnce.toURL(redirectUrl, (request, response) => { - requestIsRedirected = true - response.end() - }).then(serverUrl => { - let requestIsIntercepted = false - session.defaultSession.webRequest.onBeforeRequest( - (details, callback) => { - if (details.url === `${serverUrl}${requestUrl}`) { - requestIsIntercepted = true - // Disabled due to false positive in StandardJS - // eslint-disable-next-line standard/no-callback-literal - callback({ - redirectURL: `${serverUrl}${redirectUrl}` - }) - } else { - callback({ - cancel: false - }) - } - }) - - const urlRequest = net.request(`${serverUrl}${requestUrl}`) - - urlRequest.on('response', (response) => { - expect(response.statusCode).to.equal(200) - response.on('data', () => {}) - response.on('end', () => { - expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL') - expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module') - done() - }) - }) - urlRequest.end() - }) - }) - - it('should to able to create and intercept a request using a custom session object', (done) => { - const requestUrl = '/requestUrl' - const redirectUrl = '/redirectUrl' - const customPartitionName = 'custom-partition' - let requestIsRedirected = false - respondOnce.toURL(redirectUrl, (request, response) => { - requestIsRedirected = true - response.end() - }).then(serverUrl => { - session.defaultSession.webRequest.onBeforeRequest((details, callback) => { - expect.fail('Request should not be intercepted by the default session') - }) - - const customSession = session.fromPartition(customPartitionName, { cache: false }) - let requestIsIntercepted = false - customSession.webRequest.onBeforeRequest((details, callback) => { - if (details.url === `${serverUrl}${requestUrl}`) { - requestIsIntercepted = true - // Disabled due to false positive in StandardJS - // eslint-disable-next-line standard/no-callback-literal - callback({ - redirectURL: `${serverUrl}${redirectUrl}` - }) - } else { - callback({ - cancel: false - }) - } - }) - - const urlRequest = net.request({ - url: `${serverUrl}${requestUrl}`, - session: customSession - }) - urlRequest.on('response', (response) => { - expect(response.statusCode).to.equal(200) - response.on('data', (chunk) => { - }) - response.on('end', () => { - expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL') - expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module') - done() - }) - }) - urlRequest.end() - }) - }) - - it('should to able to create and intercept a request using a custom session object', (done) => { - const requestUrl = '/requestUrl' - const redirectUrl = '/redirectUrl' - const customPartitionName = 'custom-partition' - let requestIsRedirected = false - respondOnce.toURL(redirectUrl, (request, response) => { - requestIsRedirected = true - response.end() - }).then(serverUrl => { - session.defaultSession.webRequest.onBeforeRequest((details, callback) => { - expect.fail('Request should not be intercepted by the default session') - }) - - const customSession = session.fromPartition(customPartitionName, { cache: false }) - let requestIsIntercepted = false - customSession.webRequest.onBeforeRequest((details, callback) => { + (details, callback) => { callback({ cancel: false }); } + ); + }).to.not.throw(); + }); + + it('Requests should be intercepted by webRequest module', async () => { + const requestUrl = '/requestUrl'; + const redirectUrl = '/redirectUrl'; + let requestIsRedirected = false; + const serverUrl = await respondOnce.toURL(redirectUrl, (request, response) => { + requestIsRedirected = true; + response.end(); + }); + let requestIsIntercepted = false; + session.defaultSession.webRequest.onBeforeRequest( + (details, callback) => { if (details.url === `${serverUrl}${requestUrl}`) { - requestIsIntercepted = true + requestIsIntercepted = true; // Disabled due to false positive in StandardJS // eslint-disable-next-line standard/no-callback-literal callback({ redirectURL: `${serverUrl}${redirectUrl}` - }) + }); } else { callback({ cancel: false - }) + }); } - }) - - const urlRequest = net.request({ - url: `${serverUrl}${requestUrl}`, - partition: customPartitionName - }) - urlRequest.on('response', (response) => { - expect(response.statusCode).to.equal(200) - response.on('data', (chunk) => { - }) - response.on('end', () => { - expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL') - expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module') - done() - }) - }) - urlRequest.end() - }) - }) - }) + }); - it('should throw if given an invalid redirect mode', () => { - expect(() => { - net.request({ - url: 'https://test', - redirect: 'custom' - }) - }).to.throw('redirect mode should be one of follow, error or manual') - }) + const urlRequest = net.request(`${serverUrl}${requestUrl}`); + const response = await getResponse(urlRequest); + + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL'); + expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module'); + }); + + it('should to able to create and intercept a request using a custom session object', async () => { + const requestUrl = '/requestUrl'; + const redirectUrl = '/redirectUrl'; + const customPartitionName = 'custom-partition'; + let requestIsRedirected = false; + const serverUrl = await respondOnce.toURL(redirectUrl, (request, response) => { + requestIsRedirected = true; + response.end(); + }); + session.defaultSession.webRequest.onBeforeRequest(() => { + expect.fail('Request should not be intercepted by the default session'); + }); + + const customSession = session.fromPartition(customPartitionName, { cache: false }); + let requestIsIntercepted = false; + customSession.webRequest.onBeforeRequest((details, callback) => { + if (details.url === `${serverUrl}${requestUrl}`) { + requestIsIntercepted = true; + // Disabled due to false positive in StandardJS + // eslint-disable-next-line standard/no-callback-literal + callback({ + redirectURL: `${serverUrl}${redirectUrl}` + }); + } else { + callback({ + cancel: false + }); + } + }); + + const urlRequest = net.request({ + url: `${serverUrl}${requestUrl}`, + session: customSession + }); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL'); + expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module'); + }); + + it('should to able to create and intercept a request using a custom partition name', async () => { + const requestUrl = '/requestUrl'; + const redirectUrl = '/redirectUrl'; + const customPartitionName = 'custom-partition'; + let requestIsRedirected = false; + const serverUrl = await respondOnce.toURL(redirectUrl, (request, response) => { + requestIsRedirected = true; + response.end(); + }); + session.defaultSession.webRequest.onBeforeRequest(() => { + expect.fail('Request should not be intercepted by the default session'); + }); + + const customSession = session.fromPartition(customPartitionName, { cache: false }); + let requestIsIntercepted = false; + customSession.webRequest.onBeforeRequest((details, callback) => { + if (details.url === `${serverUrl}${requestUrl}`) { + requestIsIntercepted = true; + // Disabled due to false positive in StandardJS + // eslint-disable-next-line standard/no-callback-literal + callback({ + redirectURL: `${serverUrl}${redirectUrl}` + }); + } else { + callback({ + cancel: false + }); + } + }); + + const urlRequest = net.request({ + url: `${serverUrl}${requestUrl}`, + partition: customPartitionName + }); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL'); + expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module'); + }); + }); it('should throw when calling getHeader without a name', () => { expect(() => { - (net.request({ url: 'https://test' }).getHeader as any)() - }).to.throw(/`name` is required for getHeader\(name\)/) + (net.request({ url: 'https://test' }).getHeader as any)(); + }).to.throw(/`name` is required for getHeader\(name\)/); expect(() => { - net.request({ url: 'https://test' }).getHeader(null as any) - }).to.throw(/`name` is required for getHeader\(name\)/) - }) + net.request({ url: 'https://test' }).getHeader(null as any); + }).to.throw(/`name` is required for getHeader\(name\)/); + }); it('should throw when calling removeHeader without a name', () => { expect(() => { - (net.request({ url: 'https://test' }).removeHeader as any)() - }).to.throw(/`name` is required for removeHeader\(name\)/) + (net.request({ url: 'https://test' }).removeHeader as any)(); + }).to.throw(/`name` is required for removeHeader\(name\)/); expect(() => { - net.request({ url: 'https://test' }).removeHeader(null as any) - }).to.throw(/`name` is required for removeHeader\(name\)/) - }) - - it('should follow redirect when no redirect mode is provided', (done) => { - const requestUrl = '/301' - respondOnce.toRoutes({ - '/301': (request, response) => { - response.statusCode = 301 - response.setHeader('Location', '/200') - response.end() + net.request({ url: 'https://test' }).removeHeader(null as any); + }).to.throw(/`name` is required for removeHeader\(name\)/); + }); + + it('should follow redirect when no redirect handler is provided', async () => { + const requestUrl = '/302'; + const serverUrl = await respondOnce.toRoutes({ + '/302': (request, response) => { + response.statusCode = 302; + response.setHeader('Location', '/200'); + response.end(); }, '/200': (request, response) => { - response.statusCode = 200 - response.end() - }, - }).then(serverUrl => { - const urlRequest = net.request({ - url: `${serverUrl}${requestUrl}` - }) - urlRequest.on('response', (response) => { - expect(response.statusCode).to.equal(200) - done() - }) - urlRequest.end() - }) - }) - - it('should follow redirect chain when no redirect mode is provided', (done) => { - respondOnce.toRoutes({ + response.statusCode = 200; + response.end(); + } + }); + const urlRequest = net.request({ + url: `${serverUrl}${requestUrl}` + }); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + }); + + it('should follow redirect chain when no redirect handler is provided', async () => { + const serverUrl = await respondOnce.toRoutes({ '/redirectChain': (request, response) => { - response.statusCode = 301 - response.setHeader('Location', '/301') - response.end() + response.statusCode = 302; + response.setHeader('Location', '/302'); + response.end(); }, - '/301': (request, response) => { - response.statusCode = 301 - response.setHeader('Location', '/200') - response.end() + '/302': (request, response) => { + response.statusCode = 302; + response.setHeader('Location', '/200'); + response.end(); }, '/200': (request, response) => { - response.statusCode = 200 - response.end() - }, - }).then(serverUrl => { - const urlRequest = net.request({ - url: `${serverUrl}/redirectChain` - }) - urlRequest.on('response', (response) => { - expect(response.statusCode).to.equal(200) - done() - }) - urlRequest.end() - }) - }) - - it.skip('should not follow redirect when mode is error', (done) => { - respondOnce.toSingleURL((request, response) => { - response.statusCode = 301 - response.setHeader('Location', '/200') - response.end() - }).then(serverUrl => { - const urlRequest = net.request({ - url: serverUrl, - redirect: 'error' - }) - urlRequest.on('error', (error) => { - expect(error.message).to.equal('Request cannot follow redirect with the current redirect mode') - }) - urlRequest.on('close', () => { - done() - }) - urlRequest.end() - }) - }) - - it('should allow follow redirect when mode is manual', (done) => { - respondOnce.toRoutes({ + response.statusCode = 200; + response.end(); + } + }); + const urlRequest = net.request({ + url: `${serverUrl}/redirectChain` + }); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + }); + + it('should not follow redirect when request is canceled in redirect handler', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.statusCode = 302; + response.setHeader('Location', '/200'); + response.end(); + }); + const urlRequest = net.request({ + url: serverUrl + }); + urlRequest.end(); + urlRequest.on('redirect', () => { urlRequest.abort(); }); + urlRequest.on('error', () => {}); + await emittedOnce(urlRequest, 'abort'); + }); + + it('should not follow redirect when mode is error', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.statusCode = 302; + response.setHeader('Location', '/200'); + response.end(); + }); + const urlRequest = net.request({ + url: serverUrl, + redirect: 'error' + }); + urlRequest.end(); + await emittedOnce(urlRequest, 'error'); + }); + + it('should follow redirect when handler calls callback', async () => { + const serverUrl = await respondOnce.toRoutes({ '/redirectChain': (request, response) => { - response.statusCode = 301 - response.setHeader('Location', '/301') - response.end() - }, - '/301': (request, response) => { - response.statusCode = 301 - response.setHeader('Location', '/200') - response.end() - }, - '/200': (request, response) => { - response.statusCode = 200 - response.end() + response.statusCode = 302; + response.setHeader('Location', '/302'); + response.end(); }, - }).then(serverUrl => { - const urlRequest = net.request({ - url: `${serverUrl}/redirectChain`, - redirect: 'manual' - }) - let redirectCount = 0 - urlRequest.on('response', (response) => { - expect(response.statusCode).to.equal(200) - expect(redirectCount).to.equal(2) - done() - }) - urlRequest.on('redirect', (status, method, url) => { - if (url === `${serverUrl}/301` || url === `${serverUrl}/200`) { - redirectCount += 1 - urlRequest.followRedirect() - } - }) - urlRequest.end() - }) - }) - - it('should allow cancelling redirect when mode is manual', (done) => { - respondOnce.toRoutes({ - '/redirect': (request, response) => { - response.statusCode = 301 - response.setHeader('Location', '/200') - response.end() + '/302': (request, response) => { + response.statusCode = 302; + response.setHeader('Location', '/200'); + response.end(); }, '/200': (request, response) => { - response.statusCode = 200 - response.end() - }, - }).then(serverUrl => { - const urlRequest = net.request({ - url: `${serverUrl}/redirect`, - redirect: 'manual' - }) - urlRequest.on('response', (response) => { - expect(response.statusCode).that.equal(200) - response.on('data', () => {}) - response.on('end', () => { - urlRequest.abort() - }) - }) - let redirectCount = 0 - urlRequest.on('close', () => { - expect(redirectCount).to.equal(1) - done() - }) - urlRequest.on('redirect', (status, method, url) => { - if (url === `${serverUrl}/200`) { - redirectCount += 1 - urlRequest.followRedirect() - } - }) - urlRequest.end() - }) - }) + response.statusCode = 200; + response.end(); + } + }); + const urlRequest = net.request({ url: `${serverUrl}/redirectChain`, redirect: 'manual' }); + const redirects: string[] = []; + urlRequest.on('redirect', (status, method, url) => { + redirects.push(url); + urlRequest.followRedirect(); + }); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + expect(redirects).to.deep.equal([ + `${serverUrl}/302`, + `${serverUrl}/200` + ]); + }); it('should throw if given an invalid session option', () => { expect(() => { net.request({ url: 'https://foo', session: 1 as any - }) - }).to.throw("`session` should be an instance of the Session class") - }) + }); + }).to.throw('`session` should be an instance of the Session class'); + }); it('should throw if given an invalid partition option', () => { expect(() => { net.request({ url: 'https://foo', partition: 1 as any - }) - }).to.throw("`partition` should be a string") - }) - - it('should be able to create a request with options', (done) => { - const customHeaderName = 'Some-Custom-Header-Name' - const customHeaderValue = 'Some-Customer-Header-Value' - respondOnce.toURL('/', (request, response) => { - expect(request.method).to.equal('GET') - expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue) - response.statusCode = 200 - response.statusMessage = 'OK' - response.end() - }).then(serverUrlUnparsed => { - const serverUrl = url.parse(serverUrlUnparsed) - const options = { - port: serverUrl.port ? parseInt(serverUrl.port, 10) : undefined, - hostname: '127.0.0.1', - headers: { [customHeaderName]: customHeaderValue } - } - const urlRequest = net.request(options) - urlRequest.on('response', (response) => { - expect(response.statusCode).to.be.equal(200) - response.on('data', () => {}) - response.on('end', () => { - done() - }) - }) - urlRequest.end() - }) - }) - - it('should be able to pipe a readable stream into a net request', (done) => { - const bodyData = randomString(kOneMegaByte) - let netRequestReceived = false - let netRequestEnded = false - - Promise.all([ + }); + }).to.throw('`partition` should be a string'); + }); + + it('should be able to create a request with options', async () => { + const customHeaderName = 'Some-Custom-Header-Name'; + const customHeaderValue = 'Some-Customer-Header-Value'; + const serverUrlUnparsed = await respondOnce.toURL('/', (request, response) => { + expect(request.method).to.equal('GET'); + expect(request.headers[customHeaderName.toLowerCase()]).to.equal(customHeaderValue); + response.statusCode = 200; + response.statusMessage = 'OK'; + response.end(); + }); + const serverUrl = url.parse(serverUrlUnparsed); + const options = { + port: serverUrl.port ? parseInt(serverUrl.port, 10) : undefined, + hostname: '127.0.0.1', + headers: { [customHeaderName]: customHeaderValue } + }; + const urlRequest = net.request(options); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.be.equal(200); + await collectStreamBody(response); + }); + + it('should be able to pipe a readable stream into a net request', async () => { + const bodyData = randomString(kOneMegaByte); + let netRequestReceived = false; + let netRequestEnded = false; + + const [nodeServerUrl, netServerUrl] = await Promise.all([ respondOnce.toSingleURL((request, response) => response.end(bodyData)), respondOnce.toSingleURL((request, response) => { - netRequestReceived = true - let receivedBodyData = '' + netRequestReceived = true; + let receivedBodyData = ''; request.on('data', (chunk) => { - receivedBodyData += chunk.toString() - }) + receivedBodyData += chunk.toString(); + }); request.on('end', (chunk: Buffer | undefined) => { - netRequestEnded = true + netRequestEnded = true; if (chunk) { - receivedBodyData += chunk.toString() + receivedBodyData += chunk.toString(); } - expect(receivedBodyData).to.be.equal(bodyData) - response.end() - }) - }) - ]).then(([nodeServerUrl, netServerUrl]) => { - const nodeRequest = http.request(nodeServerUrl) - nodeRequest.on('response', (nodeResponse) => { - const netRequest = net.request(netServerUrl) - netRequest.on('response', (netResponse) => { - expect(netResponse.statusCode).to.equal(200) - netResponse.on('data', (chunk) => {}) - netResponse.on('end', () => { - expect(netRequestReceived).to.be.true('net request received') - expect(netRequestEnded).to.be.true('net request ended') - done() - }) - }) - nodeResponse.pipe(netRequest) - }) - nodeRequest.end() - }) - }) - - it('should emit error event on server socket close', (done) => { - respondOnce.toSingleURL((request, response) => { - request.socket.destroy() - }).then(serverUrl => { - let requestErrorEventEmitted = false - const urlRequest = net.request(serverUrl) - urlRequest.on('error', (error) => { - expect(error).to.be.an('Error') - requestErrorEventEmitted = true - }) - urlRequest.on('close', () => { - expect(requestErrorEventEmitted).to.be.true('request error event was emitted') - done() + expect(receivedBodyData).to.be.equal(bodyData); + response.end(); + }); }) - urlRequest.end() - }) - }) - }) + ]); + const nodeRequest = http.request(nodeServerUrl); + const nodeResponse = await getResponse(nodeRequest as any) as any as http.ServerResponse; + const netRequest = net.request(netServerUrl); + const responsePromise = emittedOnce(netRequest, 'response'); + // TODO(@MarshallOfSound) - FIXME with #22730 + nodeResponse.pipe(netRequest as any); + const [netResponse] = await responsePromise; + expect(netResponse.statusCode).to.equal(200); + await collectStreamBody(netResponse); + expect(netRequestReceived).to.be.true('net request received'); + expect(netRequestEnded).to.be.true('net request ended'); + }); + + it('should report upload progress', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.end(); + }); + const netRequest = net.request({ url: serverUrl, method: 'POST' }); + expect(netRequest.getUploadProgress()).to.deep.equal({ active: false }); + netRequest.end(Buffer.from('hello')); + const [position, total] = await emittedOnce(netRequest, 'upload-progress'); + expect(netRequest.getUploadProgress()).to.deep.equal({ active: true, started: true, current: position, total }); + }); + + it('should emit error event on server socket destroy', async () => { + const serverUrl = await respondOnce.toSingleURL((request) => { + request.socket.destroy(); + }); + const urlRequest = net.request(serverUrl); + urlRequest.end(); + const [error] = await emittedOnce(urlRequest, 'error'); + expect(error.message).to.equal('net::ERR_EMPTY_RESPONSE'); + }); + + it('should emit error event on server request destroy', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + request.destroy(); + response.end(); + }); + const urlRequest = net.request(serverUrl); + urlRequest.end(randomBuffer(kOneMegaByte)); + const [error] = await emittedOnce(urlRequest, 'error'); + expect(error.message).to.be.oneOf(['net::ERR_CONNECTION_RESET', 'net::ERR_CONNECTION_ABORTED']); + }); + + it('should not emit any event after close', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.end(); + }); + + const urlRequest = net.request(serverUrl); + urlRequest.end(); + + await emittedOnce(urlRequest, 'close'); + await new Promise((resolve, reject) => { + ['finish', 'abort', 'close', 'error'].forEach(evName => { + urlRequest.on(evName as any, () => { + reject(new Error(`Unexpected ${evName} event`)); + }); + }); + setTimeout(resolve, 50); + }); + }); + + it('should remove the referer header when no referrer url specified', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + expect(request.headers.referer).to.equal(undefined); + response.statusCode = 200; + response.statusMessage = 'OK'; + response.end(); + }); + const urlRequest = net.request(serverUrl); + urlRequest.end(); + + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + }); + + it('should set the referer header when a referrer url specified', async () => { + const referrerURL = 'https://www.electronjs.org/'; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + expect(request.headers.referer).to.equal(referrerURL); + response.statusCode = 200; + response.statusMessage = 'OK'; + response.end(); + }); + const urlRequest = net.request(serverUrl); + urlRequest.setHeader('referer', referrerURL); + urlRequest.end(); + + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + await collectStreamBody(response); + }); + }); describe('IncomingMessage API', () => { - it('response object should implement the IncomingMessage API', (done) => { - const customHeaderName = 'Some-Custom-Header-Name' - const customHeaderValue = 'Some-Customer-Header-Value' - - respondOnce.toSingleURL((request, response) => { - response.statusCode = 200 - response.statusMessage = 'OK' - response.setHeader(customHeaderName, customHeaderValue) - response.end() - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - - urlRequest.on('response', (response) => { - expect(response.statusCode).to.equal(200) - expect(response.statusMessage).to.equal('OK') - - const headers = response.headers - expect(headers).to.be.an('object') - const headerValue = headers[customHeaderName.toLowerCase()] - expect(headerValue).to.equal(customHeaderValue) - - const httpVersion = response.httpVersion - expect(httpVersion).to.be.a('string').and.to.have.lengthOf.at.least(1) - - const httpVersionMajor = response.httpVersionMajor - expect(httpVersionMajor).to.be.a('number').and.to.be.at.least(1) - - const httpVersionMinor = response.httpVersionMinor - expect(httpVersionMinor).to.be.a('number').and.to.be.at.least(0) - - response.on('data', chunk => {}) - response.on('end', () => { done() }) - }) - urlRequest.end() - }) - }) - - it('should discard duplicate headers', (done) => { - const includedHeader = 'max-forwards' - const discardableHeader = 'Max-Forwards' - - const includedHeaderValue = 'max-fwds-val' - const discardableHeaderValue = 'max-fwds-val-two' - - respondOnce.toSingleURL((request, response) => { - response.statusCode = 200 - response.statusMessage = 'OK' - response.setHeader(discardableHeader, discardableHeaderValue) - response.setHeader(includedHeader, includedHeaderValue) - response.end() - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - - urlRequest.on('response', response => { - expect(response.statusCode).to.equal(200) - expect(response.statusMessage).to.equal('OK') - - const headers = response.headers - expect(headers).to.be.an('object') - - expect(headers).to.have.property(includedHeader) - expect(headers).to.not.have.property(discardableHeader) - expect(headers[includedHeader]).to.equal(includedHeaderValue) - - response.on('data', chunk => {}) - response.on('end', () => { done() }) - }) - urlRequest.end() - }) - }) - - it('should join repeated non-discardable value with ,', (done) => { - respondOnce.toSingleURL((request, response) => { - response.statusCode = 200 - response.statusMessage = 'OK' - response.setHeader('referrer-policy', ['first-text', 'second-text']) - response.end() - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - - urlRequest.on('response', response => { - expect(response.statusCode).to.equal(200) - expect(response.statusMessage).to.equal('OK') - - const headers = response.headers - expect(headers).to.be.an('object') - expect(headers).to.have.property('referrer-policy') - expect(headers['referrer-policy']).to.equal('first-text, second-text') - - response.on('data', chunk => {}) - response.on('end', () => { done() }) - }) - urlRequest.end() - }) - }) - - it('should be able to pipe a net response into a writable stream', (done) => { - const bodyData = randomString(kOneKiloByte) - Promise.all([ + it('response object should implement the IncomingMessage API', async () => { + const customHeaderName = 'Some-Custom-Header-Name'; + const customHeaderValue = 'Some-Customer-Header-Value'; + + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.statusCode = 200; + response.statusMessage = 'OK'; + response.setHeader(customHeaderName, customHeaderValue); + response.end(); + }); + + const urlRequest = net.request(serverUrl); + const response = await getResponse(urlRequest); + + expect(response.statusCode).to.equal(200); + expect(response.statusMessage).to.equal('OK'); + + const headers = response.headers; + expect(headers).to.be.an('object'); + const headerValue = headers[customHeaderName.toLowerCase()]; + expect(headerValue).to.equal(customHeaderValue); + + const httpVersion = response.httpVersion; + expect(httpVersion).to.be.a('string').and.to.have.lengthOf.at.least(1); + + const httpVersionMajor = response.httpVersionMajor; + expect(httpVersionMajor).to.be.a('number').and.to.be.at.least(1); + + const httpVersionMinor = response.httpVersionMinor; + expect(httpVersionMinor).to.be.a('number').and.to.be.at.least(0); + + await collectStreamBody(response); + }); + + it('should discard duplicate headers', async () => { + const includedHeader = 'max-forwards'; + const discardableHeader = 'Max-Forwards'; + + const includedHeaderValue = 'max-fwds-val'; + const discardableHeaderValue = 'max-fwds-val-two'; + + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.statusCode = 200; + response.statusMessage = 'OK'; + response.setHeader(discardableHeader, discardableHeaderValue); + response.setHeader(includedHeader, includedHeaderValue); + response.end(); + }); + const urlRequest = net.request(serverUrl); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + expect(response.statusMessage).to.equal('OK'); + + const headers = response.headers; + expect(headers).to.be.an('object'); + + expect(headers).to.have.property(includedHeader); + expect(headers).to.not.have.property(discardableHeader); + expect(headers[includedHeader]).to.equal(includedHeaderValue); + + await collectStreamBody(response); + }); + + it('should join repeated non-discardable value with ,', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.statusCode = 200; + response.statusMessage = 'OK'; + response.setHeader('referrer-policy', ['first-text', 'second-text']); + response.end(); + }); + const urlRequest = net.request(serverUrl); + const response = await getResponse(urlRequest); + expect(response.statusCode).to.equal(200); + expect(response.statusMessage).to.equal('OK'); + + const headers = response.headers; + expect(headers).to.be.an('object'); + expect(headers).to.have.property('referrer-policy'); + expect(headers['referrer-policy']).to.equal('first-text, second-text'); + + await collectStreamBody(response); + }); + + it('should be able to pipe a net response into a writable stream', async () => { + const bodyData = randomString(kOneKiloByte); + let nodeRequestProcessed = false; + const [netServerUrl, nodeServerUrl] = await Promise.all([ respondOnce.toSingleURL((request, response) => response.end(bodyData)), - respondOnce.toSingleURL((request, response) => { - let receivedBodyData = '' - request.on('data', (chunk) => { - receivedBodyData += chunk.toString() - }) - request.on('end', (chunk: Buffer | undefined) => { - if (chunk) { - receivedBodyData += chunk.toString() - } - expect(receivedBodyData).to.be.equal(bodyData) - response.end() - }) - }) - ]).then(([netServerUrl, nodeServerUrl]) => { - const netRequest = net.request(netServerUrl) - netRequest.on('response', (netResponse) => { - const serverUrl = url.parse(nodeServerUrl) - const nodeOptions = { - method: 'POST', - path: serverUrl.path, - port: serverUrl.port - } - const nodeRequest = http.request(nodeOptions, res => { - res.on('data', (chunk) => {}) - res.on('end', () => { - done() - }) - }); - // TODO: IncomingMessage should properly extend ReadableStream in the - // docs - (netResponse as any).pipe(nodeRequest) - }) - netRequest.end() - }) - }) - - it('should not emit any event after close', (done) => { - const bodyData = randomString(kOneKiloByte) - respondOnce.toSingleURL((request, response) => { - response.end(bodyData) - }).then(serverUrl => { - let requestCloseEventEmitted = false - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - expect(requestCloseEventEmitted).to.be.false('request close event emitted') - const statusCode = response.statusCode - expect(statusCode).to.equal(200) - response.on('data', () => { }) - response.on('end', () => { }) - response.on('error', () => { - expect(requestCloseEventEmitted).to.be.false('request close event emitted') - }) - response.on('aborted', () => { - expect(requestCloseEventEmitted).to.be.false('request close event emitted') - }) - }) - urlRequest.on('finish', () => { - expect(requestCloseEventEmitted).to.be.false('request close event emitted') - }) - urlRequest.on('error', () => { - expect(requestCloseEventEmitted).to.be.false('request close event emitted') - }) - urlRequest.on('abort', () => { - expect(requestCloseEventEmitted).to.be.false('request close event emitted') - }) - urlRequest.on('close', () => { - requestCloseEventEmitted = true - // Wait so that all async events get scheduled. - setTimeout(() => { - done() - }, 100) - }) - urlRequest.end() - }) - }) - }) + respondOnce.toSingleURL(async (request, response) => { + const receivedBodyData = await collectStreamBody(request); + expect(receivedBodyData).to.be.equal(bodyData); + nodeRequestProcessed = true; + response.end(); + }) + ]); + const netRequest = net.request(netServerUrl); + const netResponse = await getResponse(netRequest); + const serverUrl = url.parse(nodeServerUrl); + const nodeOptions = { + method: 'POST', + path: serverUrl.path, + port: serverUrl.port + }; + const nodeRequest = http.request(nodeOptions); + const nodeResponsePromise = emittedOnce(nodeRequest, 'response'); + // TODO(@MarshallOfSound) - FIXME with #22730 + (netResponse as any).pipe(nodeRequest); + const [nodeResponse] = await nodeResponsePromise; + netRequest.end(); + await collectStreamBody(nodeResponse); + expect(nodeRequestProcessed).to.equal(true); + }); + }); describe('Stability and performance', () => { it('should free unreferenced, never-started request objects without crash', (done) => { - net.request('https://test') + net.request('https://test'); process.nextTick(() => { - const v8Util = process.electronBinding('v8_util') - v8Util.requestGarbageCollectionForTesting() - done() - }) - }) - - it('should collect on-going requests without crash', (done) => { - let finishResponse: (() => void) | null = null - respondOnce.toSingleURL((request, response) => { - response.write(randomString(kOneKiloByte)) + const v8Util = process.electronBinding('v8_util'); + v8Util.requestGarbageCollectionForTesting(); + done(); + }); + }); + + it('should collect on-going requests without crash', async () => { + let finishResponse: (() => void) | null = null; + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.write(randomString(kOneKiloByte)); finishResponse = () => { - response.write(randomString(kOneKiloByte)) - response.end() - } - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - response.on('data', () => { }) - response.on('end', () => { - done() - }) - process.nextTick(() => { - // Trigger a garbage collection. - const v8Util = process.electronBinding('v8_util') - v8Util.requestGarbageCollectionForTesting() - finishResponse!() - }) - }) - urlRequest.end() - }) - }) - - it('should collect unreferenced, ended requests without crash', (done) => { - respondOnce.toSingleURL((request, response) => { - response.end() - }).then(serverUrl => { - const urlRequest = net.request(serverUrl) - urlRequest.on('response', (response) => { - response.on('data', () => {}) - response.on('end', () => {}) - }) - urlRequest.on('close', () => { - process.nextTick(() => { - const v8Util = process.electronBinding('v8_util') - v8Util.requestGarbageCollectionForTesting() - done() - }) - }) - urlRequest.end() - }) - }) - }) -}) + response.write(randomString(kOneKiloByte)); + response.end(); + }; + }); + const urlRequest = net.request(serverUrl); + const response = await getResponse(urlRequest); + process.nextTick(() => { + // Trigger a garbage collection. + const v8Util = process.electronBinding('v8_util'); + v8Util.requestGarbageCollectionForTesting(); + finishResponse!(); + }); + await collectStreamBody(response); + }); + + it('should collect unreferenced, ended requests without crash', async () => { + const serverUrl = await respondOnce.toSingleURL((request, response) => { + response.end(); + }); + const urlRequest = net.request(serverUrl); + process.nextTick(() => { + const v8Util = process.electronBinding('v8_util'); + v8Util.requestGarbageCollectionForTesting(); + }); + const response = await getResponse(urlRequest); + await collectStreamBody(response); + }); + + it('should finish sending data when urlRequest is unreferenced', async () => { + const serverUrl = await respondOnce.toSingleURL(async (request, response) => { + const received = await collectStreamBodyBuffer(request); + expect(received.length).to.equal(kOneMegaByte); + response.end(); + }); + const urlRequest = net.request(serverUrl); + urlRequest.on('close', () => { + process.nextTick(() => { + const v8Util = process.electronBinding('v8_util'); + v8Util.requestGarbageCollectionForTesting(); + }); + }); + urlRequest.write(randomBuffer(kOneMegaByte)); + const response = await getResponse(urlRequest); + await collectStreamBody(response); + }); + + it('should finish sending data when urlRequest is unreferenced for chunked encoding', async () => { + const serverUrl = await respondOnce.toSingleURL(async (request, response) => { + const received = await collectStreamBodyBuffer(request); + response.end(); + expect(received.length).to.equal(kOneMegaByte); + }); + const urlRequest = net.request(serverUrl); + urlRequest.chunkedEncoding = true; + urlRequest.write(randomBuffer(kOneMegaByte)); + const response = await getResponse(urlRequest); + await collectStreamBody(response); + process.nextTick(() => { + const v8Util = process.electronBinding('v8_util'); + v8Util.requestGarbageCollectionForTesting(); + }); + }); + + it('should finish sending data when urlRequest is unreferenced before close event for chunked encoding', async () => { + const serverUrl = await respondOnce.toSingleURL(async (request, response) => { + const received = await collectStreamBodyBuffer(request); + response.end(); + expect(received.length).to.equal(kOneMegaByte); + }); + const urlRequest = net.request(serverUrl); + urlRequest.chunkedEncoding = true; + urlRequest.write(randomBuffer(kOneMegaByte)); + const v8Util = process.electronBinding('v8_util'); + v8Util.requestGarbageCollectionForTesting(); + await collectStreamBody(await getResponse(urlRequest)); + }); + + it('should finish sending data when urlRequest is unreferenced', async () => { + const serverUrl = await respondOnce.toSingleURL(async (request, response) => { + const received = await collectStreamBodyBuffer(request); + response.end(); + expect(received.length).to.equal(kOneMegaByte); + }); + const urlRequest = net.request(serverUrl); + urlRequest.on('close', () => { + process.nextTick(() => { + const v8Util = process.electronBinding('v8_util'); + v8Util.requestGarbageCollectionForTesting(); + }); + }); + urlRequest.write(randomBuffer(kOneMegaByte)); + await collectStreamBody(await getResponse(urlRequest)); + }); + + it('should finish sending data when urlRequest is unreferenced for chunked encoding', async () => { + const serverUrl = await respondOnce.toSingleURL(async (request, response) => { + const received = await collectStreamBodyBuffer(request); + response.end(); + expect(received.length).to.equal(kOneMegaByte); + }); + const urlRequest = net.request(serverUrl); + urlRequest.on('close', () => { + process.nextTick(() => { + const v8Util = process.electronBinding('v8_util'); + v8Util.requestGarbageCollectionForTesting(); + }); + }); + urlRequest.chunkedEncoding = true; + urlRequest.write(randomBuffer(kOneMegaByte)); + await collectStreamBody(await getResponse(urlRequest)); + }); + }); +}); diff --git a/spec-main/api-power-monitor-spec.ts b/spec-main/api-power-monitor-spec.ts index 38063252ca0b4..9e180be0e9c50 100644 --- a/spec-main/api-power-monitor-spec.ts +++ b/spec-main/api-power-monitor-spec.ts @@ -14,7 +14,8 @@ import { promisify } from 'util' describe('powerMonitor', () => { let logindMock: any, dbusMockPowerMonitor: any, getCalls: any, emitSignal: any, reset: any - ifdescribe(process.platform === 'linux' && process.env.DBUS_SYSTEM_BUS_ADDRESS != null)('when powerMonitor module is loaded with dbus mock', () => { + // TODO(deepak1556): Enable on arm64 after upgrade, it crashes at the moment. + ifdescribe(process.platform === 'linux' && process.arch !== 'arm64' && process.env.DBUS_SYSTEM_BUS_ADDRESS != null)('when powerMonitor module is loaded with dbus mock', () => { before(async () => { const systemBus = dbus.systemBus() const loginService = systemBus.getService('org.freedesktop.login1') diff --git a/spec-main/api-protocol-spec.ts b/spec-main/api-protocol-spec.ts index 67c38936aaaee..754410940f393 100644 --- a/spec-main/api-protocol-spec.ts +++ b/spec-main/api-protocol-spec.ts @@ -392,6 +392,25 @@ describe('protocol module', () => { const r = await ajax(protocolName + '://fake-host') expect(r.data).to.have.lengthOf(data.length) }) + + it('can handle a stream completing while writing', async () => { + function dumbPassthrough () { + return new stream.Transform({ + async transform (chunk, encoding, cb) { + cb(null, chunk) + } + }) + } + await registerStreamProtocol(protocolName, (request, callback) => { + callback({ + statusCode: 200, + headers: { 'Content-Type': 'text/plain' }, + data: getStream(1024 * 1024, Buffer.alloc(1024 * 1024 * 2)).pipe(dumbPassthrough()) + }) + }) + const r = await ajax(protocolName + '://fake-host') + expect(r.data).to.have.lengthOf(1024 * 1024 * 2) + }) }) describe('protocol.isProtocolHandled', () => { @@ -529,12 +548,12 @@ describe('protocol module', () => { const port = (server.address() as AddressInfo).port const url = `http://127.0.0.1:${port}` await interceptHttpProtocol('http', (request, callback) => { - const data = { + const data: Electron.RedirectRequest = { url: url, method: 'POST', uploadData: { contentType: 'application/x-www-form-urlencoded', - bytes: request.uploadData[0].bytes + data: request.uploadData[0].bytes }, session: null } diff --git a/spec-main/api-session-spec.ts b/spec-main/api-session-spec.ts index 40975b9713722..9c501573181d5 100644 --- a/spec-main/api-session-spec.ts +++ b/spec-main/api-session-spec.ts @@ -58,6 +58,15 @@ describe('session module', () => { const value = '0' afterEach(closeAllWindows) + // Clear cookie of defaultSession after each test. + afterEach(async () => { + const { cookies } = session.defaultSession + const cs = await cookies.get({ url }) + for (const c of cs) { + await cookies.remove(url, c.name) + } + }) + it('should get cookies', async () => { const server = http.createServer((req, res) => { res.setHeader('Set-Cookie', [`${name}=${value}`]) @@ -78,9 +87,33 @@ describe('session module', () => { const name = '1' const value = '1' - await cookies.set({ url, name, value, expirationDate: (+new Date) / 1000 + 120 }) - const cs = await cookies.get({ url }) - expect(cs.some(c => c.name === name && c.value === value)).to.equal(true) + await cookies.set({ url, name, value, expirationDate: (+new Date()) / 1000 + 120 }) + const c = (await cookies.get({ url }))[0] + expect(c.name).to.equal(name) + expect(c.value).to.equal(value) + expect(c.session).to.equal(false) + }) + + it('sets session cookies', async () => { + const { cookies } = session.defaultSession + const name = '2' + const value = '1' + + await cookies.set({ url, name, value }) + const c = (await cookies.get({ url }))[0] + expect(c.name).to.equal(name) + expect(c.value).to.equal(value) + expect(c.session).to.equal(true) + }) + + it('sets cookies without name', async () => { + const { cookies } = session.defaultSession + const value = '3' + + await cookies.set({ url, value }) + const c = (await cookies.get({ url }))[0] + expect(c.name).to.be.empty() + expect(c.value).to.equal(value) }) it('gets cookies without url', async () => { @@ -313,11 +346,6 @@ describe('session module', () => { beforeEach(async () => { customSession = session.fromPartition('proxyconfig') - // FIXME(deepak1556): This is just a hack to force - // creation of request context which in turn initializes - // the network context, can be removed with network - // service enabled. - await customSession.clearHostResolverCache() }) afterEach(() => { @@ -516,15 +544,23 @@ describe('session module', () => { const fetch = (url: string) => new Promise((resolve, reject) => { const request = net.request({ url, session: ses }) request.on('response', (response) => { - let data = '' + let data: string | null = null response.on('data', (chunk) => { + if (!data) { + data = '' + } data += chunk }) response.on('end', () => { - resolve(data) + if (!data) { + reject(new Error('Empty response')) + } else { + resolve(data) + } }) response.on('error', (error: any) => { reject(new Error(error)) }) - }); + }) + request.on('error', (error: any) => { reject(new Error(error)) }) request.end() }) // the first time should throw due to unauthenticated diff --git a/spec-main/api-system-preferences-spec.ts b/spec-main/api-system-preferences-spec.ts index 85e776995dff1..158c0a08626c0 100644 --- a/spec-main/api-system-preferences-spec.ts +++ b/spec-main/api-system-preferences-spec.ts @@ -246,6 +246,11 @@ describe('systemPreferences module', () => { const microphoneStatus = systemPreferences.getMediaAccessStatus('microphone') expect(statuses).to.include(microphoneStatus) }) + + it('returns an access status for a screen access request', () => { + const screenStatus = systemPreferences.getMediaAccessStatus('screen') + expect(statuses).to.include(screenStatus) + }) }) describe('systemPreferences.getAnimationSettings()', () => { diff --git a/spec-main/api-touch-bar-spec.ts b/spec-main/api-touch-bar-spec.ts index c39210a052950..b6c04e82f2b7d 100644 --- a/spec-main/api-touch-bar-spec.ts +++ b/spec-main/api-touch-bar-spec.ts @@ -32,6 +32,14 @@ describe('TouchBar module', () => { }).to.throw('Escape item must be an instance of TouchBarItem') }) + it('throws an error if the same TouchBarItem is added multiple times', () => { + expect(() => { + const item = new TouchBarLabel({ label: 'Label' }) + const touchBar = new TouchBar({ items: [item, item] }) + touchBar.toString() + }).to.throw('Cannot add a single instance of TouchBarItem multiple times in a TouchBar') + }) + describe('BrowserWindow behavior', () => { let window: BrowserWindow diff --git a/spec-main/api-tray-spec.ts b/spec-main/api-tray-spec.ts index ebf1a76cc3bef..c1e755b222ac8 100644 --- a/spec-main/api-tray-spec.ts +++ b/spec-main/api-tray-spec.ts @@ -51,6 +51,13 @@ describe('tray module', () => { }) tray.popUpContextMenu() }) + + it('can be called with a menu', () => { + const menu = Menu.buildFromTemplate([{ label: 'Test' }]); + expect(() => { + tray.popUpContextMenu(menu); + }).to.not.throw(); + }); }) describe('tray.getBounds()', () => { diff --git a/spec-main/api-web-contents-spec.ts b/spec-main/api-web-contents-spec.ts index 9ac84deff121d..19d6978cd8fac 100644 --- a/spec-main/api-web-contents-spec.ts +++ b/spec-main/api-web-contents-spec.ts @@ -104,24 +104,46 @@ describe('webContents module', () => { }) ifdescribe(features.isPrintingEnabled())('webContents.print()', () => { + let w: BrowserWindow + + beforeEach(() => { + w = new BrowserWindow({ show: false }) + }) + afterEach(closeAllWindows) + it('throws when invalid settings are passed', () => { - const w = new BrowserWindow({ show: false }) expect(() => { // @ts-ignore this line is intentionally incorrect w.webContents.print(true) }).to.throw('webContents.print(): Invalid print settings specified.') + }) + it('throws when an invalid callback is passed', () => { expect(() => { // @ts-ignore this line is intentionally incorrect w.webContents.print({}, true) }).to.throw('webContents.print(): Invalid optional callback provided.') }) - it('does not crash', () => { - const w = new BrowserWindow({ show: false }) + ifit(process.platform !== 'linux')('throws when an invalid deviceName is passed', () => { expect(() => { - w.webContents.print({ silent: true }) + w.webContents.print({ deviceName: 'i-am-a-nonexistent-printer' }, () => {}) + }).to.throw('webContents.print(): Invalid deviceName provided.') + }) + + it('does not crash with custom margins', () => { + expect(() => { + w.webContents.print({ + silent: true, + margins: { + marginType: 'custom', + top: 1, + bottom: 1, + left: 1, + right: 1 + } + }) }).to.not.throw() }) }) @@ -219,6 +241,23 @@ describe('webContents module', () => { }) }) + describe('webContents.executeJavaScriptInIsolatedWorld', () => { + let w: BrowserWindow + + before(async () => { + w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } }) + await w.loadURL('about:blank') + }) + + it('resolves the returned promise with the result', async () => { + await w.webContents.executeJavaScriptInIsolatedWorld(999, [{ code: 'window.X = 123' }]) + const isolatedResult = await w.webContents.executeJavaScriptInIsolatedWorld(999, [{ code: 'window.X' }]) + const mainWorldResult = await w.webContents.executeJavaScript('window.X') + expect(isolatedResult).to.equal(123) + expect(mainWorldResult).to.equal(undefined) + }) + }) + describe('loadURL() promise API', () => { let w: BrowserWindow beforeEach(async () => { @@ -339,7 +378,7 @@ describe('webContents module', () => { const devToolsOpened = emittedOnce(w.webContents, 'devtools-opened') w.webContents.openDevTools() await devToolsOpened - expect(webContents.getFocusedWebContents().id).to.equal(w.webContents.devToolsWebContents.id) + expect(webContents.getFocusedWebContents().id).to.equal(w.webContents.devToolsWebContents!.id) const devToolsClosed = emittedOnce(w.webContents, 'devtools-closed') w.webContents.closeDevTools() await devToolsClosed @@ -423,7 +462,7 @@ describe('webContents module', () => { const w = new BrowserWindow({show: false}) w.webContents.openDevTools() w.webContents.once('devtools-opened', () => { - expect(w.webContents.devToolsWebContents.getWebPreferences()).to.be.null() + expect(w.webContents.devToolsWebContents!.getWebPreferences()).to.be.null() done() }) }) @@ -689,13 +728,11 @@ describe('webContents module', () => { expect(() => { w.webContents.startDrag({ file: __filename } as any) - }).to.throw(`Must specify 'icon' option`) + }).to.throw(`Must specify non-empty 'icon' option`) - if (process.platform === 'darwin') { - expect(() => { - w.webContents.startDrag({ file: __filename, icon: __filename }) - }).to.throw(`Must specify non-empty 'icon' option`) - } + expect(() => { + w.webContents.startDrag({ file: __filename, icon: __filename }) + }).to.throw(`Must specify non-empty 'icon' option`) }) }) @@ -756,7 +793,19 @@ describe('webContents module', () => { afterEach(closeAllWindows) - // TODO(codebytere): remove in Electron v8.0.0 + it('throws on an invalid zoomFactor', async () => { + const w = new BrowserWindow({ show: false }) + await w.loadURL('about:blank') + + expect(() => { + w.webContents.setZoomFactor(0.0) + }).to.throw(/'zoomFactor' must be a double greater than 0.0/) + + expect(() => { + w.webContents.setZoomFactor(-2.0) + }).to.throw(/'zoomFactor' must be a double greater than 0.0/) + }) + it('can set the correct zoom level (functions)', async () => { const w = new BrowserWindow({ show: false }) try { @@ -1402,6 +1451,19 @@ describe('webContents module', () => { const data = await w.webContents.printToPDF({}) expect(data).to.be.an.instanceof(Buffer).that.is.not.empty() }) + + it('does not crash when called multiple times', async () => { + const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }) + await w.loadURL('data:text/html,

Hello, World!

') + const promises = [] + for (let i = 0; i < 2; i++) { + promises.push(w.webContents.printToPDF({})) + } + const results = await Promise.all(promises) + for (const data of results) { + expect(data).to.be.an.instanceof(Buffer).that.is.not.empty() + } + }) }) describe('PictureInPicture video', () => { @@ -1442,11 +1504,11 @@ describe('webContents module', () => { const opened = emittedOnce(w.webContents, 'devtools-opened') w.webContents.openDevTools() await opened - await emittedOnce(w.webContents.devToolsWebContents, 'did-finish-load') - w.webContents.devToolsWebContents.focus() + await emittedOnce(w.webContents.devToolsWebContents!, 'did-finish-load') + w.webContents.devToolsWebContents!.focus() // Focus an input field - await w.webContents.devToolsWebContents.executeJavaScript(` + await w.webContents.devToolsWebContents!.executeJavaScript(` const input = document.createElement('input') document.body.innerHTML = '' document.body.appendChild(input) @@ -1456,7 +1518,7 @@ describe('webContents module', () => { // Write something to the clipboard clipboard.writeText('test value') - const pasted = w.webContents.devToolsWebContents.executeJavaScript(`new Promise(resolve => { + const pasted = w.webContents.devToolsWebContents!.executeJavaScript(`new Promise(resolve => { document.querySelector('input').addEventListener('paste', (e) => { resolve(e.target.value) }) @@ -1507,4 +1569,136 @@ describe('webContents module', () => { await devtoolsClosed }) }) + + describe('login event', () => { + afterEach(closeAllWindows) + + let server: http.Server + let serverUrl: string + let serverPort: number + let proxyServer: http.Server + let proxyServerPort: number + + before((done) => { + server = http.createServer((request, response) => { + if (request.url === '/no-auth') { + return response.end('ok') + } + if (request.headers.authorization) { + response.writeHead(200, { 'Content-type': 'text/plain' }) + return response.end(request.headers.authorization) + } + response + .writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }) + .end('401') + }).listen(0, '127.0.0.1', () => { + serverPort = (server.address() as AddressInfo).port + serverUrl = `http://127.0.0.1:${serverPort}` + done() + }) + }) + + before((done) => { + proxyServer = http.createServer((request, response) => { + if (request.headers['proxy-authorization']) { + response.writeHead(200, { 'Content-type': 'text/plain' }) + return response.end(request.headers['proxy-authorization']) + } + response + .writeHead(407, { 'Proxy-Authenticate': 'Basic realm="Foo"' }) + .end() + }).listen(0, '127.0.0.1', () => { + proxyServerPort = (proxyServer.address() as AddressInfo).port + done() + }) + }) + + afterEach(async () => { + await session.defaultSession.clearAuthCache({ type: 'password' }) + }) + + after(() => { + server.close() + proxyServer.close() + }) + + it('is emitted when navigating', async () => { + const [user, pass] = ['user', 'pass'] + const w = new BrowserWindow({ show: false }) + let eventRequest: any + let eventAuthInfo: any + w.webContents.on('login', (event, request, authInfo, cb) => { + eventRequest = request + eventAuthInfo = authInfo + event.preventDefault() + cb(user, pass) + }) + await w.loadURL(serverUrl) + const body = await w.webContents.executeJavaScript(`document.documentElement.textContent`) + expect(body).to.equal(`Basic ${Buffer.from(`${user}:${pass}`).toString('base64')}`) + expect(eventRequest.url).to.equal(serverUrl + '/') + expect(eventAuthInfo.isProxy).to.be.false() + expect(eventAuthInfo.scheme).to.equal('basic') + expect(eventAuthInfo.host).to.equal('127.0.0.1') + expect(eventAuthInfo.port).to.equal(serverPort) + expect(eventAuthInfo.realm).to.equal('Foo') + }) + + it('is emitted when a proxy requests authorization', async () => { + const customSession = session.fromPartition(`${Math.random()}`) + await customSession.setProxy({ proxyRules: `127.0.0.1:${proxyServerPort}`, proxyBypassRules: '<-loopback>' }) + const [user, pass] = ['user', 'pass'] + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }) + let eventRequest: any + let eventAuthInfo: any + w.webContents.on('login', (event, request, authInfo, cb) => { + eventRequest = request + eventAuthInfo = authInfo + event.preventDefault() + cb(user, pass) + }) + await w.loadURL(`${serverUrl}/no-auth`) + const body = await w.webContents.executeJavaScript(`document.documentElement.textContent`) + expect(body).to.equal(`Basic ${Buffer.from(`${user}:${pass}`).toString('base64')}`) + expect(eventRequest.url).to.equal(`${serverUrl}/no-auth`) + expect(eventAuthInfo.isProxy).to.be.true() + expect(eventAuthInfo.scheme).to.equal('basic') + expect(eventAuthInfo.host).to.equal('127.0.0.1') + expect(eventAuthInfo.port).to.equal(proxyServerPort) + expect(eventAuthInfo.realm).to.equal('Foo') + }) + + it('cancels authentication when callback is called with no arguments', async () => { + const w = new BrowserWindow({ show: false }) + w.webContents.on('login', (event, request, authInfo, cb) => { + event.preventDefault() + cb() + }) + await w.loadURL(serverUrl) + const body = await w.webContents.executeJavaScript(`document.documentElement.textContent`) + expect(body).to.equal('401') + }) + }) + + it('emits a cancelable event before creating a child webcontents', async () => { + const w = new BrowserWindow({ + show: false, + webPreferences: { + sandbox: true + } + }) + w.webContents.on('-will-add-new-contents' as any, (event: any, url: any) => { + expect(url).to.equal('about:blank') + event.preventDefault() + }) + let wasCalled = false + w.webContents.on('new-window' as any, () => { + wasCalled = true + }) + await w.loadURL('about:blank') + await w.webContents.executeJavaScript(`window.open('about:blank')`) + await new Promise((resolve) => { process.nextTick(resolve) }) + expect(wasCalled).to.equal(false) + await closeAllWindows() + }) }) diff --git a/spec-main/api-web-request-spec.ts b/spec-main/api-web-request-spec.ts index f19c8af1f5486..b212c705a3666 100644 --- a/spec-main/api-web-request-spec.ts +++ b/spec-main/api-web-request-spec.ts @@ -2,8 +2,10 @@ import { expect } from 'chai' import * as http from 'http' import * as qs from 'querystring' import * as path from 'path' -import { session, WebContents, webContents } from 'electron' +import * as WebSocket from 'ws' +import { ipcMain, session, WebContents, webContents } from 'electron' import { AddressInfo } from 'net'; +import { emittedOnce } from './events-helpers' const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures') @@ -20,6 +22,9 @@ describe('webRequest module', () => { if (req.headers.accept === '*/*;test/header') { content += 'header/received' } + if (req.headers.origin === 'http://new-origin') { + content += 'new/origin' + } res.end(content) } }) @@ -118,6 +123,14 @@ describe('webRequest module', () => { const { data } = await ajax(defaultURL) expect(data).to.equal('/redirect') }) + + it('does not crash for redirects', async () => { + ses.webRequest.onBeforeRequest((details, callback) => { + callback({ cancel: false }) + }) + await ajax(defaultURL + 'serverRedirect') + await ajax(defaultURL + 'serverRedirect') + }) }) describe('webRequest.onBeforeSendHeaders', () => { @@ -145,6 +158,16 @@ describe('webRequest module', () => { expect(data).to.equal('/header/received') }) + it('can change CORS headers', async () => { + ses.webRequest.onBeforeSendHeaders((details, callback) => { + const requestHeaders = details.requestHeaders + requestHeaders.Origin = 'http://new-origin' + callback({ requestHeaders: requestHeaders }) + }) + const { data } = await ajax(defaultURL) + expect(data).to.equal('/new/origin') + }) + it('resets the whole headers', async () => { const requestHeaders = { Test: 'header' @@ -199,6 +222,16 @@ describe('webRequest module', () => { expect(headers).to.match(/^custom: Changed$/m) }) + it('can change CORS headers', async () => { + ses.webRequest.onHeadersReceived((details, callback) => { + const responseHeaders = details.responseHeaders! + responseHeaders['access-control-allow-origin'] = ['http://new-origin'] as any + callback({ responseHeaders: responseHeaders }) + }) + const { headers } = await ajax(defaultURL) + expect(headers).to.match(/^access-control-allow-origin: http:\/\/new-origin$/m) + }) + it('does not change header by default', async () => { ses.webRequest.onHeadersReceived((details, callback) => { callback({}) @@ -317,4 +350,100 @@ describe('webRequest module', () => { await expect(ajax(defaultURL)).to.eventually.be.rejectedWith('404') }) }) + + describe('WebSocket connections', () => { + it('can be proxyed', async () => { + // Setup server. + const reqHeaders : { [key: string] : any } = {} + const server = http.createServer((req, res) => { + reqHeaders[req.url!] = req.headers + res.setHeader('foo1', 'bar1') + res.end('ok') + }) + const wss = new WebSocket.Server({ noServer: true }) + wss.on('connection', function connection (ws) { + ws.on('message', function incoming (message) { + if (message === 'foo') { + ws.send('bar') + } + }) + }) + server.on('upgrade', function upgrade (request, socket, head) { + const pathname = require('url').parse(request.url).pathname + if (pathname === '/websocket') { + reqHeaders[request.url] = request.headers + wss.handleUpgrade(request, socket, head, function done (ws) { + wss.emit('connection', ws, request) + }) + } + }) + + // Start server. + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)) + const port = String((server.address() as AddressInfo).port) + + // Use a separate session for testing. + const ses = session.fromPartition('WebRequestWebSocket') + + // Setup listeners. + const receivedHeaders : { [key: string] : any } = {} + ses.webRequest.onBeforeSendHeaders((details, callback) => { + details.requestHeaders.foo = 'bar' + callback({ requestHeaders: details.requestHeaders }) + }) + ses.webRequest.onHeadersReceived((details, callback) => { + const pathname = require('url').parse(details.url).pathname + receivedHeaders[pathname] = details.responseHeaders + callback({ cancel: false }) + }) + ses.webRequest.onResponseStarted((details) => { + if (details.url.startsWith('ws://')) { + expect(details.responseHeaders!['Connection'][0]).be.equal('Upgrade') + } else if (details.url.startsWith('http')) { + expect(details.responseHeaders!['foo1'][0]).be.equal('bar1') + } + }) + ses.webRequest.onSendHeaders((details) => { + if (details.url.startsWith('ws://')) { + expect(details.requestHeaders['foo']).be.equal('bar') + expect(details.requestHeaders['Upgrade']).be.equal('websocket') + } else if (details.url.startsWith('http')) { + expect(details.requestHeaders['foo']).be.equal('bar') + } + }) + ses.webRequest.onCompleted((details) => { + if (details.url.startsWith('ws://')) { + expect(details['error']).be.equal('net::ERR_WS_UPGRADE') + } else if (details.url.startsWith('http')) { + expect(details['error']).be.equal('net::OK') + } + }) + + const contents = (webContents as any).create({ + session: ses, + nodeIntegration: true, + webSecurity: false + }) + + // Cleanup. + after(() => { + contents.destroy() + server.close() + ses.webRequest.onBeforeRequest(null) + ses.webRequest.onBeforeSendHeaders(null) + ses.webRequest.onHeadersReceived(null) + ses.webRequest.onResponseStarted(null) + ses.webRequest.onSendHeaders(null) + ses.webRequest.onCompleted(null) + }) + + contents.loadFile(path.join(__dirname, 'fixtures', 'api', 'webrequest.html'), { query: { port } }) + await emittedOnce(ipcMain, 'websocket-success') + + expect(receivedHeaders['/websocket']['Upgrade'][0]).to.equal('websocket') + expect(receivedHeaders['/']['foo1'][0]).to.equal('bar1') + expect(reqHeaders['/websocket']['foo']).to.equal('bar') + expect(reqHeaders['/']['foo']).to.equal('bar') + }) + }) }) diff --git a/spec-main/chromium-spec.ts b/spec-main/chromium-spec.ts index 288ecddc7fd52..cecee71ca0e73 100644 --- a/spec-main/chromium-spec.ts +++ b/spec-main/chromium-spec.ts @@ -77,6 +77,36 @@ describe('reporting api', () => { server.close() } }) + + describe('window.open', () => { + it('denies custom open when nativeWindowOpen: true', async () => { + const w = new BrowserWindow({ + show: false, + webPreferences: { + contextIsolation: false, + nodeIntegration: true, + nativeWindowOpen: true + } + }); + w.loadURL('about:blank'); + + const previousListeners = process.listeners('uncaughtException'); + process.removeAllListeners('uncaughtException'); + try { + const uncaughtException = new Promise(resolve => { + process.once('uncaughtException', resolve); + }); + expect(await w.webContents.executeJavaScript(`(${function () { + const ipc = process.electronBinding('ipc').ipc; + return ipc.sendSync(true, 'ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', ['', '', ''])[0]; + }})()`)).to.be.null(); + const exception = await uncaughtException; + expect(exception.message).to.match(/denied: expected native window\.open/); + } finally { + previousListeners.forEach(l => process.on('uncaughtException', l)); + } + }); + }); }) describe('window.postMessage', () => { @@ -321,8 +351,9 @@ describe('chromium features', () => { const appLocale = app.getLocale() const w = new BrowserWindow({ show: false }) await w.loadURL('about:blank') - const languages = await w.webContents.executeJavaScript(`navigator.languages`) - expect(languages).to.deep.equal([appLocale]) + const languages = await w.webContents.executeJavaScript('navigator.languages') + expect(languages.length).to.be.greaterThan(0) + expect(languages).to.contain(appLocale) }) }) @@ -426,6 +457,85 @@ describe('chromium features', () => { }) }) + describe('form submit', () => { + let server: http.Server + let serverUrl: string + + before(async () => { + server = http.createServer((req, res) => { + let body = '' + req.on('data', (chunk) => { + body += chunk + }) + res.setHeader('Content-Type', 'application/json') + req.on('end', () => { + res.end(`body:${body}`) + }) + }) + await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)) + serverUrl = `http://localhost:${(server.address() as any).port}` + }) + after(async () => { + server.close() + await closeAllWindows() + }); + + [true, false].forEach((isSandboxEnabled) => + describe(`sandbox=${isSandboxEnabled}`, () => { + it('posts data in the same window', () => { + const w = new BrowserWindow({ + show: false, + webPreferences: { + sandbox: isSandboxEnabled + } + }) + + return new Promise(async (resolve) => { + await w.loadFile(path.join(fixturesPath, 'pages', 'form-with-data.html')) + + w.webContents.once('did-finish-load', async () => { + const res = await w.webContents.executeJavaScript('document.body.innerText') + expect(res).to.equal('body:greeting=hello') + resolve() + }) + + w.webContents.executeJavaScript(` + const form = document.querySelector('form') + form.action = '${serverUrl}'; + form.submit(); + `) + }) + }) + + it('posts data to a new window with target=_blank', () => { + const w = new BrowserWindow({ + show: false, + webPreferences: { + sandbox: isSandboxEnabled + } + }) + + return new Promise(async (resolve) => { + await w.loadFile(path.join(fixturesPath, 'pages', 'form-with-data.html')) + + app.once('browser-window-created', async (event, newWin) => { + const res = await newWin.webContents.executeJavaScript('document.body.innerText') + expect(res).to.equal('body:greeting=hello') + resolve() + }) + + w.webContents.executeJavaScript(` + const form = document.querySelector('form') + form.action = '${serverUrl}'; + form.target = '_blank'; + form.submit(); + `) + }) + }) + }) + ) + }) + describe('window.open', () => { for (const show of [true, false]) { it(`inherits parent visibility over parent {show=${show}} option`, (done) => { @@ -618,6 +728,21 @@ describe('chromium features', () => { expect(labels.some((l: any) => l)).to.be.false() }) + it('returns the same device ids across reloads', async () => { + const ses = session.fromPartition('persist:media-device-id') + const w = new BrowserWindow({ + show: false, + webPreferences: { + nodeIntegration: true, + session: ses + } + }) + w.loadFile(path.join(fixturesPath, 'pages', 'media-id-reset.html')) + const [, firstDeviceIds] = await emittedOnce(ipcMain, 'deviceIds') + const [, secondDeviceIds] = await emittedOnce(ipcMain, 'deviceIds', () => w.webContents.reload()) + expect(firstDeviceIds).to.deep.equal(secondDeviceIds) + }) + it('can return new device id when cookie storage is cleared', async () => { const ses = session.fromPartition('persist:media-device-id') const w = new BrowserWindow({ @@ -630,8 +755,7 @@ describe('chromium features', () => { w.loadFile(path.join(fixturesPath, 'pages', 'media-id-reset.html')) const [, firstDeviceIds] = await emittedOnce(ipcMain, 'deviceIds') await ses.clearStorageData({ storages: ['cookies'] }) - w.webContents.reload() - const [, secondDeviceIds] = await emittedOnce(ipcMain, 'deviceIds') + const [, secondDeviceIds] = await emittedOnce(ipcMain, 'deviceIds', () => w.webContents.reload()) expect(firstDeviceIds).to.not.deep.equal(secondDeviceIds) }) }) @@ -689,23 +813,28 @@ describe('chromium features', () => { afterEach(closeAllWindows) describe('when opened from main window', () => { - for (const {parent, child, nodeIntegration, nativeWindowOpen, openerAccessible} of table) { - const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration} nativeWindowOpen=${nativeWindowOpen}, child should ${openerAccessible ? '' : 'not '}be able to access opener` - it(description, async () => { - const w = new BrowserWindow({show: false, webPreferences: { nodeIntegration: true, nativeWindowOpen }}) - await w.loadURL(parent) - const childOpenerLocation = await w.webContents.executeJavaScript(`new Promise(resolve => { - window.addEventListener('message', function f(e) { - resolve(e.data) + for (const { parent, child, nodeIntegration, nativeWindowOpen, openerAccessible } of table) { + for (const sandboxPopup of [false, true]) { + const description = `when parent=${s(parent)} opens child=${s(child)} with nodeIntegration=${nodeIntegration} nativeWindowOpen=${nativeWindowOpen} sandboxPopup=${sandboxPopup}, child should ${openerAccessible ? '' : 'not '}be able to access opener` + it(description, async () => { + const w = new BrowserWindow({show: false, webPreferences: { nodeIntegration: true, nativeWindowOpen }}) + w.webContents.once('new-window', (e, url, frameName, disposition, options) => { + options!.webPreferences!.sandbox = sandboxPopup }) - window.open(${JSON.stringify(child)}, "", "show=no,nodeIntegration=${nodeIntegration ? "yes" : "no"}") - })`) - if (openerAccessible) { - expect(childOpenerLocation).to.be.a('string') - } else { - expect(childOpenerLocation).to.be.null() - } - }) + await w.loadURL(parent) + const childOpenerLocation = await w.webContents.executeJavaScript(`new Promise(resolve => { + window.addEventListener('message', function f(e) { + resolve(e.data) + }) + window.open(${JSON.stringify(child)}, "", "show=no,nodeIntegration=${nodeIntegration ? "yes" : "no"}") + })`) + if (openerAccessible) { + expect(childOpenerLocation).to.be.a('string') + } else { + expect(childOpenerLocation).to.be.null() + } + }) + } } }) @@ -885,10 +1014,170 @@ describe('chromium features', () => { }) } - testLocalStorageAfterXSiteRedirect('after a cross-site redirect') - testLocalStorageAfterXSiteRedirect('after a cross-site redirect in sandbox mode', { sandbox: true }) - }) - }) + testLocalStorageAfterXSiteRedirect('after a cross-site redirect'); + testLocalStorageAfterXSiteRedirect('after a cross-site redirect in sandbox mode', { sandbox: true }); + }); + + describe('enableWebSQL webpreference', () => { + const standardScheme = (global as any).standardScheme; + const origin = `${standardScheme}://fake-host`; + const filePath = path.join(fixturesPath, 'pages', 'storage', 'web_sql.html'); + const sqlPartition = 'web-sql-preference-test'; + const sqlSession = session.fromPartition(sqlPartition); + const securityError = 'An attempt was made to break through the security policy of the user agent.'; + let contents: WebContents, w: BrowserWindow; + + before(() => { + sqlSession.protocol.registerFileProtocol(standardScheme, (request, callback) => { + callback({ path: filePath }); + }); + }); + + after(() => { + sqlSession.protocol.unregisterProtocol(standardScheme); + }); + + afterEach(async () => { + if (contents) { + (contents as any).destroy(); + contents = null as any; + } + await closeAllWindows(); + (w as any) = null; + }); + + it('default value allows websql', async () => { + contents = (webContents as any).create({ + session: sqlSession, + nodeIntegration: true + }); + contents.loadURL(origin); + const [, error] = await emittedOnce(ipcMain, 'web-sql-response'); + expect(error).to.be.null(); + }); + + it('when set to false can disallow websql', async () => { + contents = (webContents as any).create({ + session: sqlSession, + nodeIntegration: true, + enableWebSQL: false + }); + contents.loadURL(origin); + const [, error] = await emittedOnce(ipcMain, 'web-sql-response'); + expect(error).to.equal(securityError); + }); + + it('when set to false does not disable indexedDB', async () => { + contents = (webContents as any).create({ + session: sqlSession, + nodeIntegration: true, + enableWebSQL: false + }); + contents.loadURL(origin); + const [, error] = await emittedOnce(ipcMain, 'web-sql-response'); + expect(error).to.equal(securityError); + const dbName = 'random'; + const result = await contents.executeJavaScript(` + new Promise((resolve, reject) => { + try { + let req = window.indexedDB.open('${dbName}'); + req.onsuccess = (event) => { + let db = req.result; + resolve(db.name); + } + req.onerror = (event) => { resolve(event.target.code); } + } catch (e) { + resolve(e.message); + } + }); + `); + expect(result).to.equal(dbName); + }); + + it('child webContents can override when the embedder has allowed websql', async () => { + w = new BrowserWindow({ + show: false, + webPreferences: { + nodeIntegration: true, + webviewTag: true, + session: sqlSession + } + }); + w.webContents.loadURL(origin); + const [, error] = await emittedOnce(ipcMain, 'web-sql-response'); + expect(error).to.be.null(); + const webviewResult = emittedOnce(ipcMain, 'web-sql-response'); + await w.webContents.executeJavaScript(` + new Promise((resolve, reject) => { + const webview = new WebView(); + webview.setAttribute('src', '${origin}'); + webview.setAttribute('webpreferences', 'enableWebSQL=0'); + webview.setAttribute('partition', '${sqlPartition}'); + webview.setAttribute('nodeIntegration', 'on'); + document.body.appendChild(webview); + webview.addEventListener('dom-ready', () => resolve()); + }); + `); + const [, childError] = await webviewResult; + expect(childError).to.equal(securityError); + }); + + it('child webContents cannot override when the embedder has disallowed websql', async () => { + w = new BrowserWindow({ + show: false, + webPreferences: { + nodeIntegration: true, + enableWebSQL: false, + webviewTag: true, + session: sqlSession + } + }); + w.webContents.loadURL('data:text/html,'); + const webviewResult = emittedOnce(ipcMain, 'web-sql-response'); + await w.webContents.executeJavaScript(` + new Promise((resolve, reject) => { + const webview = new WebView(); + webview.setAttribute('src', '${origin}'); + webview.setAttribute('webpreferences', 'enableWebSQL=1'); + webview.setAttribute('partition', '${sqlPartition}'); + webview.setAttribute('nodeIntegration', 'on'); + document.body.appendChild(webview); + webview.addEventListener('dom-ready', () => resolve()); + }); + `); + const [, childError] = await webviewResult; + expect(childError).to.equal(securityError); + }); + + it('child webContents can use websql when the embedder has allowed websql', async () => { + w = new BrowserWindow({ + show: false, + webPreferences: { + nodeIntegration: true, + webviewTag: true, + session: sqlSession + } + }); + w.webContents.loadURL(origin); + const [, error] = await emittedOnce(ipcMain, 'web-sql-response'); + expect(error).to.be.null(); + const webviewResult = emittedOnce(ipcMain, 'web-sql-response'); + await w.webContents.executeJavaScript(` + new Promise((resolve, reject) => { + const webview = new WebView(); + webview.setAttribute('src', '${origin}'); + webview.setAttribute('webpreferences', 'enableWebSQL=1'); + webview.setAttribute('partition', '${sqlPartition}'); + webview.setAttribute('nodeIntegration', 'on'); + document.body.appendChild(webview); + webview.addEventListener('dom-ready', () => resolve()); + }); + `); + const [, childError] = await webviewResult; + expect(childError).to.be.null(); + }); + }); + }); ifdescribe(features.isPDFViewerEnabled())('PDF Viewer', () => { const pdfSource = url.format({ @@ -1073,4 +1362,82 @@ describe('font fallback', () => { else if (process.platform === 'darwin') expect(fonts[0].familyName).to.equal('Hiragino Kaku Gothic ProN') }) -}) \ No newline at end of file +}) + +describe('iframe using HTML fullscreen API while window is OS-fullscreened', () => { + const fullscreenChildHtml = promisify(fs.readFile)( + path.join(fixturesPath, 'pages', 'fullscreen-oopif.html') + ) + let w: BrowserWindow, server: http.Server + + before(() => { + server = http.createServer(async (_req, res) => { + res.writeHead(200, { 'Content-Type': 'text/html' }) + res.write(await fullscreenChildHtml) + res.end() + }) + + server.listen(8989, '127.0.0.1') + }) + + beforeEach(() => { + w = new BrowserWindow({ + show: true, + fullscreen: true, + webPreferences: { + nodeIntegration: true, + nodeIntegrationInSubFrames: true + } + }) + }) + + afterEach(async () => { + await closeAllWindows() + ;(w as any) = null + server.close() + }) + + it('can fullscreen from out-of-process iframes (OOPIFs)', done => { + ipcMain.once('fullscreenChange', async () => { + const fullscreenWidth = await w.webContents.executeJavaScript( + "document.querySelector('iframe').offsetWidth" + ) + expect(fullscreenWidth > 0).to.be.true() + + await w.webContents.executeJavaScript( + "document.querySelector('iframe').contentWindow.postMessage('exitFullscreen', '*')" + ) + + await new Promise(resolve => setTimeout(resolve, 500)) + + const width = await w.webContents.executeJavaScript( + "document.querySelector('iframe').offsetWidth" + ) + expect(width).to.equal(0) + + done() + }) + + const html = + '' + w.loadURL(`data:text/html,${html}`) + }) + + it('can fullscreen from in-process iframes', done => { + ipcMain.once('fullscreenChange', async () => { + const fullscreenWidth = await w.webContents.executeJavaScript( + "document.querySelector('iframe').offsetWidth" + ) + expect(fullscreenWidth > 0).to.true() + + await w.webContents.executeJavaScript('document.exitFullscreen()') + const width = await w.webContents.executeJavaScript( + "document.querySelector('iframe').offsetWidth" + ) + expect(width).to.equal(0) + done() + }) + + w.loadFile(path.join(fixturesPath, 'pages', 'fullscreen-ipif.html')) + }) +}) diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index b799d3199dc96..ca9979a1164b4 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -33,12 +33,12 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex // extension in an in-memory session results in it being installed in the // default session. const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); - (customSession as any).loadChromeExtension(path.join(fixtures, 'extensions', 'red-bg')) + (customSession as any).loadChromeExtension(path.join(fixtures, 'extensions', 'red-bg')); const w = new BrowserWindow({show: false, webPreferences: {session: customSession}}) - await w.loadURL(url) - const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') - expect(bg).to.equal('red') - }) + await w.loadURL(url); + const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor'); + expect(bg).to.equal('red'); + }); it('confines an extension to the session it was loaded in', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); @@ -109,6 +109,20 @@ ifdescribe(!process.electronBinding('features').isExtensionsEnabled())('chrome e afterEach(() => closeWindow(w).then(() => { w = null as unknown as BrowserWindow })) + it('chrome.runtime.connect parses arguments properly', async function () { + await w.loadURL('about:blank') + + const promise = emittedOnce(w.webContents, 'console-message') + + const message = { method: 'connect' } + w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`) + + const [,, responseString] = await promise + const response = JSON.parse(responseString) + + expect(response).to.be.true() + }) + it('runtime.getManifest returns extension manifest', async () => { const actualManifest = (() => { const data = fs.readFileSync(path.join(fixtures, 'extensions/chrome-api/manifest.json'), 'utf-8') diff --git a/spec-main/fixtures/api/leak-exit-webcontentsview.js b/spec-main/fixtures/api/leak-exit-webcontentsview.js index 1196cfcca839b..cec51aeef2764 100644 --- a/spec-main/fixtures/api/leak-exit-webcontentsview.js +++ b/spec-main/fixtures/api/leak-exit-webcontentsview.js @@ -3,5 +3,5 @@ app.on('ready', function () { const web = webContents.create({}) new WebContentsView(web) // eslint-disable-line - process.nextTick(() => app.quit()) + app.quit() }) diff --git a/spec-main/fixtures/api/test-menu-null/main.js b/spec-main/fixtures/api/test-menu-null/main.js new file mode 100644 index 0000000000000..c7509e7343a15 --- /dev/null +++ b/spec-main/fixtures/api/test-menu-null/main.js @@ -0,0 +1,16 @@ +const { app, BrowserWindow } = require('electron') + +let win +app.on('ready', function () { + win = new BrowserWindow({}) + win.setMenu(null) + + setTimeout(() => { + if (win.isMenuBarVisible()) { + console.log('Window has a menu') + } else { + console.log('Window has no menu') + } + app.quit() + }) +}) diff --git a/spec-main/fixtures/api/test-menu-null/package.json b/spec-main/fixtures/api/test-menu-null/package.json new file mode 100644 index 0000000000000..bfb7944df5c83 --- /dev/null +++ b/spec-main/fixtures/api/test-menu-null/package.json @@ -0,0 +1,4 @@ +{ + "name": "electron-test-menu", + "main": "main.js" +} diff --git a/spec-main/fixtures/api/test-menu-visibility/main.js b/spec-main/fixtures/api/test-menu-visibility/main.js new file mode 100644 index 0000000000000..c8def24201359 --- /dev/null +++ b/spec-main/fixtures/api/test-menu-visibility/main.js @@ -0,0 +1,16 @@ +const { app, BrowserWindow } = require('electron') + +let win +app.on('ready', function () { + win = new BrowserWindow({}) + win.setMenuBarVisibility(false) + + setTimeout(() => { + if (win.isMenuBarVisible()) { + console.log('Window has a menu') + } else { + console.log('Window has no menu') + } + app.quit() + }) +}) diff --git a/spec-main/fixtures/api/test-menu-visibility/package.json b/spec-main/fixtures/api/test-menu-visibility/package.json new file mode 100644 index 0000000000000..bfb7944df5c83 --- /dev/null +++ b/spec-main/fixtures/api/test-menu-visibility/package.json @@ -0,0 +1,4 @@ +{ + "name": "electron-test-menu", + "main": "main.js" +} diff --git a/spec-main/fixtures/api/webrequest.html b/spec-main/fixtures/api/webrequest.html new file mode 100644 index 0000000000000..6cc9f0bd18908 --- /dev/null +++ b/spec-main/fixtures/api/webrequest.html @@ -0,0 +1,27 @@ + diff --git a/spec-main/index.js b/spec-main/index.js index dafa8db3fc5a4..9f2cfd9725f9d 100644 --- a/spec-main/index.js +++ b/spec-main/index.js @@ -20,9 +20,6 @@ v8.setFlagsFromString('--expose_gc') app.commandLine.appendSwitch('js-flags', '--expose_gc') // Prevent the spec runner quiting when the first window closes app.on('window-all-closed', () => null) -// TODO: This API should _probably_ only be enabled for the specific test that needs it -// not the entire test suite -app.commandLine.appendSwitch('ignore-certificate-errors') // Use fake device for Media Stream to replace actual camera and microphone. app.commandLine.appendSwitch('use-fake-device-for-media-stream') diff --git a/spec-main/package.json b/spec-main/package.json index eb5a7e949a10d..bb272f81db0d0 100644 --- a/spec-main/package.json +++ b/spec-main/package.json @@ -4,7 +4,9 @@ "main": "index.js", "version": "0.1.0", "devDependencies": { + "@types/ws": "^7.2.0", "echo": "file:fixtures/native-addon/echo", - "q": "^1.5.1" + "q": "^1.5.1", + "ws": "^7.2.1" } } diff --git a/spec-main/yarn.lock b/spec-main/yarn.lock index 79ec47695e196..a40f5e0e8487b 100644 --- a/spec-main/yarn.lock +++ b/spec-main/yarn.lock @@ -2,6 +2,18 @@ # yarn lockfile v1 +"@types/node@*": + version "13.7.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.0.tgz#b417deda18cf8400f278733499ad5547ed1abec4" + integrity sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ== + +"@types/ws@^7.2.0": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.2.1.tgz#b800f2b8aee694e2b581113643e20d79dd3b8556" + integrity sha512-UEmRNbXFGvfs/sLncf01GuVv6U1mZP3Df0iXWx4kUlikJxbFyFADp95mDn1XDTE2mXpzzoHcKlfFcbytLq4vaA== + dependencies: + "@types/node" "*" + "echo@file:fixtures/native-addon/echo": version "0.0.1" @@ -9,3 +21,8 @@ q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +ws@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e" + integrity sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A== diff --git a/spec/api-clipboard-spec.js b/spec/api-clipboard-spec.js index 5267f3f0a0bcc..ea27336fb4bae 100644 --- a/spec/api-clipboard-spec.js +++ b/spec/api-clipboard-spec.js @@ -1,139 +1,146 @@ -const { expect } = require('chai') -const path = require('path') -const { Buffer } = require('buffer') +const { expect } = require('chai'); +const path = require('path'); +const { Buffer } = require('buffer'); -const { clipboard, nativeImage } = require('electron') +const { clipboard, nativeImage } = require('electron'); describe('clipboard module', () => { - const fixtures = path.resolve(__dirname, 'fixtures') + const fixtures = path.resolve(__dirname, 'fixtures'); + + // FIXME(zcbenz): Clipboard tests are failing on WOA. + beforeEach(function () { + if (process.platform === 'win32' && process.arch === 'arm64') { + this.skip(); + } + }); describe('clipboard.readImage()', () => { it('returns NativeImage instance', () => { - const p = path.join(fixtures, 'assets', 'logo.png') - const i = nativeImage.createFromPath(p) - clipboard.writeImage(p) - expect(clipboard.readImage().toDataURL()).to.equal(i.toDataURL()) - }) - }) + const p = path.join(fixtures, 'assets', 'logo.png'); + const i = nativeImage.createFromPath(p); + clipboard.writeImage(p); + expect(clipboard.readImage().toDataURL()).to.equal(i.toDataURL()); + }); + }); describe('clipboard.readText()', () => { it('returns unicode string correctly', () => { - const text = '千江有水千江月,万里无云万里天' - clipboard.writeText(text) - expect(clipboard.readText()).to.equal(text) - }) - }) + const text = '千江有水千江月,万里无云万里天'; + clipboard.writeText(text); + expect(clipboard.readText()).to.equal(text); + }); + }); describe('clipboard.readHTML()', () => { it('returns markup correctly', () => { - const text = 'Hi' - const markup = process.platform === 'darwin' ? "Hi" : process.platform === 'linux' ? 'Hi' : 'Hi' - clipboard.writeHTML(text) - expect(clipboard.readHTML()).to.equal(markup) - }) - }) + const text = 'Hi'; + const markup = process.platform === 'darwin' ? "Hi" : process.platform === 'linux' ? 'Hi' : 'Hi'; + clipboard.writeHTML(text); + expect(clipboard.readHTML()).to.equal(markup); + }); + }); describe('clipboard.readRTF', () => { it('returns rtf text correctly', () => { - const rtf = '{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\nThis is some {\\b bold} text.\\par\n}' - clipboard.writeRTF(rtf) - expect(clipboard.readRTF()).to.equal(rtf) - }) - }) + const rtf = '{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\f0\\pard\nThis is some {\\b bold} text.\\par\n}'; + clipboard.writeRTF(rtf); + expect(clipboard.readRTF()).to.equal(rtf); + }); + }); describe('clipboard.readBookmark', () => { before(function () { if (process.platform === 'linux') { - this.skip() + this.skip(); } - }) + }); it('returns title and url', () => { - clipboard.writeBookmark('a title', 'https://electronjs.org') + clipboard.writeBookmark('a title', 'https://electronjs.org'); expect(clipboard.readBookmark()).to.deep.equal({ title: 'a title', url: 'https://electronjs.org' - }) + }); - clipboard.writeText('no bookmark') + clipboard.writeText('no bookmark'); expect(clipboard.readBookmark()).to.deep.equal({ title: '', url: '' - }) - }) - }) + }); + }); + }); describe('clipboard.write()', () => { it('returns data correctly', () => { - const text = 'test' - const rtf = '{\\rtf1\\utf8 text}' - const p = path.join(fixtures, 'assets', 'logo.png') - const i = nativeImage.createFromPath(p) - const markup = process.platform === 'darwin' ? "Hi" : process.platform === 'linux' ? 'Hi' : 'Hi' - const bookmark = { title: 'a title', url: 'test' } + const text = 'test'; + const rtf = '{\\rtf1\\utf8 text}'; + const p = path.join(fixtures, 'assets', 'logo.png'); + const i = nativeImage.createFromPath(p); + const markup = process.platform === 'darwin' ? "Hi" : process.platform === 'linux' ? 'Hi' : 'Hi'; + const bookmark = { title: 'a title', url: 'test' }; clipboard.write({ text: 'test', html: 'Hi', rtf: '{\\rtf1\\utf8 text}', bookmark: 'a title', image: p - }) + }); - expect(clipboard.readText()).to.equal(text) - expect(clipboard.readHTML()).to.equal(markup) - expect(clipboard.readRTF()).to.equal(rtf) - expect(clipboard.readImage().toDataURL()).to.equal(i.toDataURL()) + expect(clipboard.readText()).to.equal(text); + expect(clipboard.readHTML()).to.equal(markup); + expect(clipboard.readRTF()).to.equal(rtf); + expect(clipboard.readImage().toDataURL()).to.equal(i.toDataURL()); if (process.platform !== 'linux') { - expect(clipboard.readBookmark()).to.deep.equal(bookmark) + expect(clipboard.readBookmark()).to.deep.equal(bookmark); } - }) - }) + }); + }); describe('clipboard.read/writeFindText(text)', () => { before(function () { if (process.platform !== 'darwin') { - this.skip() + this.skip(); } - }) + }); it('reads and write text to the find pasteboard', () => { - clipboard.writeFindText('find this') - expect(clipboard.readFindText()).to.equal('find this') - }) - }) + clipboard.writeFindText('find this'); + expect(clipboard.readFindText()).to.equal('find this'); + }); + }); describe('clipboard.writeBuffer(format, buffer)', () => { it('writes a Buffer for the specified format', function () { if (process.platform !== 'darwin') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const buffer = Buffer.from('writeBuffer', 'utf8') - clipboard.writeBuffer('public.utf8-plain-text', buffer) - expect(clipboard.readText()).to.equal('writeBuffer') - }) + const buffer = Buffer.from('writeBuffer', 'utf8'); + clipboard.writeBuffer('public.utf8-plain-text', buffer); + expect(clipboard.readText()).to.equal('writeBuffer'); + }); it('throws an error when a non-Buffer is specified', () => { expect(() => { - clipboard.writeBuffer('public.utf8-plain-text', 'hello') - }).to.throw(/buffer must be a node Buffer/) - }) - }) + clipboard.writeBuffer('public.utf8-plain-text', 'hello'); + }).to.throw(/buffer must be a node Buffer/); + }); + }); describe('clipboard.readBuffer(format)', () => { before(function () { if (process.platform !== 'darwin') { - this.skip() + this.skip(); } - }) + }); it('returns a Buffer of the content for the specified format', () => { - const buffer = Buffer.from('this is binary', 'utf8') - clipboard.writeText(buffer.toString()) - expect(buffer.equals(clipboard.readBuffer('public.utf8-plain-text'))).to.equal(true) - }) - }) -}) + const buffer = Buffer.from('this is binary', 'utf8'); + clipboard.writeText(buffer.toString()); + expect(buffer.equals(clipboard.readBuffer('public.utf8-plain-text'))).to.equal(true); + }); + }); +}); diff --git a/spec/api-native-image-spec.js b/spec/api-native-image-spec.js index d7729c4a9bb23..b0fbdb6f2083e 100644 --- a/spec/api-native-image-spec.js +++ b/spec/api-native-image-spec.js @@ -1,18 +1,18 @@ -'use strict' +'use strict'; -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const { nativeImage } = require('electron') -const path = require('path') +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const { nativeImage } = require('electron'); +const path = require('path'); -const { expect } = chai -chai.use(dirtyChai) +const { expect } = chai; +chai.use(dirtyChai); describe('nativeImage module', () => { const ImageFormat = { PNG: 'png', JPEG: 'jpeg' - } + }; const images = [ { @@ -50,7 +50,7 @@ describe('nativeImage module', () => { height: 3, width: 3 } - ] + ]; /** * @param {?string} filename @@ -58,8 +58,8 @@ describe('nativeImage module', () => { */ const getImagePathFromFilename = (filename) => { return (filename === null) ? null - : path.join(__dirname, 'fixtures', 'assets', filename) - } + : path.join(__dirname, 'fixtures', 'assets', filename); + }; /** * @param {!Object} image @@ -68,12 +68,12 @@ describe('nativeImage module', () => { */ const imageMatchesTheFilters = (image, filters = null) => { if (filters === null) { - return true + return true; } return Object.entries(filters) - .every(([key, value]) => image[key] === value) - } + .every(([key, value]) => image[key] === value); + }; /** * @param {!Object} filters @@ -81,362 +81,362 @@ describe('nativeImage module', () => { */ const getImages = (filters) => { const matchingImages = images - .filter(i => imageMatchesTheFilters(i, filters)) + .filter(i => imageMatchesTheFilters(i, filters)); // Add `.path` property to every image. matchingImages - .forEach(i => { i.path = getImagePathFromFilename(i.filename) }) + .forEach(i => { i.path = getImagePathFromFilename(i.filename); }); - return matchingImages - } + return matchingImages; + }; /** * @param {!Object} filters * @returns {Object} A matching image if any. */ const getImage = (filters) => { - const matchingImages = getImages(filters) + const matchingImages = getImages(filters); - let matchingImage = null + let matchingImage = null; if (matchingImages.length > 0) { - matchingImage = matchingImages[0] + matchingImage = matchingImages[0]; } - return matchingImage - } + return matchingImage; + }; describe('isMacTemplateImage property', () => { before(function () { - if (process.platform !== 'darwin') this.skip() - }) + if (process.platform !== 'darwin') this.skip(); + }); it('returns whether the image is a template image', () => { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); - expect(image.isMacTemplateImage).to.be.a('boolean') + expect(image.isMacTemplateImage).to.be.a('boolean'); - expect(image.isTemplateImage).to.be.a('function') - expect(image.setTemplateImage).to.be.a('function') - }) + expect(image.isTemplateImage).to.be.a('function'); + expect(image.setTemplateImage).to.be.a('function'); + }); it('correctly recognizes a template image', () => { - const templateImage = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo_Template.png')) - expect(templateImage.isMacTemplateImage).to.be.true() - }) + const templateImage = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo_Template.png')); + expect(templateImage.isMacTemplateImage).to.be.true(); + }); it('sets a template image', function () { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - expect(image.isMacTemplateImage).to.be.false() + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + expect(image.isMacTemplateImage).to.be.false(); - image.isMacTemplateImage = true - expect(image.isMacTemplateImage).to.be.true() - }) - }) + image.isMacTemplateImage = true; + expect(image.isMacTemplateImage).to.be.true(); + }); + }); describe('createEmpty()', () => { it('returns an empty image', () => { - const empty = nativeImage.createEmpty() - expect(empty.isEmpty()).to.be.true() - expect(empty.getAspectRatio()).to.equal(1) - expect(empty.toDataURL()).to.equal('data:image/png;base64,') - expect(empty.toDataURL({ scaleFactor: 2.0 })).to.equal('data:image/png;base64,') - expect(empty.getSize()).to.deep.equal({ width: 0, height: 0 }) - expect(empty.getBitmap()).to.be.empty() - expect(empty.getBitmap({ scaleFactor: 2.0 })).to.be.empty() - expect(empty.toBitmap()).to.be.empty() - expect(empty.toBitmap({ scaleFactor: 2.0 })).to.be.empty() - expect(empty.toJPEG(100)).to.be.empty() - expect(empty.toPNG()).to.be.empty() - expect(empty.toPNG({ scaleFactor: 2.0 })).to.be.empty() + const empty = nativeImage.createEmpty(); + expect(empty.isEmpty()).to.be.true(); + expect(empty.getAspectRatio()).to.equal(1); + expect(empty.toDataURL()).to.equal('data:image/png;base64,'); + expect(empty.toDataURL({ scaleFactor: 2.0 })).to.equal('data:image/png;base64,'); + expect(empty.getSize()).to.deep.equal({ width: 0, height: 0 }); + expect(empty.getBitmap()).to.be.empty(); + expect(empty.getBitmap({ scaleFactor: 2.0 })).to.be.empty(); + expect(empty.toBitmap()).to.be.empty(); + expect(empty.toBitmap({ scaleFactor: 2.0 })).to.be.empty(); + expect(empty.toJPEG(100)).to.be.empty(); + expect(empty.toPNG()).to.be.empty(); + expect(empty.toPNG({ scaleFactor: 2.0 })).to.be.empty(); if (process.platform === 'darwin') { - expect(empty.getNativeHandle()).to.be.empty() + expect(empty.getNativeHandle()).to.be.empty(); } - }) - }) + }); + }); describe('createFromBitmap(buffer, options)', () => { it('returns an empty image when the buffer is empty', () => { - expect(nativeImage.createFromBitmap(Buffer.from([]), { width: 0, height: 0 }).isEmpty()).to.be.true() - }) + expect(nativeImage.createFromBitmap(Buffer.from([]), { width: 0, height: 0 }).isEmpty()).to.be.true(); + }); it('returns an image created from the given buffer', () => { - const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) + const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); - const imageB = nativeImage.createFromBitmap(imageA.toBitmap(), imageA.getSize()) - expect(imageB.getSize()).to.deep.equal({ width: 538, height: 190 }) + const imageB = nativeImage.createFromBitmap(imageA.toBitmap(), imageA.getSize()); + expect(imageB.getSize()).to.deep.equal({ width: 538, height: 190 }); - const imageC = nativeImage.createFromBuffer(imageA.toBitmap(), { ...imageA.getSize(), scaleFactor: 2.0 }) - expect(imageC.getSize()).to.deep.equal({ width: 269, height: 95 }) - }) + const imageC = nativeImage.createFromBuffer(imageA.toBitmap(), { ...imageA.getSize(), scaleFactor: 2.0 }); + expect(imageC.getSize()).to.deep.equal({ width: 269, height: 95 }); + }); it('throws on invalid arguments', () => { - expect(() => nativeImage.createFromBitmap(null, {})).to.throw('buffer must be a node Buffer') - expect(() => nativeImage.createFromBitmap([12, 14, 124, 12], {})).to.throw('buffer must be a node Buffer') - expect(() => nativeImage.createFromBitmap(Buffer.from([]), {})).to.throw('width is required') - expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1 })).to.throw('height is required') - expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1, height: 1 })).to.throw('invalid buffer size') - }) - }) + expect(() => nativeImage.createFromBitmap(null, {})).to.throw('buffer must be a node Buffer'); + expect(() => nativeImage.createFromBitmap([12, 14, 124, 12], {})).to.throw('buffer must be a node Buffer'); + expect(() => nativeImage.createFromBitmap(Buffer.from([]), {})).to.throw('width is required'); + expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1 })).to.throw('height is required'); + expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1, height: 1 })).to.throw('invalid buffer size'); + }); + }); describe('createFromBuffer(buffer, options)', () => { it('returns an empty image when the buffer is empty', () => { - expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty()).to.be.true() - }) + expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty()).to.be.true(); + }); it('returns an empty image when the buffer is too small', () => { - const image = nativeImage.createFromBuffer(Buffer.from([1, 2, 3, 4]), { width: 100, height: 100 }) - expect(image.isEmpty()).to.be.true() - }) + const image = nativeImage.createFromBuffer(Buffer.from([1, 2, 3, 4]), { width: 100, height: 100 }); + expect(image.isEmpty()).to.be.true(); + }); it('returns an image created from the given buffer', () => { - const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) + const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); - const imageB = nativeImage.createFromBuffer(imageA.toPNG()) - expect(imageB.getSize()).to.deep.equal({ width: 538, height: 190 }) - expect(imageA.toBitmap().equals(imageB.toBitmap())).to.be.true() + const imageB = nativeImage.createFromBuffer(imageA.toPNG()); + expect(imageB.getSize()).to.deep.equal({ width: 538, height: 190 }); + expect(imageA.toBitmap().equals(imageB.toBitmap())).to.be.true(); - const imageC = nativeImage.createFromBuffer(imageA.toJPEG(100)) - expect(imageC.getSize()).to.deep.equal({ width: 538, height: 190 }) + const imageC = nativeImage.createFromBuffer(imageA.toJPEG(100)); + expect(imageC.getSize()).to.deep.equal({ width: 538, height: 190 }); const imageD = nativeImage.createFromBuffer(imageA.toBitmap(), - { width: 538, height: 190 }) - expect(imageD.getSize()).to.deep.equal({ width: 538, height: 190 }) + { width: 538, height: 190 }); + expect(imageD.getSize()).to.deep.equal({ width: 538, height: 190 }); const imageE = nativeImage.createFromBuffer(imageA.toBitmap(), - { width: 100, height: 200 }) - expect(imageE.getSize()).to.deep.equal({ width: 100, height: 200 }) + { width: 100, height: 200 }); + expect(imageE.getSize()).to.deep.equal({ width: 100, height: 200 }); - const imageF = nativeImage.createFromBuffer(imageA.toBitmap()) - expect(imageF.isEmpty()).to.be.true() + const imageF = nativeImage.createFromBuffer(imageA.toBitmap()); + expect(imageF.isEmpty()).to.be.true(); const imageG = nativeImage.createFromBuffer(imageA.toPNG(), - { width: 100, height: 200 }) - expect(imageG.getSize()).to.deep.equal({ width: 538, height: 190 }) + { width: 100, height: 200 }); + expect(imageG.getSize()).to.deep.equal({ width: 538, height: 190 }); const imageH = nativeImage.createFromBuffer(imageA.toJPEG(100), - { width: 100, height: 200 }) - expect(imageH.getSize()).to.deep.equal({ width: 538, height: 190 }) + { width: 100, height: 200 }); + expect(imageH.getSize()).to.deep.equal({ width: 538, height: 190 }); const imageI = nativeImage.createFromBuffer(imageA.toBitmap(), - { width: 538, height: 190, scaleFactor: 2.0 }) - expect(imageI.getSize()).to.deep.equal({ width: 269, height: 95 }) - }) + { width: 538, height: 190, scaleFactor: 2.0 }); + expect(imageI.getSize()).to.deep.equal({ width: 269, height: 95 }); + }); it('throws on invalid arguments', () => { - expect(() => nativeImage.createFromBuffer(null)).to.throw('buffer must be a node Buffer') - expect(() => nativeImage.createFromBuffer([12, 14, 124, 12])).to.throw('buffer must be a node Buffer') - }) - }) + expect(() => nativeImage.createFromBuffer(null)).to.throw('buffer must be a node Buffer'); + expect(() => nativeImage.createFromBuffer([12, 14, 124, 12])).to.throw('buffer must be a node Buffer'); + }); + }); describe('createFromDataURL(dataURL)', () => { it('returns an empty image from the empty string', () => { - expect(nativeImage.createFromDataURL('').isEmpty()).to.be.true() - }) + expect(nativeImage.createFromDataURL('').isEmpty()).to.be.true(); + }); it('returns an image created from the given string', () => { - const imagesData = getImages({ hasDataUrl: true }) + const imagesData = getImages({ hasDataUrl: true }); for (const imageData of imagesData) { - const imageFromPath = nativeImage.createFromPath(imageData.path) - const imageFromDataUrl = nativeImage.createFromDataURL(imageData.dataUrl) + const imageFromPath = nativeImage.createFromPath(imageData.path); + const imageFromDataUrl = nativeImage.createFromDataURL(imageData.dataUrl); - expect(imageFromDataUrl.isEmpty()).to.be.false() - expect(imageFromDataUrl.getSize()).to.deep.equal(imageFromPath.getSize()) + expect(imageFromDataUrl.isEmpty()).to.be.false(); + expect(imageFromDataUrl.getSize()).to.deep.equal(imageFromPath.getSize()); expect(imageFromDataUrl.toBitmap()).to.satisfy( - bitmap => imageFromPath.toBitmap().equals(bitmap)) - expect(imageFromDataUrl.toDataURL()).to.equal(imageFromPath.toDataURL()) + bitmap => imageFromPath.toBitmap().equals(bitmap)); + expect(imageFromDataUrl.toDataURL()).to.equal(imageFromPath.toDataURL()); } - }) - }) + }); + }); describe('toDataURL()', () => { it('returns a PNG data URL', () => { - const imagesData = getImages({ hasDataUrl: true }) + const imagesData = getImages({ hasDataUrl: true }); for (const imageData of imagesData) { - const imageFromPath = nativeImage.createFromPath(imageData.path) + const imageFromPath = nativeImage.createFromPath(imageData.path); - const scaleFactors = [1.0, 2.0] + const scaleFactors = [1.0, 2.0]; for (const scaleFactor of scaleFactors) { - expect(imageFromPath.toDataURL({ scaleFactor })).to.equal(imageData.dataUrl) + expect(imageFromPath.toDataURL({ scaleFactor })).to.equal(imageData.dataUrl); } } - }) + }); it('returns a data URL at 1x scale factor by default', () => { - const imageData = getImage({ filename: 'logo.png' }) - const image = nativeImage.createFromPath(imageData.path) + const imageData = getImage({ filename: 'logo.png' }); + const image = nativeImage.createFromPath(imageData.path); const imageOne = nativeImage.createFromBuffer(image.toPNG(), { width: image.getSize().width, height: image.getSize().height, scaleFactor: 2.0 - }) + }); expect(imageOne.getSize()).to.deep.equal( - { width: imageData.width / 2, height: imageData.height / 2 }) + { width: imageData.width / 2, height: imageData.height / 2 }); - const imageTwo = nativeImage.createFromDataURL(imageOne.toDataURL()) + const imageTwo = nativeImage.createFromDataURL(imageOne.toDataURL()); expect(imageTwo.getSize()).to.deep.equal( - { width: imageData.width, height: imageData.height }) + { width: imageData.width, height: imageData.height }); - expect(imageOne.toBitmap().equals(imageTwo.toBitmap())).to.be.true() - }) + expect(imageOne.toBitmap().equals(imageTwo.toBitmap())).to.be.true(); + }); it('supports a scale factor', () => { - const imageData = getImage({ filename: 'logo.png' }) - const image = nativeImage.createFromPath(imageData.path) - const expectedSize = { width: imageData.width, height: imageData.height } + const imageData = getImage({ filename: 'logo.png' }); + const image = nativeImage.createFromPath(imageData.path); + const expectedSize = { width: imageData.width, height: imageData.height }; const imageFromDataUrlOne = nativeImage.createFromDataURL( - image.toDataURL({ scaleFactor: 1.0 })) - expect(imageFromDataUrlOne.getSize()).to.deep.equal(expectedSize) + image.toDataURL({ scaleFactor: 1.0 })); + expect(imageFromDataUrlOne.getSize()).to.deep.equal(expectedSize); const imageFromDataUrlTwo = nativeImage.createFromDataURL( - image.toDataURL({ scaleFactor: 2.0 })) - expect(imageFromDataUrlTwo.getSize()).to.deep.equal(expectedSize) - }) - }) + image.toDataURL({ scaleFactor: 2.0 })); + expect(imageFromDataUrlTwo.getSize()).to.deep.equal(expectedSize); + }); + }); describe('toPNG()', () => { it('returns a buffer at 1x scale factor by default', () => { - const imageData = getImage({ filename: 'logo.png' }) - const imageA = nativeImage.createFromPath(imageData.path) + const imageData = getImage({ filename: 'logo.png' }); + const imageA = nativeImage.createFromPath(imageData.path); const imageB = nativeImage.createFromBuffer(imageA.toPNG(), { width: imageA.getSize().width, height: imageA.getSize().height, scaleFactor: 2.0 - }) + }); expect(imageB.getSize()).to.deep.equal( - { width: imageData.width / 2, height: imageData.height / 2 }) + { width: imageData.width / 2, height: imageData.height / 2 }); - const imageC = nativeImage.createFromBuffer(imageB.toPNG()) + const imageC = nativeImage.createFromBuffer(imageB.toPNG()); expect(imageC.getSize()).to.deep.equal( - { width: imageData.width, height: imageData.height }) + { width: imageData.width, height: imageData.height }); - expect(imageB.toBitmap().equals(imageC.toBitmap())).to.be.true() - }) + expect(imageB.toBitmap().equals(imageC.toBitmap())).to.be.true(); + }); it('supports a scale factor', () => { - const imageData = getImage({ filename: 'logo.png' }) - const image = nativeImage.createFromPath(imageData.path) + const imageData = getImage({ filename: 'logo.png' }); + const image = nativeImage.createFromPath(imageData.path); const imageFromBufferOne = nativeImage.createFromBuffer( - image.toPNG({ scaleFactor: 1.0 })) + image.toPNG({ scaleFactor: 1.0 })); expect(imageFromBufferOne.getSize()).to.deep.equal( - { width: imageData.width, height: imageData.height }) + { width: imageData.width, height: imageData.height }); const imageFromBufferTwo = nativeImage.createFromBuffer( - image.toPNG({ scaleFactor: 2.0 }), { scaleFactor: 2.0 }) + image.toPNG({ scaleFactor: 2.0 }), { scaleFactor: 2.0 }); expect(imageFromBufferTwo.getSize()).to.deep.equal( - { width: imageData.width / 2, height: imageData.height / 2 }) - }) - }) + { width: imageData.width / 2, height: imageData.height / 2 }); + }); + }); describe('createFromPath(path)', () => { it('returns an empty image for invalid paths', () => { - expect(nativeImage.createFromPath('').isEmpty()).to.be.true() - expect(nativeImage.createFromPath('does-not-exist.png').isEmpty()).to.be.true() - expect(nativeImage.createFromPath('does-not-exist.ico').isEmpty()).to.be.true() - expect(nativeImage.createFromPath(__dirname).isEmpty()).to.be.true() - expect(nativeImage.createFromPath(__filename).isEmpty()).to.be.true() - }) + expect(nativeImage.createFromPath('').isEmpty()).to.be.true(); + expect(nativeImage.createFromPath('does-not-exist.png').isEmpty()).to.be.true(); + expect(nativeImage.createFromPath('does-not-exist.ico').isEmpty()).to.be.true(); + expect(nativeImage.createFromPath(__dirname).isEmpty()).to.be.true(); + expect(nativeImage.createFromPath(__filename).isEmpty()).to.be.true(); + }); it('loads images from paths relative to the current working directory', () => { - const imagePath = path.relative('.', path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - const image = nativeImage.createFromPath(imagePath) - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }) - }) + const imagePath = path.relative('.', path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + const image = nativeImage.createFromPath(imagePath); + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }); + }); it('loads images from paths with `.` segments', () => { - const imagePath = `${path.join(__dirname, 'fixtures')}${path.sep}.${path.sep}${path.join('assets', 'logo.png')}` - const image = nativeImage.createFromPath(imagePath) - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }) - }) + const imagePath = `${path.join(__dirname, 'fixtures')}${path.sep}.${path.sep}${path.join('assets', 'logo.png')}`; + const image = nativeImage.createFromPath(imagePath); + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }); + }); it('loads images from paths with `..` segments', () => { - const imagePath = `${path.join(__dirname, 'fixtures', 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}` - const image = nativeImage.createFromPath(imagePath) - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }) - }) + const imagePath = `${path.join(__dirname, 'fixtures', 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}`; + const image = nativeImage.createFromPath(imagePath); + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 538, height: 190 }); + }); it('Gets an NSImage pointer on macOS', function () { if (process.platform !== 'darwin') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const imagePath = `${path.join(__dirname, 'fixtures', 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}` - const image = nativeImage.createFromPath(imagePath) - const nsimage = image.getNativeHandle() + const imagePath = `${path.join(__dirname, 'fixtures', 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}`; + const image = nativeImage.createFromPath(imagePath); + const nsimage = image.getNativeHandle(); - expect(nsimage).to.have.lengthOf(8) + expect(nsimage).to.have.lengthOf(8); // If all bytes are null, that's Bad - const allBytesAreNotNull = nsimage.reduce((acc, x) => acc || (x !== 0), false) - expect(allBytesAreNotNull) - }) + const allBytesAreNotNull = nsimage.reduce((acc, x) => acc || (x !== 0), false); + expect(allBytesAreNotNull); + }); it('loads images from .ico files on Windows', function () { if (process.platform !== 'win32') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const imagePath = path.join(__dirname, 'fixtures', 'assets', 'icon.ico') - const image = nativeImage.createFromPath(imagePath) - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 256, height: 256 }) - }) - }) + const imagePath = path.join(__dirname, 'fixtures', 'assets', 'icon.ico'); + const image = nativeImage.createFromPath(imagePath); + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 256, height: 256 }); + }); + }); describe('createFromNamedImage(name)', () => { it('returns empty for invalid options', () => { - const image = nativeImage.createFromNamedImage('totally_not_real') - expect(image.isEmpty()).to.be.true() - }) + const image = nativeImage.createFromNamedImage('totally_not_real'); + expect(image.isEmpty()).to.be.true(); + }); it('returns empty on non-darwin platforms', function () { if (process.platform === 'darwin') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const image = nativeImage.createFromNamedImage('NSActionTemplate') - expect(image.isEmpty()).to.be.true() - }) + const image = nativeImage.createFromNamedImage('NSActionTemplate'); + expect(image.isEmpty()).to.be.true(); + }); it('returns a valid image on darwin', function () { if (process.platform !== 'darwin') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const image = nativeImage.createFromNamedImage('NSActionTemplate') - expect(image.isEmpty()).to.be.false() - }) + const image = nativeImage.createFromNamedImage('NSActionTemplate'); + expect(image.isEmpty()).to.be.false(); + }); it('returns allows an HSL shift for a valid image on darwin', function () { if (process.platform !== 'darwin') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const image = nativeImage.createFromNamedImage('NSActionTemplate', [0.5, 0.2, 0.8]) - expect(image.isEmpty()).to.be.false() - }) - }) + const image = nativeImage.createFromNamedImage('NSActionTemplate', [0.5, 0.2, 0.8]); + expect(image.isEmpty()).to.be.false(); + }); + }); describe('resize(options)', () => { it('returns a resized image', () => { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); for (const [resizeTo, expectedSize] of new Map([ [{}, { width: 538, height: 190 }], [{ width: 269 }, { width: 269, height: 95 }], @@ -448,167 +448,167 @@ describe('nativeImage module', () => { [{ width: 0, height: 0 }, { width: 0, height: 0 }], [{ width: -1, height: -1 }, { width: 0, height: 0 }] ])) { - const actualSize = image.resize(resizeTo).getSize() - expect(actualSize).to.deep.equal(expectedSize) + const actualSize = image.resize(resizeTo).getSize(); + expect(actualSize).to.deep.equal(expectedSize); } - }) + }); it('returns an empty image when called on an empty image', () => { - expect(nativeImage.createEmpty().resize({ width: 1, height: 1 }).isEmpty()).to.be.true() - expect(nativeImage.createEmpty().resize({ width: 0, height: 0 }).isEmpty()).to.be.true() - }) + expect(nativeImage.createEmpty().resize({ width: 1, height: 1 }).isEmpty()).to.be.true(); + expect(nativeImage.createEmpty().resize({ width: 0, height: 0 }).isEmpty()).to.be.true(); + }); it('supports a quality option', () => { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - const good = image.resize({ width: 100, height: 100, quality: 'good' }) - const better = image.resize({ width: 100, height: 100, quality: 'better' }) - const best = image.resize({ width: 100, height: 100, quality: 'best' }) + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + const good = image.resize({ width: 100, height: 100, quality: 'good' }); + const better = image.resize({ width: 100, height: 100, quality: 'better' }); + const best = image.resize({ width: 100, height: 100, quality: 'best' }); - expect(good.toPNG()).to.have.lengthOf.at.most(better.toPNG().length) - expect(better.toPNG()).to.have.lengthOf.below(best.toPNG().length) - }) - }) + expect(good.toPNG()).to.have.lengthOf.at.most(better.toPNG().length); + expect(better.toPNG()).to.have.lengthOf.below(best.toPNG().length); + }); + }); describe('crop(bounds)', () => { it('returns an empty image when called on an empty image', () => { - expect(nativeImage.createEmpty().crop({ width: 1, height: 2, x: 0, y: 0 }).isEmpty()).to.be.true() - expect(nativeImage.createEmpty().crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true() - }) + expect(nativeImage.createEmpty().crop({ width: 1, height: 2, x: 0, y: 0 }).isEmpty()).to.be.true(); + expect(nativeImage.createEmpty().crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true(); + }); it('returns an empty image when the bounds are invalid', () => { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - expect(image.crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true() - expect(image.crop({ width: -1, height: 10, x: 0, y: 0 }).isEmpty()).to.be.true() - expect(image.crop({ width: 10, height: -35, x: 0, y: 0 }).isEmpty()).to.be.true() - expect(image.crop({ width: 100, height: 100, x: 1000, y: 1000 }).isEmpty()).to.be.true() - }) + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + expect(image.crop({ width: 0, height: 0, x: 0, y: 0 }).isEmpty()).to.be.true(); + expect(image.crop({ width: -1, height: 10, x: 0, y: 0 }).isEmpty()).to.be.true(); + expect(image.crop({ width: 10, height: -35, x: 0, y: 0 }).isEmpty()).to.be.true(); + expect(image.crop({ width: 100, height: 100, x: 1000, y: 1000 }).isEmpty()).to.be.true(); + }); it('returns a cropped image', () => { - const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')) - const cropA = image.crop({ width: 25, height: 64, x: 0, y: 0 }) - const cropB = image.crop({ width: 25, height: 64, x: 30, y: 40 }) - expect(cropA.getSize()).to.deep.equal({ width: 25, height: 64 }) - expect(cropB.getSize()).to.deep.equal({ width: 25, height: 64 }) - expect(cropA.toPNG().equals(cropB.toPNG())).to.be.false() - }) - }) + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')); + const cropA = image.crop({ width: 25, height: 64, x: 0, y: 0 }); + const cropB = image.crop({ width: 25, height: 64, x: 30, y: 40 }); + expect(cropA.getSize()).to.deep.equal({ width: 25, height: 64 }); + expect(cropB.getSize()).to.deep.equal({ width: 25, height: 64 }); + expect(cropA.toPNG().equals(cropB.toPNG())).to.be.false(); + }); + }); describe('getAspectRatio()', () => { it('returns an aspect ratio of an empty image', () => { - expect(nativeImage.createEmpty().getAspectRatio()).to.equal(1.0) - }) + expect(nativeImage.createEmpty().getAspectRatio()).to.equal(1.0); + }); it('returns an aspect ratio of an image', () => { - const imageData = getImage({ filename: 'logo.png' }) + const imageData = getImage({ filename: 'logo.png' }); // imageData.width / imageData.height = 2.831578947368421 - const expectedAspectRatio = 2.8315789699554443 + const expectedAspectRatio = 2.8315789699554443; - const image = nativeImage.createFromPath(imageData.path) - expect(image.getAspectRatio()).to.equal(expectedAspectRatio) - }) - }) + const image = nativeImage.createFromPath(imageData.path); + expect(image.getAspectRatio()).to.equal(expectedAspectRatio); + }); + }); describe('addRepresentation()', () => { it('does not add representation when the buffer is too small', () => { - const image = nativeImage.createEmpty() + const image = nativeImage.createEmpty(); image.addRepresentation({ buffer: Buffer.from([1, 2, 3, 4]), width: 100, height: 100 - }) + }); - expect(image.isEmpty()).to.be.true() - }) + expect(image.isEmpty()).to.be.true(); + }); it('supports adding a buffer representation for a scale factor', () => { - const image = nativeImage.createEmpty() + const image = nativeImage.createEmpty(); - const imageDataOne = getImage({ width: 1, height: 1 }) + const imageDataOne = getImage({ width: 1, height: 1 }); image.addRepresentation({ scaleFactor: 1.0, buffer: nativeImage.createFromPath(imageDataOne.path).toPNG() - }) + }); - const imageDataTwo = getImage({ width: 2, height: 2 }) + const imageDataTwo = getImage({ width: 2, height: 2 }); image.addRepresentation({ scaleFactor: 2.0, buffer: nativeImage.createFromPath(imageDataTwo.path).toPNG() - }) + }); - const imageDataThree = getImage({ width: 3, height: 3 }) + const imageDataThree = getImage({ width: 3, height: 3 }); image.addRepresentation({ scaleFactor: 3.0, buffer: nativeImage.createFromPath(imageDataThree.path).toPNG() - }) + }); image.addRepresentation({ scaleFactor: 4.0, buffer: 'invalid' - }) + }); - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 1, height: 1 }) + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 1, height: 1 }); - expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl) - expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl) - expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl) - expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl) - }) + expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl); + expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl); + expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl); + expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl); + }); it('supports adding a data URL representation for a scale factor', () => { - const image = nativeImage.createEmpty() + const image = nativeImage.createEmpty(); - const imageDataOne = getImage({ width: 1, height: 1 }) + const imageDataOne = getImage({ width: 1, height: 1 }); image.addRepresentation({ scaleFactor: 1.0, dataURL: imageDataOne.dataUrl - }) + }); - const imageDataTwo = getImage({ width: 2, height: 2 }) + const imageDataTwo = getImage({ width: 2, height: 2 }); image.addRepresentation({ scaleFactor: 2.0, dataURL: imageDataTwo.dataUrl - }) + }); - const imageDataThree = getImage({ width: 3, height: 3 }) + const imageDataThree = getImage({ width: 3, height: 3 }); image.addRepresentation({ scaleFactor: 3.0, dataURL: imageDataThree.dataUrl - }) + }); image.addRepresentation({ scaleFactor: 4.0, dataURL: 'invalid' - }) + }); - expect(image.isEmpty()).to.be.false() - expect(image.getSize()).to.deep.equal({ width: 1, height: 1 }) + expect(image.isEmpty()).to.be.false(); + expect(image.getSize()).to.deep.equal({ width: 1, height: 1 }); - expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl) - expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl) - expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl) - expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl) - }) + expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl); + expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl); + expect(image.toDataURL({ scaleFactor: 3.0 })).to.equal(imageDataThree.dataUrl); + expect(image.toDataURL({ scaleFactor: 4.0 })).to.equal(imageDataThree.dataUrl); + }); it('supports adding a representation to an existing image', () => { - const imageDataOne = getImage({ width: 1, height: 1 }) - const image = nativeImage.createFromPath(imageDataOne.path) + const imageDataOne = getImage({ width: 1, height: 1 }); + const image = nativeImage.createFromPath(imageDataOne.path); - const imageDataTwo = getImage({ width: 2, height: 2 }) + const imageDataTwo = getImage({ width: 2, height: 2 }); image.addRepresentation({ scaleFactor: 2.0, dataURL: imageDataTwo.dataUrl - }) + }); - const imageDataThree = getImage({ width: 3, height: 3 }) + const imageDataThree = getImage({ width: 3, height: 3 }); image.addRepresentation({ scaleFactor: 2.0, dataURL: imageDataThree.dataUrl - }) + }); - expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl) - expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl) - }) - }) -}) + expect(image.toDataURL({ scaleFactor: 1.0 })).to.equal(imageDataOne.dataUrl); + expect(image.toDataURL({ scaleFactor: 2.0 })).to.equal(imageDataTwo.dataUrl); + }); + }); +}); diff --git a/spec/api-process-spec.js b/spec/api-process-spec.js index a6ae4df692eae..6006fd39c4041 100644 --- a/spec/api-process-spec.js +++ b/spec/api-process-spec.js @@ -1,118 +1,119 @@ -const { ipcRenderer } = require('electron') -const fs = require('fs') -const path = require('path') +const { ipcRenderer } = require('electron'); +const fs = require('fs'); +const path = require('path'); -const { expect } = require('chai') +const { expect } = require('chai'); describe('process module', () => { describe('process.getCreationTime()', () => { it('returns a creation time', () => { - const creationTime = process.getCreationTime() - expect(creationTime).to.be.a('number').and.be.at.least(0) - }) - }) + const creationTime = process.getCreationTime(); + expect(creationTime).to.be.a('number').and.be.at.least(0); + }); + }); describe('process.getCPUUsage()', () => { it('returns a cpu usage object', () => { - const cpuUsage = process.getCPUUsage() - expect(cpuUsage.percentCPUUsage).to.be.a('number') - expect(cpuUsage.idleWakeupsPerSecond).to.be.a('number') - }) - }) + const cpuUsage = process.getCPUUsage(); + expect(cpuUsage.percentCPUUsage).to.be.a('number'); + expect(cpuUsage.idleWakeupsPerSecond).to.be.a('number'); + }); + }); describe('process.getIOCounters()', () => { before(function () { if (process.platform === 'darwin') { - this.skip() + this.skip(); } - }) + }); it('returns an io counters object', () => { - const ioCounters = process.getIOCounters() - expect(ioCounters.readOperationCount).to.be.a('number') - expect(ioCounters.writeOperationCount).to.be.a('number') - expect(ioCounters.otherOperationCount).to.be.a('number') - expect(ioCounters.readTransferCount).to.be.a('number') - expect(ioCounters.writeTransferCount).to.be.a('number') - expect(ioCounters.otherTransferCount).to.be.a('number') - }) - }) + const ioCounters = process.getIOCounters(); + expect(ioCounters.readOperationCount).to.be.a('number'); + expect(ioCounters.writeOperationCount).to.be.a('number'); + expect(ioCounters.otherOperationCount).to.be.a('number'); + expect(ioCounters.readTransferCount).to.be.a('number'); + expect(ioCounters.writeTransferCount).to.be.a('number'); + expect(ioCounters.otherTransferCount).to.be.a('number'); + }); + }); describe('process.getBlinkMemoryInfo()', () => { it('returns blink memory information object', () => { - const heapStats = process.getBlinkMemoryInfo() - expect(heapStats.allocated).to.be.a('number') - expect(heapStats.total).to.be.a('number') - }) - }) + const heapStats = process.getBlinkMemoryInfo(); + expect(heapStats.allocated).to.be.a('number'); + expect(heapStats.total).to.be.a('number'); + }); + }); describe('process.getProcessMemoryInfo()', async () => { it('resolves promise successfully with valid data', async () => { - const memoryInfo = await process.getProcessMemoryInfo() - expect(memoryInfo).to.be.an('object') + const memoryInfo = await process.getProcessMemoryInfo(); + expect(memoryInfo).to.be.an('object'); if (process.platform === 'linux' || process.platform === 'windows') { - expect(memoryInfo.residentSet).to.be.a('number').greaterThan(0) + expect(memoryInfo.residentSet).to.be.a('number').greaterThan(0); } - expect(memoryInfo.private).to.be.a('number').greaterThan(0) + expect(memoryInfo.private).to.be.a('number').greaterThan(0); // Shared bytes can be zero - expect(memoryInfo.shared).to.be.a('number').greaterThan(-1) - }) - }) + expect(memoryInfo.shared).to.be.a('number').greaterThan(-1); + }); + }); describe('process.getSystemMemoryInfo()', () => { it('returns system memory info object', () => { - const systemMemoryInfo = process.getSystemMemoryInfo() - expect(systemMemoryInfo.free).to.be.a('number') - expect(systemMemoryInfo.total).to.be.a('number') - }) - }) + const systemMemoryInfo = process.getSystemMemoryInfo(); + expect(systemMemoryInfo.free).to.be.a('number'); + expect(systemMemoryInfo.total).to.be.a('number'); + }); + }); describe('process.getSystemVersion()', () => { it('returns a string', () => { - expect(process.getSystemVersion()).to.be.a('string') - }) - }) + expect(process.getSystemVersion()).to.be.a('string'); + }); + }); describe('process.getHeapStatistics()', () => { it('returns heap statistics object', () => { - const heapStats = process.getHeapStatistics() - expect(heapStats.totalHeapSize).to.be.a('number') - expect(heapStats.totalHeapSizeExecutable).to.be.a('number') - expect(heapStats.totalPhysicalSize).to.be.a('number') - expect(heapStats.totalAvailableSize).to.be.a('number') - expect(heapStats.usedHeapSize).to.be.a('number') - expect(heapStats.heapSizeLimit).to.be.a('number') - expect(heapStats.mallocedMemory).to.be.a('number') - expect(heapStats.peakMallocedMemory).to.be.a('number') - expect(heapStats.doesZapGarbage).to.be.a('boolean') - }) - }) + const heapStats = process.getHeapStatistics(); + expect(heapStats.totalHeapSize).to.be.a('number'); + expect(heapStats.totalHeapSizeExecutable).to.be.a('number'); + expect(heapStats.totalPhysicalSize).to.be.a('number'); + expect(heapStats.totalAvailableSize).to.be.a('number'); + expect(heapStats.usedHeapSize).to.be.a('number'); + expect(heapStats.heapSizeLimit).to.be.a('number'); + expect(heapStats.mallocedMemory).to.be.a('number'); + expect(heapStats.peakMallocedMemory).to.be.a('number'); + expect(heapStats.doesZapGarbage).to.be.a('boolean'); + }); + }); describe('process.takeHeapSnapshot()', () => { - it('returns true on success', async () => { - const filePath = path.join(await ipcRenderer.invoke('get-temp-dir'), 'test.heapsnapshot') + // TODO(nornagon): re-enable this once it's passing again + xit('returns true on success', async () => { + const filePath = path.join(await ipcRenderer.invoke('get-temp-dir'), 'test.heapsnapshot'); const cleanup = () => { try { - fs.unlinkSync(filePath) + fs.unlinkSync(filePath); } catch (e) { // ignore error } - } + }; try { - const success = process.takeHeapSnapshot(filePath) - expect(success).to.be.true() - const stats = fs.statSync(filePath) - expect(stats.size).not.to.be.equal(0) + const success = process.takeHeapSnapshot(filePath); + expect(success).to.be.true(); + const stats = fs.statSync(filePath); + expect(stats.size).not.to.be.equal(0); } finally { - cleanup() + cleanup(); } - }) + }); it('returns false on failure', () => { - const success = process.takeHeapSnapshot('') - expect(success).to.be.false() - }) - }) -}) + const success = process.takeHeapSnapshot(''); + expect(success).to.be.false(); + }); + }); +}); diff --git a/spec/api-remote-spec.js b/spec/api-remote-spec.js index 1231bdb352453..92e984212c68b 100644 --- a/spec/api-remote-spec.js +++ b/spec/api-remote-spec.js @@ -1,549 +1,549 @@ -'use strict' +'use strict'; -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const path = require('path') -const { closeWindow } = require('./window-helpers') -const { resolveGetters } = require('./expect-helpers') -const { ifdescribe } = require('./spec-helpers') +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const path = require('path'); +const { closeWindow } = require('./window-helpers'); +const { resolveGetters } = require('./expect-helpers'); +const { ifdescribe } = require('./spec-helpers'); -const { remote, ipcRenderer } = require('electron') -const { ipcMain, BrowserWindow } = remote -const { expect } = chai +const { remote, ipcRenderer } = require('electron'); +const { ipcMain, BrowserWindow } = remote; +const { expect } = chai; -const features = process.electronBinding('features') +const features = process.electronBinding('features'); -chai.use(dirtyChai) +chai.use(dirtyChai); const comparePaths = (path1, path2) => { if (process.platform === 'win32') { - path1 = path1.toLowerCase() - path2 = path2.toLowerCase() + path1 = path1.toLowerCase(); + path2 = path2.toLowerCase(); } - expect(path1).to.equal(path2) -} + expect(path1).to.equal(path2); +}; ifdescribe(features.isRemoteModuleEnabled())('remote module', () => { - const fixtures = path.join(__dirname, 'fixtures') + const fixtures = path.join(__dirname, 'fixtures'); describe('remote.require', () => { it('should returns same object for the same module', () => { - const dialog1 = remote.require('electron') - const dialog2 = remote.require('electron') - expect(dialog1).to.equal(dialog2) - }) + const dialog1 = remote.require('electron'); + const dialog2 = remote.require('electron'); + expect(dialog1).to.equal(dialog2); + }); it('should work when object contains id property', () => { - const a = remote.require(path.join(fixtures, 'module', 'id.js')) - expect(a.id).to.equal(1127) - }) + const a = remote.require(path.join(fixtures, 'module', 'id.js')); + expect(a.id).to.equal(1127); + }); it('should work when object has no prototype', () => { - const a = remote.require(path.join(fixtures, 'module', 'no-prototype.js')) - expect(a.foo.constructor.name).to.equal('') - expect(a.foo.bar).to.equal('baz') - expect(a.foo.baz).to.equal(false) - expect(a.bar).to.equal(1234) - expect(a.anonymous.constructor.name).to.equal('') - expect(a.getConstructorName(Object.create(null))).to.equal('') - expect(a.getConstructorName(new (class {})())).to.equal('') - }) + const a = remote.require(path.join(fixtures, 'module', 'no-prototype.js')); + expect(a.foo.constructor.name).to.equal(''); + expect(a.foo.bar).to.equal('baz'); + expect(a.foo.baz).to.equal(false); + expect(a.bar).to.equal(1234); + expect(a.anonymous.constructor.name).to.equal(''); + expect(a.getConstructorName(Object.create(null))).to.equal(''); + expect(a.getConstructorName(new (class {})())).to.equal(''); + }); it('should search module from the user app', () => { - comparePaths(path.normalize(remote.process.mainModule.filename), path.resolve(__dirname, 'static', 'main.js')) - comparePaths(path.normalize(remote.process.mainModule.paths[0]), path.resolve(__dirname, 'static', 'node_modules')) - }) + comparePaths(path.normalize(remote.process.mainModule.filename), path.resolve(__dirname, 'static', 'main.js')); + comparePaths(path.normalize(remote.process.mainModule.paths[0]), path.resolve(__dirname, 'static', 'node_modules')); + }); it('should work with function properties', () => { - let a = remote.require(path.join(fixtures, 'module', 'export-function-with-properties.js')) - expect(a).to.be.a('function') - expect(a.bar).to.equal('baz') - - a = remote.require(path.join(fixtures, 'module', 'function-with-properties.js')) - expect(a).to.be.an('object') - expect(a.foo()).to.equal('hello') - expect(a.foo.bar).to.equal('baz') - expect(a.foo.nested.prop).to.equal('yes') - expect(a.foo.method1()).to.equal('world') - expect(a.foo.method1.prop1()).to.equal(123) - - expect(a.foo).to.have.a.property('bar') - expect(a.foo).to.have.a.property('nested') - expect(a.foo).to.have.a.property('method1') - - a = remote.require(path.join(fixtures, 'module', 'function-with-missing-properties.js')).setup() - expect(a.bar()).to.equal(true) - expect(a.bar.baz).to.be.undefined() - }) + let a = remote.require(path.join(fixtures, 'module', 'export-function-with-properties.js')); + expect(a).to.be.a('function'); + expect(a.bar).to.equal('baz'); + + a = remote.require(path.join(fixtures, 'module', 'function-with-properties.js')); + expect(a).to.be.an('object'); + expect(a.foo()).to.equal('hello'); + expect(a.foo.bar).to.equal('baz'); + expect(a.foo.nested.prop).to.equal('yes'); + expect(a.foo.method1()).to.equal('world'); + expect(a.foo.method1.prop1()).to.equal(123); + + expect(a.foo).to.have.a.property('bar'); + expect(a.foo).to.have.a.property('nested'); + expect(a.foo).to.have.a.property('method1'); + + a = remote.require(path.join(fixtures, 'module', 'function-with-missing-properties.js')).setup(); + expect(a.bar()).to.equal(true); + expect(a.bar.baz).to.be.undefined(); + }); it('should work with static class members', () => { - const a = remote.require(path.join(fixtures, 'module', 'remote-static.js')) - expect(a.Foo).to.be.a('function') - expect(a.Foo.foo()).to.equal(3) - expect(a.Foo.bar).to.equal('baz') + const a = remote.require(path.join(fixtures, 'module', 'remote-static.js')); + expect(a.Foo).to.be.a('function'); + expect(a.Foo.foo()).to.equal(3); + expect(a.Foo.bar).to.equal('baz'); - const foo = new a.Foo() - expect(foo.baz()).to.equal(123) - }) + const foo = new a.Foo(); + expect(foo.baz()).to.equal(123); + }); it('includes the length of functions specified as arguments', () => { - const a = remote.require(path.join(fixtures, 'module', 'function-with-args.js')) - expect(a((a, b, c, d, f) => {})).to.equal(5) - expect(a((a) => {})).to.equal(1) - expect(a((...args) => {})).to.equal(0) - }) + const a = remote.require(path.join(fixtures, 'module', 'function-with-args.js')); + expect(a((a, b, c, d, f) => {})).to.equal(5); + expect(a((a) => {})).to.equal(1); + expect(a((...args) => {})).to.equal(0); + }); it('handles circular references in arrays and objects', () => { - const a = remote.require(path.join(fixtures, 'module', 'circular.js')) + const a = remote.require(path.join(fixtures, 'module', 'circular.js')); - let arrayA = ['foo'] - const arrayB = [arrayA, 'bar'] - arrayA.push(arrayB) + let arrayA = ['foo']; + const arrayB = [arrayA, 'bar']; + arrayA.push(arrayB); expect(a.returnArgs(arrayA, arrayB)).to.deep.equal([ ['foo', [null, 'bar']], [['foo', null], 'bar'] - ]) + ]); - let objectA = { foo: 'bar' } - const objectB = { baz: objectA } - objectA.objectB = objectB + let objectA = { foo: 'bar' }; + const objectB = { baz: objectA }; + objectA.objectB = objectB; expect(a.returnArgs(objectA, objectB)).to.deep.equal([ { foo: 'bar', objectB: { baz: null } }, { baz: { foo: 'bar', objectB: null } } - ]) + ]); - arrayA = [1, 2, 3] + arrayA = [1, 2, 3]; expect(a.returnArgs({ foo: arrayA }, { bar: arrayA })).to.deep.equal([ { foo: [1, 2, 3] }, { bar: [1, 2, 3] } - ]) + ]); - objectA = { foo: 'bar' } + objectA = { foo: 'bar' }; expect(a.returnArgs({ foo: objectA }, { bar: objectA })).to.deep.equal([ { foo: { foo: 'bar' } }, { bar: { foo: 'bar' } } - ]) + ]); - arrayA = [] - arrayA.push(arrayA) + arrayA = []; + arrayA.push(arrayA); expect(a.returnArgs(arrayA)).to.deep.equal([ [null] - ]) + ]); - objectA = {} - objectA.foo = objectA - objectA.bar = 'baz' + objectA = {}; + objectA.foo = objectA; + objectA.bar = 'baz'; expect(a.returnArgs(objectA)).to.deep.equal([ { foo: null, bar: 'baz' } - ]) + ]); - objectA = {} - objectA.foo = { bar: objectA } - objectA.bar = 'baz' + objectA = {}; + objectA.foo = { bar: objectA }; + objectA.bar = 'baz'; expect(a.returnArgs(objectA)).to.deep.equal([ { foo: { bar: null }, bar: 'baz' } - ]) - }) - }) + ]); + }); + }); describe('remote.createFunctionWithReturnValue', () => { it('should be called in browser synchronously', () => { - const buf = Buffer.from('test') - const call = remote.require(path.join(fixtures, 'module', 'call.js')) - const result = call.call(remote.createFunctionWithReturnValue(buf)) - expect(result).to.be.an.instanceOf(Buffer) - }) - }) + const buf = Buffer.from('test'); + const call = remote.require(path.join(fixtures, 'module', 'call.js')); + const result = call.call(remote.createFunctionWithReturnValue(buf)); + expect(result).to.be.an.instanceOf(Buffer); + }); + }); describe('remote modules', () => { - it('includes browser process modules as properties', () => { - expect(remote.app.getPath).to.be.a('function') - expect(remote.webContents.getFocusedWebContents).to.be.a('function') - expect(remote.clipboard.readText).to.be.a('function') - }) + it('includes browser process modules as properties', async () => { + const mainModules = await ipcRenderer.invoke('get-modules'); + const remoteModules = mainModules.filter(name => remote[name]); + expect(remoteModules).to.be.deep.equal(mainModules); + }); it('returns toString() of original function via toString()', () => { - const { readText } = remote.clipboard - expect(readText.toString().startsWith('function')).to.be.true() + const { readText } = remote.clipboard; + expect(readText.toString().startsWith('function')).to.be.true(); - const { functionWithToStringProperty } = remote.require(path.join(fixtures, 'module', 'to-string-non-function.js')) - expect(functionWithToStringProperty.toString).to.equal('hello') - }) - }) + const { functionWithToStringProperty } = remote.require(path.join(fixtures, 'module', 'to-string-non-function.js')); + expect(functionWithToStringProperty.toString).to.equal('hello'); + }); + }); describe('remote object in renderer', () => { it('can change its properties', () => { - const property = remote.require(path.join(fixtures, 'module', 'property.js')) - expect(property).to.have.a.property('property').that.is.equal(1127) - - property.property = null - expect(property).to.have.a.property('property').that.is.null() - property.property = undefined - expect(property).to.have.a.property('property').that.is.undefined() - property.property = 1007 - expect(property).to.have.a.property('property').that.is.equal(1007) - - expect(property.getFunctionProperty()).to.equal('foo-browser') - property.func.property = 'bar' - expect(property.getFunctionProperty()).to.equal('bar-browser') - property.func.property = 'foo' // revert back - - const property2 = remote.require(path.join(fixtures, 'module', 'property.js')) - expect(property2.property).to.equal(1007) - property.property = 1127 - }) + const property = remote.require(path.join(fixtures, 'module', 'property.js')); + expect(property).to.have.a.property('property').that.is.equal(1127); + + property.property = null; + expect(property).to.have.a.property('property').that.is.null(); + property.property = undefined; + expect(property).to.have.a.property('property').that.is.undefined(); + property.property = 1007; + expect(property).to.have.a.property('property').that.is.equal(1007); + + expect(property.getFunctionProperty()).to.equal('foo-browser'); + property.func.property = 'bar'; + expect(property.getFunctionProperty()).to.equal('bar-browser'); + property.func.property = 'foo'; // revert back + + const property2 = remote.require(path.join(fixtures, 'module', 'property.js')); + expect(property2.property).to.equal(1007); + property.property = 1127; + }); it('rethrows errors getting/setting properties', () => { - const foo = remote.require(path.join(fixtures, 'module', 'error-properties.js')) + const foo = remote.require(path.join(fixtures, 'module', 'error-properties.js')); expect(() => { // eslint-disable-next-line foo.bar - }).to.throw('getting error') + }).to.throw('getting error'); expect(() => { - foo.bar = 'test' - }).to.throw('setting error') - }) + foo.bar = 'test'; + }).to.throw('setting error'); + }); it('can set a remote property with a remote object', () => { - const foo = remote.require(path.join(fixtures, 'module', 'remote-object-set.js')) + const foo = remote.require(path.join(fixtures, 'module', 'remote-object-set.js')); expect(() => { - foo.bar = remote.getCurrentWindow() - }).to.not.throw() - }) + foo.bar = remote.getCurrentWindow(); + }).to.not.throw(); + }); it('can construct an object from its member', () => { - const call = remote.require(path.join(fixtures, 'module', 'call.js')) - const obj = new call.constructor() - expect(obj.test).to.equal('test') - }) + const call = remote.require(path.join(fixtures, 'module', 'call.js')); + const obj = new call.constructor(); + expect(obj.test).to.equal('test'); + }); it('can reassign and delete its member functions', () => { - const remoteFunctions = remote.require(path.join(fixtures, 'module', 'function.js')) - expect(remoteFunctions.aFunction()).to.equal(1127) + const remoteFunctions = remote.require(path.join(fixtures, 'module', 'function.js')); + expect(remoteFunctions.aFunction()).to.equal(1127); - remoteFunctions.aFunction = () => { return 1234 } - expect(remoteFunctions.aFunction()).to.equal(1234) + remoteFunctions.aFunction = () => { return 1234; }; + expect(remoteFunctions.aFunction()).to.equal(1234); - expect(delete remoteFunctions.aFunction).to.equal(true) - }) + expect(delete remoteFunctions.aFunction).to.equal(true); + }); it('is referenced by its members', () => { - const stringify = remote.getGlobal('JSON').stringify - global.gc() - stringify({}) - }) - }) + const stringify = remote.getGlobal('JSON').stringify; + global.gc(); + stringify({}); + }); + }); describe('remote value in browser', () => { - const print = path.join(fixtures, 'module', 'print_name.js') - const printName = remote.require(print) + const print = path.join(fixtures, 'module', 'print_name.js'); + const printName = remote.require(print); it('preserves NaN', () => { - expect(printName.getNaN()).to.be.NaN() - expect(printName.echo(NaN)).to.be.NaN() - }) + expect(printName.getNaN()).to.be.NaN(); + expect(printName.echo(NaN)).to.be.NaN(); + }); it('preserves Infinity', () => { - expect(printName.getInfinity()).to.equal(Infinity) - expect(printName.echo(Infinity)).to.equal(Infinity) - }) + expect(printName.getInfinity()).to.equal(Infinity); + expect(printName.echo(Infinity)).to.equal(Infinity); + }); it('keeps its constructor name for objects', () => { - const buf = Buffer.from('test') - expect(printName.print(buf)).to.equal('Buffer') - }) + const buf = Buffer.from('test'); + expect(printName.print(buf)).to.equal('Buffer'); + }); it('supports instanceof Boolean', () => { - const obj = Boolean(true) - expect(printName.print(obj)).to.equal('Boolean') - expect(printName.echo(obj)).to.deep.equal(obj) - }) + const obj = Boolean(true); + expect(printName.print(obj)).to.equal('Boolean'); + expect(printName.echo(obj)).to.deep.equal(obj); + }); it('supports instanceof Number', () => { - const obj = Number(42) - expect(printName.print(obj)).to.equal('Number') - expect(printName.echo(obj)).to.deep.equal(obj) - }) + const obj = Number(42); + expect(printName.print(obj)).to.equal('Number'); + expect(printName.echo(obj)).to.deep.equal(obj); + }); it('supports instanceof String', () => { - const obj = String('Hello World!') - expect(printName.print(obj)).to.equal('String') - expect(printName.echo(obj)).to.deep.equal(obj) - }) + const obj = String('Hello World!'); + expect(printName.print(obj)).to.equal('String'); + expect(printName.echo(obj)).to.deep.equal(obj); + }); it('supports instanceof Date', () => { - const now = new Date() - expect(printName.print(now)).to.equal('Date') - expect(printName.echo(now)).to.deep.equal(now) - }) + const now = new Date(); + expect(printName.print(now)).to.equal('Date'); + expect(printName.echo(now)).to.deep.equal(now); + }); it('supports instanceof RegExp', () => { - const regexp = RegExp('.*') - expect(printName.print(regexp)).to.equal('RegExp') - expect(printName.echo(regexp)).to.deep.equal(regexp) - }) + const regexp = RegExp('.*'); + expect(printName.print(regexp)).to.equal('RegExp'); + expect(printName.echo(regexp)).to.deep.equal(regexp); + }); it('supports instanceof Buffer', () => { - const buffer = Buffer.from('test') - expect(buffer.equals(printName.echo(buffer))).to.be.true() + const buffer = Buffer.from('test'); + expect(buffer.equals(printName.echo(buffer))).to.be.true(); - const objectWithBuffer = { a: 'foo', b: Buffer.from('bar') } - expect(objectWithBuffer.b.equals(printName.echo(objectWithBuffer).b)).to.be.true() + const objectWithBuffer = { a: 'foo', b: Buffer.from('bar') }; + expect(objectWithBuffer.b.equals(printName.echo(objectWithBuffer).b)).to.be.true(); - const arrayWithBuffer = [1, 2, Buffer.from('baz')] - expect(arrayWithBuffer[2].equals(printName.echo(arrayWithBuffer)[2])).to.be.true() - }) + const arrayWithBuffer = [1, 2, Buffer.from('baz')]; + expect(arrayWithBuffer[2].equals(printName.echo(arrayWithBuffer)[2])).to.be.true(); + }); it('supports instanceof ArrayBuffer', () => { - const buffer = new ArrayBuffer(8) - const view = new DataView(buffer) + const buffer = new ArrayBuffer(8); + const view = new DataView(buffer); - view.setFloat64(0, Math.PI) - expect(printName.echo(buffer)).to.deep.equal(buffer) - expect(printName.print(buffer)).to.equal('ArrayBuffer') - }) + view.setFloat64(0, Math.PI); + expect(printName.echo(buffer)).to.deep.equal(buffer); + expect(printName.print(buffer)).to.equal('ArrayBuffer'); + }); it('supports instanceof Int8Array', () => { - const values = [1, 2, 3, 4] - expect([...printName.typedArray('Int8Array', values)]).to.deep.equal(values) + const values = [1, 2, 3, 4]; + expect([...printName.typedArray('Int8Array', values)]).to.deep.equal(values); - const int8values = new Int8Array(values) - expect(printName.typedArray('Int8Array', int8values)).to.deep.equal(int8values) - expect(printName.print(int8values)).to.equal('Int8Array') - }) + const int8values = new Int8Array(values); + expect(printName.typedArray('Int8Array', int8values)).to.deep.equal(int8values); + expect(printName.print(int8values)).to.equal('Int8Array'); + }); it('supports instanceof Uint8Array', () => { - const values = [1, 2, 3, 4] - expect([...printName.typedArray('Uint8Array', values)]).to.deep.equal(values) + const values = [1, 2, 3, 4]; + expect([...printName.typedArray('Uint8Array', values)]).to.deep.equal(values); - const uint8values = new Uint8Array(values) - expect(printName.typedArray('Uint8Array', uint8values)).to.deep.equal(uint8values) - expect(printName.print(uint8values)).to.equal('Uint8Array') - }) + const uint8values = new Uint8Array(values); + expect(printName.typedArray('Uint8Array', uint8values)).to.deep.equal(uint8values); + expect(printName.print(uint8values)).to.equal('Uint8Array'); + }); it('supports instanceof Uint8ClampedArray', () => { - const values = [1, 2, 3, 4] - expect([...printName.typedArray('Uint8ClampedArray', values)]).to.deep.equal(values) + const values = [1, 2, 3, 4]; + expect([...printName.typedArray('Uint8ClampedArray', values)]).to.deep.equal(values); - const uint8values = new Uint8ClampedArray(values) - expect(printName.typedArray('Uint8ClampedArray', uint8values)).to.deep.equal(uint8values) - expect(printName.print(uint8values)).to.equal('Uint8ClampedArray') - }) + const uint8values = new Uint8ClampedArray(values); + expect(printName.typedArray('Uint8ClampedArray', uint8values)).to.deep.equal(uint8values); + expect(printName.print(uint8values)).to.equal('Uint8ClampedArray'); + }); it('supports instanceof Int16Array', () => { - const values = [0x1234, 0x2345, 0x3456, 0x4567] - expect([...printName.typedArray('Int16Array', values)]).to.deep.equal(values) + const values = [0x1234, 0x2345, 0x3456, 0x4567]; + expect([...printName.typedArray('Int16Array', values)]).to.deep.equal(values); - const int16values = new Int16Array(values) - expect(printName.typedArray('Int16Array', int16values)).to.deep.equal(int16values) - expect(printName.print(int16values)).to.equal('Int16Array') - }) + const int16values = new Int16Array(values); + expect(printName.typedArray('Int16Array', int16values)).to.deep.equal(int16values); + expect(printName.print(int16values)).to.equal('Int16Array'); + }); it('supports instanceof Uint16Array', () => { - const values = [0x1234, 0x2345, 0x3456, 0x4567] - expect([...printName.typedArray('Uint16Array', values)]).to.deep.equal(values) + const values = [0x1234, 0x2345, 0x3456, 0x4567]; + expect([...printName.typedArray('Uint16Array', values)]).to.deep.equal(values); - const uint16values = new Uint16Array(values) - expect(printName.typedArray('Uint16Array', uint16values)).to.deep.equal(uint16values) - expect(printName.print(uint16values)).to.equal('Uint16Array') - }) + const uint16values = new Uint16Array(values); + expect(printName.typedArray('Uint16Array', uint16values)).to.deep.equal(uint16values); + expect(printName.print(uint16values)).to.equal('Uint16Array'); + }); it('supports instanceof Int32Array', () => { - const values = [0x12345678, 0x23456789] - expect([...printName.typedArray('Int32Array', values)]).to.deep.equal(values) + const values = [0x12345678, 0x23456789]; + expect([...printName.typedArray('Int32Array', values)]).to.deep.equal(values); - const int32values = new Int32Array(values) - expect(printName.typedArray('Int32Array', int32values)).to.deep.equal(int32values) - expect(printName.print(int32values)).to.equal('Int32Array') - }) + const int32values = new Int32Array(values); + expect(printName.typedArray('Int32Array', int32values)).to.deep.equal(int32values); + expect(printName.print(int32values)).to.equal('Int32Array'); + }); it('supports instanceof Uint32Array', () => { - const values = [0x12345678, 0x23456789] - expect([...printName.typedArray('Uint32Array', values)]).to.deep.equal(values) + const values = [0x12345678, 0x23456789]; + expect([...printName.typedArray('Uint32Array', values)]).to.deep.equal(values); - const uint32values = new Uint32Array(values) - expect(printName.typedArray('Uint32Array', uint32values)).to.deep.equal(uint32values) - expect(printName.print(uint32values)).to.equal('Uint32Array') - }) + const uint32values = new Uint32Array(values); + expect(printName.typedArray('Uint32Array', uint32values)).to.deep.equal(uint32values); + expect(printName.print(uint32values)).to.equal('Uint32Array'); + }); it('supports instanceof Float32Array', () => { - const values = [0.5, 1.0, 1.5] - expect([...printName.typedArray('Float32Array', values)]).to.deep.equal(values) + const values = [0.5, 1.0, 1.5]; + expect([...printName.typedArray('Float32Array', values)]).to.deep.equal(values); - const float32values = new Float32Array() - expect(printName.typedArray('Float32Array', float32values)).to.deep.equal(float32values) - expect(printName.print(float32values)).to.equal('Float32Array') - }) + const float32values = new Float32Array(); + expect(printName.typedArray('Float32Array', float32values)).to.deep.equal(float32values); + expect(printName.print(float32values)).to.equal('Float32Array'); + }); it('supports instanceof Float64Array', () => { - const values = [0.5, 1.0, 1.5] - expect([...printName.typedArray('Float64Array', values)]).to.deep.equal(values) + const values = [0.5, 1.0, 1.5]; + expect([...printName.typedArray('Float64Array', values)]).to.deep.equal(values); - const float64values = new Float64Array([0.5, 1.0, 1.5]) - expect(printName.typedArray('Float64Array', float64values)).to.deep.equal(float64values) - expect(printName.print(float64values)).to.equal('Float64Array') - }) - }) + const float64values = new Float64Array([0.5, 1.0, 1.5]); + expect(printName.typedArray('Float64Array', float64values)).to.deep.equal(float64values); + expect(printName.print(float64values)).to.equal('Float64Array'); + }); + }); describe('remote promise', () => { it('can be used as promise in each side', (done) => { - const promise = remote.require(path.join(fixtures, 'module', 'promise.js')) + const promise = remote.require(path.join(fixtures, 'module', 'promise.js')); promise.twicePromise(Promise.resolve(1234)).then((value) => { - expect(value).to.equal(2468) - done() - }) - }) + expect(value).to.equal(2468); + done(); + }); + }); it('handles rejections via catch(onRejected)', (done) => { - const promise = remote.require(path.join(fixtures, 'module', 'rejected-promise.js')) + const promise = remote.require(path.join(fixtures, 'module', 'rejected-promise.js')); promise.reject(Promise.resolve(1234)).catch((error) => { - expect(error.message).to.equal('rejected') - done() - }) - }) + expect(error.message).to.equal('rejected'); + done(); + }); + }); it('handles rejections via then(onFulfilled, onRejected)', (done) => { - const promise = remote.require(path.join(fixtures, 'module', 'rejected-promise.js')) + const promise = remote.require(path.join(fixtures, 'module', 'rejected-promise.js')); promise.reject(Promise.resolve(1234)).then(() => {}, (error) => { - expect(error.message).to.equal('rejected') - done() - }) - }) + expect(error.message).to.equal('rejected'); + done(); + }); + }); it('does not emit unhandled rejection events in the main process', (done) => { remote.process.once('unhandledRejection', function (reason) { - done(reason) - }) + done(reason); + }); - const promise = remote.require(path.join(fixtures, 'module', 'unhandled-rejection.js')) + const promise = remote.require(path.join(fixtures, 'module', 'unhandled-rejection.js')); promise.reject().then(() => { - done(new Error('Promise was not rejected')) + done(new Error('Promise was not rejected')); }).catch((error) => { - expect(error.message).to.equal('rejected') - done() - }) - }) + expect(error.message).to.equal('rejected'); + done(); + }); + }); it('emits unhandled rejection events in the renderer process', (done) => { window.addEventListener('unhandledrejection', function handler (event) { - event.preventDefault() - expect(event.reason.message).to.equal('rejected') - window.removeEventListener('unhandledrejection', handler) - done() - }) + event.preventDefault(); + expect(event.reason.message).to.equal('rejected'); + window.removeEventListener('unhandledrejection', handler); + done(); + }); - const promise = remote.require(path.join(fixtures, 'module', 'unhandled-rejection.js')) + const promise = remote.require(path.join(fixtures, 'module', 'unhandled-rejection.js')); promise.reject().then(() => { - done(new Error('Promise was not rejected')) - }) - }) - }) + done(new Error('Promise was not rejected')); + }); + }); + }); describe('remote webContents', () => { it('can return same object with different getters', () => { - const contents1 = remote.getCurrentWindow().webContents - const contents2 = remote.getCurrentWebContents() - expect(contents1).to.equal(contents2) - }) - }) + const contents1 = remote.getCurrentWindow().webContents; + const contents2 = remote.getCurrentWebContents(); + expect(contents1).to.equal(contents2); + }); + }); describe('remote class', () => { - const cl = remote.require(path.join(fixtures, 'module', 'class.js')) - const base = cl.base - let derived = cl.derived + const cl = remote.require(path.join(fixtures, 'module', 'class.js')); + const base = cl.base; + let derived = cl.derived; it('can get methods', () => { - expect(base.method()).to.equal('method') - }) + expect(base.method()).to.equal('method'); + }); it('can get properties', () => { - expect(base.readonly).to.equal('readonly') - }) + expect(base.readonly).to.equal('readonly'); + }); it('can change properties', () => { - expect(base.value).to.equal('old') - base.value = 'new' - expect(base.value).to.equal('new') - base.value = 'old' - }) + expect(base.value).to.equal('old'); + base.value = 'new'; + expect(base.value).to.equal('new'); + base.value = 'old'; + }); it('has unenumerable methods', () => { - expect(base).to.not.have.own.property('method') - expect(Object.getPrototypeOf(base)).to.have.own.property('method') - }) + expect(base).to.not.have.own.property('method'); + expect(Object.getPrototypeOf(base)).to.have.own.property('method'); + }); it('keeps prototype chain in derived class', () => { - expect(derived.method()).to.equal('method') - expect(derived.readonly).to.equal('readonly') - expect(derived).to.not.have.own.property('method') - const proto = Object.getPrototypeOf(derived) - expect(proto).to.not.have.own.property('method') - expect(Object.getPrototypeOf(proto)).to.have.own.property('method') - }) + expect(derived.method()).to.equal('method'); + expect(derived.readonly).to.equal('readonly'); + expect(derived).to.not.have.own.property('method'); + const proto = Object.getPrototypeOf(derived); + expect(proto).to.not.have.own.property('method'); + expect(Object.getPrototypeOf(proto)).to.have.own.property('method'); + }); it('is referenced by methods in prototype chain', () => { - const method = derived.method - derived = null - global.gc() - expect(method()).to.equal('method') - }) - }) + const method = derived.method; + derived = null; + global.gc(); + expect(method()).to.equal('method'); + }); + }); describe('remote exception', () => { - const throwFunction = remote.require(path.join(fixtures, 'module', 'exception.js')) + const throwFunction = remote.require(path.join(fixtures, 'module', 'exception.js')); it('throws errors from the main process', () => { expect(() => { - throwFunction() - }).to.throw(/undefined/) - }) + throwFunction(); + }).to.throw(/undefined/); + }); it('tracks error cause', () => { try { - throwFunction(new Error('error from main')) - expect.fail() + throwFunction(new Error('error from main')); + expect.fail(); } catch (e) { - expect(e.message).to.match(/Could not call remote function/) - expect(e.cause.message).to.equal('error from main') + expect(e.message).to.match(/Could not call remote function/); + expect(e.cause.message).to.equal('error from main'); } - }) - }) + }); + }); describe('remote function in renderer', () => { - let w = null + let w = null; - afterEach(() => closeWindow(w).then(() => { w = null })) + afterEach(() => closeWindow(w).then(() => { w = null; })); afterEach(() => { - ipcMain.removeAllListeners('done') - }) + ipcMain.removeAllListeners('done'); + }); it('works when created in preload script', (done) => { - ipcMain.once('done', () => w.close()) - const preload = path.join(fixtures, 'module', 'preload-remote-function.js') + ipcMain.once('done', () => w.close()); + const preload = path.join(fixtures, 'module', 'preload-remote-function.js'); w = new BrowserWindow({ show: false, webPreferences: { preload } - }) - w.once('closed', () => done()) - w.loadURL('about:blank') - }) - }) + }); + w.once('closed', () => done()); + w.loadURL('about:blank'); + }); + }); describe('constructing a Uint8Array', () => { it('does not crash', () => { - const RUint8Array = remote.getGlobal('Uint8Array') - const arr = new RUint8Array() - }) - }) + const RUint8Array = remote.getGlobal('Uint8Array'); + const arr = new RUint8Array(); + }); + }); describe('remote listeners', () => { - let w = null - afterEach(() => closeWindow(w).then(() => { w = null })) + let w = null; + afterEach(() => closeWindow(w).then(() => { w = null; })); it('detaches listeners subscribed to destroyed renderers, and shows a warning', (done) => { w = new BrowserWindow({ @@ -551,7 +551,7 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => { webPreferences: { nodeIntegration: true } - }) + }); w.webContents.once('did-finish-load', () => { w.webContents.once('did-finish-load', () => { @@ -559,21 +559,39 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => { 'Attempting to call a function in a renderer window that has been closed or released.', 'Function provided here: remote-event-handler.html:11:33', 'Remote event names: remote-handler, other-remote-handler' - ].join('\n') + ].join('\n'); - const results = ipcRenderer.sendSync('try-emit-web-contents-event', w.webContents.id, 'remote-handler') + const results = ipcRenderer.sendSync('try-emit-web-contents-event', w.webContents.id, 'remote-handler'); expect(results).to.deep.equal({ warningMessage: expectedMessage, listenerCountBefore: 2, listenerCountAfter: 1 - }) - done() - }) - - w.webContents.reload() - }) - w.loadFile(path.join(fixtures, 'api', 'remote-event-handler.html')) - }) - }) -}) + }); + done(); + }); + + w.webContents.reload(); + }); + w.loadFile(path.join(fixtures, 'api', 'remote-event-handler.html')); + }); + }); + + describe('with an overriden global Promise constrctor', () => { + let original; + + before(() => { + original = Promise; + }); + + it('using a promise based method resolves correctly', async () => { + expect(await remote.getGlobal('returnAPromise')(123)).to.equal(123); + global.Promise = { resolve: () => ({}) }; + expect(await remote.getGlobal('returnAPromise')(456)).to.equal(456); + }); + + after(() => { + global.Promise = original; + }); + }); +}); diff --git a/spec/api-shell-spec.js b/spec/api-shell-spec.js index a3b3f28a103a5..8c8372836c05a 100644 --- a/spec/api-shell-spec.js +++ b/spec/api-shell-spec.js @@ -1,17 +1,17 @@ -const chai = require('chai') -const dirtyChai = require('dirty-chai') +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); -const fs = require('fs') -const path = require('path') -const os = require('os') -const http = require('http') -const { shell } = require('electron') +const fs = require('fs'); +const path = require('path'); +const os = require('os'); +const http = require('http'); +const { shell } = require('electron'); -const { expect } = chai -chai.use(dirtyChai) +const { expect } = chai; +chai.use(dirtyChai); describe('shell module', () => { - const fixtures = path.resolve(__dirname, 'fixtures') + const fixtures = path.resolve(__dirname, 'fixtures'); const shortcutOptions = { target: 'C:\\target', description: 'description', @@ -20,59 +20,59 @@ describe('shell module', () => { appUserModelId: 'appUserModelId', icon: 'icon', iconIndex: 1 - } + }; describe('shell.readShortcutLink(shortcutPath)', () => { beforeEach(function () { - if (process.platform !== 'win32') this.skip() - }) + if (process.platform !== 'win32') this.skip(); + }); it('throws when failed', () => { expect(() => { - shell.readShortcutLink('not-exist') - }).to.throw('Failed to read shortcut link') - }) + shell.readShortcutLink('not-exist'); + }).to.throw('Failed to read shortcut link'); + }); it('reads all properties of a shortcut', () => { - const shortcut = shell.readShortcutLink(path.join(fixtures, 'assets', 'shortcut.lnk')) - expect(shortcut).to.deep.equal(shortcutOptions) - }) - }) + const shortcut = shell.readShortcutLink(path.join(fixtures, 'assets', 'shortcut.lnk')); + expect(shortcut).to.deep.equal(shortcutOptions); + }); + }); describe('shell.writeShortcutLink(shortcutPath[, operation], options)', () => { beforeEach(function () { - if (process.platform !== 'win32') this.skip() - }) + if (process.platform !== 'win32') this.skip(); + }); - const tmpShortcut = path.join(os.tmpdir(), `${Date.now()}.lnk`) + const tmpShortcut = path.join(os.tmpdir(), `${Date.now()}.lnk`); afterEach(() => { - fs.unlinkSync(tmpShortcut) - }) + fs.unlinkSync(tmpShortcut); + }); it('writes the shortcut', () => { - expect(shell.writeShortcutLink(tmpShortcut, { target: 'C:\\' })).to.be.true() - expect(fs.existsSync(tmpShortcut)).to.be.true() - }) + expect(shell.writeShortcutLink(tmpShortcut, { target: 'C:\\' })).to.be.true(); + expect(fs.existsSync(tmpShortcut)).to.be.true(); + }); it('correctly sets the fields', () => { - expect(shell.writeShortcutLink(tmpShortcut, shortcutOptions)).to.be.true() - expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions) - }) + expect(shell.writeShortcutLink(tmpShortcut, shortcutOptions)).to.be.true(); + expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions); + }); it('updates the shortcut', () => { - expect(shell.writeShortcutLink(tmpShortcut, 'update', shortcutOptions)).to.be.false() - expect(shell.writeShortcutLink(tmpShortcut, 'create', shortcutOptions)).to.be.true() - expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions) - const change = { target: 'D:\\' } - expect(shell.writeShortcutLink(tmpShortcut, 'update', change)).to.be.true() - expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(Object.assign(shortcutOptions, change)) - }) + expect(shell.writeShortcutLink(tmpShortcut, 'update', shortcutOptions)).to.be.false(); + expect(shell.writeShortcutLink(tmpShortcut, 'create', shortcutOptions)).to.be.true(); + expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions); + const change = { target: 'D:\\' }; + expect(shell.writeShortcutLink(tmpShortcut, 'update', change)).to.be.true(); + expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(Object.assign(shortcutOptions, change)); + }); it('replaces the shortcut', () => { - expect(shell.writeShortcutLink(tmpShortcut, 'replace', shortcutOptions)).to.be.false() - expect(shell.writeShortcutLink(tmpShortcut, 'create', shortcutOptions)).to.be.true() - expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions) + expect(shell.writeShortcutLink(tmpShortcut, 'replace', shortcutOptions)).to.be.false(); + expect(shell.writeShortcutLink(tmpShortcut, 'create', shortcutOptions)).to.be.true(); + expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(shortcutOptions); const change = { target: 'D:\\', description: 'description2', @@ -81,9 +81,9 @@ describe('shell module', () => { appUserModelId: 'appUserModelId2', icon: 'icon2', iconIndex: 2 - } - expect(shell.writeShortcutLink(tmpShortcut, 'replace', change)).to.be.true() - expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(change) - }) - }) -}) + }; + expect(shell.writeShortcutLink(tmpShortcut, 'replace', change)).to.be.true(); + expect(shell.readShortcutLink(tmpShortcut)).to.deep.equal(change); + }); + }); +}); diff --git a/spec/api-web-frame-spec.js b/spec/api-web-frame-spec.js index 25f3187073744..3c9a7545254e3 100644 --- a/spec/api-web-frame-spec.js +++ b/spec/api-web-frame-spec.js @@ -1,39 +1,32 @@ -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const { webFrame } = require('electron') +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const { webFrame } = require('electron'); -const { expect } = chai -chai.use(dirtyChai) +const { expect } = chai; +chai.use(dirtyChai); describe('webFrame module', function () { - it('supports setting the visual and layout zoom level limits', function () { - expect(() => { - webFrame.setVisualZoomLevelLimits(1, 50) - webFrame.setLayoutZoomLevelLimits(0, 25) - }).to.not.throw() - }) - it('top is self for top frame', () => { - expect(webFrame.top.context).to.equal(webFrame.context) - }) + expect(webFrame.top.context).to.equal(webFrame.context); + }); it('opener is null for top frame', () => { - expect(webFrame.opener).to.be.null() - }) + expect(webFrame.opener).to.be.null(); + }); it('firstChild is null for top frame', () => { - expect(webFrame.firstChild).to.be.null() - }) + expect(webFrame.firstChild).to.be.null(); + }); it('getFrameForSelector() does not crash when not found', () => { - expect(webFrame.getFrameForSelector('unexist-selector')).to.be.null() - }) + expect(webFrame.getFrameForSelector('unexist-selector')).to.be.null(); + }); it('findFrameByName() does not crash when not found', () => { - expect(webFrame.findFrameByName('unexist-name')).to.be.null() - }) + expect(webFrame.findFrameByName('unexist-name')).to.be.null(); + }); it('findFrameByRoutingId() does not crash when not found', () => { - expect(webFrame.findFrameByRoutingId(-1)).to.be.null() - }) -}) + expect(webFrame.findFrameByRoutingId(-1)).to.be.null(); + }); +}); diff --git a/spec/asar-spec.js b/spec/asar-spec.js index 25387ce71f182..96ca4788e619f 100644 --- a/spec/asar-spec.js +++ b/spec/asar-spec.js @@ -1,1329 +1,1329 @@ -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const ChildProcess = require('child_process') -const fs = require('fs') -const path = require('path') -const temp = require('temp').track() -const util = require('util') -const nativeImage = require('electron').nativeImage +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const ChildProcess = require('child_process'); +const fs = require('fs'); +const path = require('path'); +const temp = require('temp').track(); +const util = require('util'); +const nativeImage = require('electron').nativeImage; -const features = process.electronBinding('features') +const features = process.electronBinding('features'); -const { expect } = chai -chai.use(dirtyChai) +const { expect } = chai; +chai.use(dirtyChai); async function expectToThrowErrorWithCode (func, code) { - let error + let error; try { - await func() + await func(); } catch (e) { - error = e + error = e; } - expect(error).is.an('Error') - expect(error).to.have.property('code').which.equals(code) + expect(error).is.an('Error'); + expect(error).to.have.property('code').which.equals(code); } describe('asar package', function () { - const fixtures = path.join(__dirname, 'fixtures') - const asarDir = path.join(fixtures, 'test.asar') + const fixtures = path.join(__dirname, 'fixtures'); + const asarDir = path.join(fixtures, 'test.asar'); describe('node api', function () { it('supports paths specified as a Buffer', function () { - const file = Buffer.from(path.join(asarDir, 'a.asar', 'file1')) - expect(fs.existsSync(file)).to.be.true() - }) + const file = Buffer.from(path.join(asarDir, 'a.asar', 'file1')); + expect(fs.existsSync(file)).to.be.true(); + }); describe('fs.readFileSync', function () { it('does not leak fd', function () { - let readCalls = 1 + let readCalls = 1; while (readCalls <= 10000) { - fs.readFileSync(path.join(process.resourcesPath, 'default_app.asar', 'main.js')) - readCalls++ + fs.readFileSync(path.join(process.resourcesPath, 'default_app.asar', 'main.js')); + readCalls++; } - }) + }); it('reads a normal file', function () { - const file1 = path.join(asarDir, 'a.asar', 'file1') - expect(fs.readFileSync(file1).toString().trim()).to.equal('file1') - const file2 = path.join(asarDir, 'a.asar', 'file2') - expect(fs.readFileSync(file2).toString().trim()).to.equal('file2') - const file3 = path.join(asarDir, 'a.asar', 'file3') - expect(fs.readFileSync(file3).toString().trim()).to.equal('file3') - }) + const file1 = path.join(asarDir, 'a.asar', 'file1'); + expect(fs.readFileSync(file1).toString().trim()).to.equal('file1'); + const file2 = path.join(asarDir, 'a.asar', 'file2'); + expect(fs.readFileSync(file2).toString().trim()).to.equal('file2'); + const file3 = path.join(asarDir, 'a.asar', 'file3'); + expect(fs.readFileSync(file3).toString().trim()).to.equal('file3'); + }); it('reads from a empty file', function () { - const file = path.join(asarDir, 'empty.asar', 'file1') - const buffer = fs.readFileSync(file) - expect(buffer).to.be.empty() - expect(buffer.toString()).to.equal('') - }) + const file = path.join(asarDir, 'empty.asar', 'file1'); + const buffer = fs.readFileSync(file); + expect(buffer).to.be.empty(); + expect(buffer.toString()).to.equal(''); + }); it('reads a linked file', function () { - const p = path.join(asarDir, 'a.asar', 'link1') - expect(fs.readFileSync(p).toString().trim()).to.equal('file1') - }) + const p = path.join(asarDir, 'a.asar', 'link1'); + expect(fs.readFileSync(p).toString().trim()).to.equal('file1'); + }); it('reads a file from linked directory', function () { - const p1 = path.join(asarDir, 'a.asar', 'link2', 'file1') - expect(fs.readFileSync(p1).toString().trim()).to.equal('file1') - const p2 = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') - expect(fs.readFileSync(p2).toString().trim()).to.equal('file1') - }) + const p1 = path.join(asarDir, 'a.asar', 'link2', 'file1'); + expect(fs.readFileSync(p1).toString().trim()).to.equal('file1'); + const p2 = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); + expect(fs.readFileSync(p2).toString().trim()).to.equal('file1'); + }); it('throws ENOENT error when can not find file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - fs.readFileSync(p) - }).to.throw(/ENOENT/) - }) + fs.readFileSync(p); + }).to.throw(/ENOENT/); + }); it('passes ENOENT error to callback when can not find file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - let async = false + const p = path.join(asarDir, 'a.asar', 'not-exist'); + let async = false; fs.readFile(p, function (error) { - expect(async).to.be.true() - expect(error).to.match(/ENOENT/) - }) - async = true - }) + expect(async).to.be.true(); + expect(error).to.match(/ENOENT/); + }); + async = true; + }); it('reads a normal file with unpacked files', function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - expect(fs.readFileSync(p).toString().trim()).to.equal('a') - }) + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + expect(fs.readFileSync(p).toString().trim()).to.equal('a'); + }); it('reads a file in filesystem', function () { - const p = path.resolve(asarDir, 'file') - expect(fs.readFileSync(p).toString().trim()).to.equal('file') - }) - }) + const p = path.resolve(asarDir, 'file'); + expect(fs.readFileSync(p).toString().trim()).to.equal('file'); + }); + }); describe('fs.readFile', function () { it('reads a normal file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); fs.readFile(p, function (err, content) { - expect(err).to.be.null() - expect(String(content).trim()).to.equal('file1') - done() - }) - }) + expect(err).to.be.null(); + expect(String(content).trim()).to.equal('file1'); + done(); + }); + }); it('reads from a empty file', function (done) { - const p = path.join(asarDir, 'empty.asar', 'file1') + const p = path.join(asarDir, 'empty.asar', 'file1'); fs.readFile(p, function (err, content) { - expect(err).to.be.null() - expect(String(content)).to.equal('') - done() - }) - }) + expect(err).to.be.null(); + expect(String(content)).to.equal(''); + done(); + }); + }); it('reads from a empty file with encoding', function (done) { - const p = path.join(asarDir, 'empty.asar', 'file1') + const p = path.join(asarDir, 'empty.asar', 'file1'); fs.readFile(p, 'utf8', function (err, content) { - expect(err).to.be.null() - expect(content).to.equal('') - done() - }) - }) + expect(err).to.be.null(); + expect(content).to.equal(''); + done(); + }); + }); it('reads a linked file', function (done) { - const p = path.join(asarDir, 'a.asar', 'link1') + const p = path.join(asarDir, 'a.asar', 'link1'); fs.readFile(p, function (err, content) { - expect(err).to.be.null() - expect(String(content).trim()).to.equal('file1') - done() - }) - }) + expect(err).to.be.null(); + expect(String(content).trim()).to.equal('file1'); + done(); + }); + }); it('reads a file from linked directory', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') + const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); fs.readFile(p, function (err, content) { - expect(err).to.be.null() - expect(String(content).trim()).to.equal('file1') - done() - }) - }) + expect(err).to.be.null(); + expect(String(content).trim()).to.equal('file1'); + done(); + }); + }); it('throws ENOENT error when can not find file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); fs.readFile(p, function (err) { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.promises.readFile', function () { it('reads a normal file', async function () { - const p = path.join(asarDir, 'a.asar', 'file1') - const content = await fs.promises.readFile(p) - expect(String(content).trim()).to.equal('file1') - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + const content = await fs.promises.readFile(p); + expect(String(content).trim()).to.equal('file1'); + }); it('reads from a empty file', async function () { - const p = path.join(asarDir, 'empty.asar', 'file1') - const content = await fs.promises.readFile(p) - expect(String(content)).to.equal('') - }) + const p = path.join(asarDir, 'empty.asar', 'file1'); + const content = await fs.promises.readFile(p); + expect(String(content)).to.equal(''); + }); it('reads from a empty file with encoding', async function () { - const p = path.join(asarDir, 'empty.asar', 'file1') - const content = await fs.promises.readFile(p, 'utf8') - expect(content).to.equal('') - }) + const p = path.join(asarDir, 'empty.asar', 'file1'); + const content = await fs.promises.readFile(p, 'utf8'); + expect(content).to.equal(''); + }); it('reads a linked file', async function () { - const p = path.join(asarDir, 'a.asar', 'link1') - const content = await fs.promises.readFile(p) - expect(String(content).trim()).to.equal('file1') - }) + const p = path.join(asarDir, 'a.asar', 'link1'); + const content = await fs.promises.readFile(p); + expect(String(content).trim()).to.equal('file1'); + }); it('reads a file from linked directory', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') - const content = await fs.promises.readFile(p) - expect(String(content).trim()).to.equal('file1') - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); + const content = await fs.promises.readFile(p); + expect(String(content).trim()).to.equal('file1'); + }); it('throws ENOENT error when can not find file', async function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.readFile(p), 'ENOENT') - }) - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.readFile(p), 'ENOENT'); + }); + }); describe('fs.copyFile', function () { it('copies a normal file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') - const dest = temp.path() + const p = path.join(asarDir, 'a.asar', 'file1'); + const dest = temp.path(); fs.copyFile(p, dest, function (err) { - expect(err).to.be.null() - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - done() - }) - }) + expect(err).to.be.null(); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + done(); + }); + }); it('copies a unpacked file', function (done) { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - const dest = temp.path() + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + const dest = temp.path(); fs.copyFile(p, dest, function (err) { - expect(err).to.be.null() - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - done() - }) - }) - }) + expect(err).to.be.null(); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + done(); + }); + }); + }); describe('fs.promises.copyFile', function () { it('copies a normal file', async function () { - const p = path.join(asarDir, 'a.asar', 'file1') - const dest = temp.path() - await fs.promises.copyFile(p, dest) - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + const dest = temp.path(); + await fs.promises.copyFile(p, dest); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + }); it('copies a unpacked file', async function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - const dest = temp.path() - await fs.promises.copyFile(p, dest) - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - }) - }) + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + const dest = temp.path(); + await fs.promises.copyFile(p, dest); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + }); + }); describe('fs.copyFileSync', function () { it('copies a normal file', function () { - const p = path.join(asarDir, 'a.asar', 'file1') - const dest = temp.path() - fs.copyFileSync(p, dest) - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + const dest = temp.path(); + fs.copyFileSync(p, dest); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + }); it('copies a unpacked file', function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - const dest = temp.path() - fs.copyFileSync(p, dest) - expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true() - }) - }) + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + const dest = temp.path(); + fs.copyFileSync(p, dest); + expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true(); + }); + }); describe('fs.lstatSync', function () { it('handles path with trailing slash correctly', function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') - fs.lstatSync(p) - fs.lstatSync(p + '/') - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); + fs.lstatSync(p); + fs.lstatSync(p + '/'); + }); it('returns information of root', function () { - const p = path.join(asarDir, 'a.asar') - const stats = fs.lstatSync(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar'); + const stats = fs.lstatSync(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + }); it('returns information of root with stats as bigint', function () { - const p = path.join(asarDir, 'a.asar') - const stats = fs.lstatSync(p, { bigint: false }) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar'); + const stats = fs.lstatSync(p, { bigint: false }); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + }); it('returns information of a normal file', function () { - const ref2 = ['file1', 'file2', 'file3', path.join('dir1', 'file1'), path.join('link2', 'file1')] + const ref2 = ['file1', 'file2', 'file3', path.join('dir1', 'file1'), path.join('link2', 'file1')]; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) - const stats = fs.lstatSync(p) - expect(stats.isFile()).to.be.true() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(6) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); + const stats = fs.lstatSync(p); + expect(stats.isFile()).to.be.true(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(6); } - }) + }); it('returns information of a normal directory', function () { - const ref2 = ['dir1', 'dir2', 'dir3'] + const ref2 = ['dir1', 'dir2', 'dir3']; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) - const stats = fs.lstatSync(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); + const stats = fs.lstatSync(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); } - }) + }); it('returns information of a linked file', function () { - const ref2 = ['link1', path.join('dir1', 'link1'), path.join('link2', 'link2')] + const ref2 = ['link1', path.join('dir1', 'link1'), path.join('link2', 'link2')]; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) - const stats = fs.lstatSync(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); + const stats = fs.lstatSync(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); } - }) + }); it('returns information of a linked directory', function () { - const ref2 = ['link2', path.join('dir1', 'link2'), path.join('link2', 'link2')] + const ref2 = ['link2', path.join('dir1', 'link2'), path.join('link2', 'link2')]; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) - const stats = fs.lstatSync(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); + const stats = fs.lstatSync(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); } - }) + }); it('throws ENOENT error when can not find file', function () { - const ref2 = ['file4', 'file5', path.join('dir1', 'file4')] + const ref2 = ['file4', 'file5', path.join('dir1', 'file4')]; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); expect(() => { - fs.lstatSync(p) - }).to.throw(/ENOENT/) + fs.lstatSync(p); + }).to.throw(/ENOENT/); } - }) - }) + }); + }); describe('fs.lstat', function () { it('handles path with trailing slash correctly', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') - fs.lstat(p + '/', done) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); + fs.lstat(p + '/', done); + }); it('returns information of root', function (done) { - const p = path.join(asarDir, 'a.asar') + const p = path.join(asarDir, 'a.asar'); fs.lstat(p, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + done(); + }); + }); it('returns information of root with stats as bigint', function (done) { - const p = path.join(asarDir, 'a.asar') + const p = path.join(asarDir, 'a.asar'); fs.lstat(p, { bigint: false }, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + done(); + }); + }); it('returns information of a normal file', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'file1') + const p = path.join(asarDir, 'a.asar', 'link2', 'file1'); fs.lstat(p, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.true() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(6) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.true(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(6); + done(); + }); + }); it('returns information of a normal directory', function (done) { - const p = path.join(asarDir, 'a.asar', 'dir1') + const p = path.join(asarDir, 'a.asar', 'dir1'); fs.lstat(p, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + done(); + }); + }); it('returns information of a linked file', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'link1') + const p = path.join(asarDir, 'a.asar', 'link2', 'link1'); fs.lstat(p, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); + done(); + }); + }); it('returns information of a linked directory', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2') + const p = path.join(asarDir, 'a.asar', 'link2', 'link2'); fs.lstat(p, function (err, stats) { - expect(err).to.be.null() - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) - done() - }) - }) + expect(err).to.be.null(); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); + done(); + }); + }); it('throws ENOENT error when can not find file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file4') + const p = path.join(asarDir, 'a.asar', 'file4'); fs.lstat(p, function (err) { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.promises.lstat', function () { it('handles path with trailing slash correctly', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1') - await fs.promises.lstat(p + '/') - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1'); + await fs.promises.lstat(p + '/'); + }); it('returns information of root', async function () { - const p = path.join(asarDir, 'a.asar') - const stats = await fs.promises.lstat(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar'); + const stats = await fs.promises.lstat(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + }); it('returns information of root with stats as bigint', async function () { - const p = path.join(asarDir, 'a.asar') - const stats = await fs.promises.lstat(p, { bigint: false }) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar'); + const stats = await fs.promises.lstat(p, { bigint: false }); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + }); it('returns information of a normal file', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'file1') - const stats = await fs.promises.lstat(p) - expect(stats.isFile()).to.be.true() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(6) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'file1'); + const stats = await fs.promises.lstat(p); + expect(stats.isFile()).to.be.true(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(6); + }); it('returns information of a normal directory', async function () { - const p = path.join(asarDir, 'a.asar', 'dir1') - const stats = await fs.promises.lstat(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.true() - expect(stats.isSymbolicLink()).to.be.false() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar', 'dir1'); + const stats = await fs.promises.lstat(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.true(); + expect(stats.isSymbolicLink()).to.be.false(); + expect(stats.size).to.equal(0); + }); it('returns information of a linked file', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link1') - const stats = await fs.promises.lstat(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link1'); + const stats = await fs.promises.lstat(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); + }); it('returns information of a linked directory', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2') - const stats = await fs.promises.lstat(p) - expect(stats.isFile()).to.be.false() - expect(stats.isDirectory()).to.be.false() - expect(stats.isSymbolicLink()).to.be.true() - expect(stats.size).to.equal(0) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2'); + const stats = await fs.promises.lstat(p); + expect(stats.isFile()).to.be.false(); + expect(stats.isDirectory()).to.be.false(); + expect(stats.isSymbolicLink()).to.be.true(); + expect(stats.size).to.equal(0); + }); it('throws ENOENT error when can not find file', async function () { - const p = path.join(asarDir, 'a.asar', 'file4') - await expectToThrowErrorWithCode(() => fs.promises.lstat(p), 'ENOENT') - }) - }) + const p = path.join(asarDir, 'a.asar', 'file4'); + await expectToThrowErrorWithCode(() => fs.promises.lstat(p), 'ENOENT'); + }); + }); describe('fs.realpathSync', () => { it('returns real path root', () => { - const parent = fs.realpathSync(asarDir) - const p = 'a.asar' - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = 'a.asar'; + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal file', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'file1') - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'file1'); + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal directory', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'dir1') - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'dir1'); + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a linked file', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link1') - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'file1')) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link1'); + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'file1')); + }); it('returns real path of a linked directory', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link2') - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link2'); + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')); + }); it('returns real path of an unpacked file', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('unpack.asar', 'a.txt') - const r = fs.realpathSync(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('unpack.asar', 'a.txt'); + const r = fs.realpathSync(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('throws ENOENT error when can not find file', () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'not-exist') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'not-exist'); expect(() => { - fs.realpathSync(path.join(parent, p)) - }).to.throw(/ENOENT/) - }) - }) + fs.realpathSync(path.join(parent, p)); + }).to.throw(/ENOENT/); + }); + }); describe('fs.realpathSync.native', () => { it('returns real path root', () => { - const parent = fs.realpathSync.native(asarDir) - const p = 'a.asar' - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync.native(asarDir); + const p = 'a.asar'; + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal file', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'file1') - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'file1'); + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal directory', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'dir1') - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'dir1'); + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a linked file', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'link2', 'link1') - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'file1')) - }) + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'link2', 'link1'); + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'file1')); + }); it('returns real path of a linked directory', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'link2', 'link2') - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')) - }) + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'link2', 'link2'); + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')); + }); it('returns real path of an unpacked file', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('unpack.asar', 'a.txt') - const r = fs.realpathSync.native(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync.native(asarDir); + const p = path.join('unpack.asar', 'a.txt'); + const r = fs.realpathSync.native(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('throws ENOENT error when can not find file', () => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'not-exist') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'not-exist'); expect(() => { - fs.realpathSync.native(path.join(parent, p)) - }).to.throw(/ENOENT/) - }) - }) + fs.realpathSync.native(path.join(parent, p)); + }).to.throw(/ENOENT/); + }); + }); describe('fs.realpath', () => { it('returns real path root', done => { - const parent = fs.realpathSync(asarDir) - const p = 'a.asar' + const parent = fs.realpathSync(asarDir); + const p = 'a.asar'; fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a normal file', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'file1') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'file1'); fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a normal directory', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'dir1') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'dir1'); fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a linked file', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link1') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link1'); fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, 'a.asar', 'file1')) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, 'a.asar', 'file1')); + done(); + }); + }); it('returns real path of a linked directory', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link2') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link2'); fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')); + done(); + }); + }); it('returns real path of an unpacked file', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('unpack.asar', 'a.txt') + const parent = fs.realpathSync(asarDir); + const p = path.join('unpack.asar', 'a.txt'); fs.realpath(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('throws ENOENT error when can not find file', done => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'not-exist') + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'not-exist'); fs.realpath(path.join(parent, p), err => { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.promises.realpath', () => { it('returns real path root', async () => { - const parent = fs.realpathSync(asarDir) - const p = 'a.asar' - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = 'a.asar'; + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal file', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'file1') - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'file1'); + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a normal directory', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'dir1') - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'dir1'); + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('returns real path of a linked file', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link1') - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'file1')) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link1'); + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'file1')); + }); it('returns real path of a linked directory', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'link2', 'link2') - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'link2', 'link2'); + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')); + }); it('returns real path of an unpacked file', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('unpack.asar', 'a.txt') - const r = await fs.promises.realpath(path.join(parent, p)) - expect(r).to.equal(path.join(parent, p)) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('unpack.asar', 'a.txt'); + const r = await fs.promises.realpath(path.join(parent, p)); + expect(r).to.equal(path.join(parent, p)); + }); it('throws ENOENT error when can not find file', async () => { - const parent = fs.realpathSync(asarDir) - const p = path.join('a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.realpath(path.join(parent, p)), 'ENOENT') - }) - }) + const parent = fs.realpathSync(asarDir); + const p = path.join('a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.realpath(path.join(parent, p)), 'ENOENT'); + }); + }); describe('fs.realpath.native', () => { it('returns real path root', done => { - const parent = fs.realpathSync.native(asarDir) - const p = 'a.asar' + const parent = fs.realpathSync.native(asarDir); + const p = 'a.asar'; fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a normal file', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'file1') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'file1'); fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a normal directory', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'dir1') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'dir1'); fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('returns real path of a linked file', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'link2', 'link1') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'link2', 'link1'); fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, 'a.asar', 'file1')) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, 'a.asar', 'file1')); + done(); + }); + }); it('returns real path of a linked directory', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'link2', 'link2') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'link2', 'link2'); fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, 'a.asar', 'dir1')); + done(); + }); + }); it('returns real path of an unpacked file', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('unpack.asar', 'a.txt') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('unpack.asar', 'a.txt'); fs.realpath.native(path.join(parent, p), (err, r) => { - expect(err).to.be.null() - expect(r).to.equal(path.join(parent, p)) - done() - }) - }) + expect(err).to.be.null(); + expect(r).to.equal(path.join(parent, p)); + done(); + }); + }); it('throws ENOENT error when can not find file', done => { - const parent = fs.realpathSync.native(asarDir) - const p = path.join('a.asar', 'not-exist') + const parent = fs.realpathSync.native(asarDir); + const p = path.join('a.asar', 'not-exist'); fs.realpath.native(path.join(parent, p), err => { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.readdirSync', function () { it('reads dirs from root', function () { - const p = path.join(asarDir, 'a.asar') - const dirs = fs.readdirSync(p) - expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']) - }) + const p = path.join(asarDir, 'a.asar'); + const dirs = fs.readdirSync(p); + expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']); + }); it('reads dirs from a normal dir', function () { - const p = path.join(asarDir, 'a.asar', 'dir1') - const dirs = fs.readdirSync(p) - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - }) + const p = path.join(asarDir, 'a.asar', 'dir1'); + const dirs = fs.readdirSync(p); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + }); it('reads dirs from a linked dir', function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2') - const dirs = fs.readdirSync(p) - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2'); + const dirs = fs.readdirSync(p); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + }); it('throws ENOENT error when can not find file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - fs.readdirSync(p) - }).to.throw(/ENOENT/) - }) - }) + fs.readdirSync(p); + }).to.throw(/ENOENT/); + }); + }); describe('fs.readdir', function () { it('reads dirs from root', function (done) { - const p = path.join(asarDir, 'a.asar') + const p = path.join(asarDir, 'a.asar'); fs.readdir(p, function (err, dirs) { - expect(err).to.be.null() - expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']) - done() - }) - }) + expect(err).to.be.null(); + expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']); + done(); + }); + }); it('reads dirs from a normal dir', function (done) { - const p = path.join(asarDir, 'a.asar', 'dir1') + const p = path.join(asarDir, 'a.asar', 'dir1'); fs.readdir(p, function (err, dirs) { - expect(err).to.be.null() - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - done() - }) - }) + expect(err).to.be.null(); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + done(); + }); + }); it('reads dirs from a linked dir', function (done) { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2') + const p = path.join(asarDir, 'a.asar', 'link2', 'link2'); fs.readdir(p, function (err, dirs) { - expect(err).to.be.null() - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - done() - }) - }) + expect(err).to.be.null(); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + done(); + }); + }); it('throws ENOENT error when can not find file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); fs.readdir(p, function (err) { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.promises.readdir', function () { it('reads dirs from root', async function () { - const p = path.join(asarDir, 'a.asar') - const dirs = await fs.promises.readdir(p) - expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']) - }) + const p = path.join(asarDir, 'a.asar'); + const dirs = await fs.promises.readdir(p); + expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']); + }); it('reads dirs from a normal dir', async function () { - const p = path.join(asarDir, 'a.asar', 'dir1') - const dirs = await fs.promises.readdir(p) - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - }) + const p = path.join(asarDir, 'a.asar', 'dir1'); + const dirs = await fs.promises.readdir(p); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + }); it('reads dirs from a linked dir', async function () { - const p = path.join(asarDir, 'a.asar', 'link2', 'link2') - const dirs = await fs.promises.readdir(p) - expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']) - }) + const p = path.join(asarDir, 'a.asar', 'link2', 'link2'); + const dirs = await fs.promises.readdir(p); + expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']); + }); it('throws ENOENT error when can not find file', async function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.readdir(p), 'ENOENT') - }) - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.readdir(p), 'ENOENT'); + }); + }); describe('fs.openSync', function () { it('opens a normal/linked/under-linked-directory file', function () { - const ref2 = ['file1', 'link1', path.join('link2', 'file1')] + const ref2 = ['file1', 'link1', path.join('link2', 'file1')]; for (let j = 0, len = ref2.length; j < len; j++) { - const file = ref2[j] - const p = path.join(asarDir, 'a.asar', file) - const fd = fs.openSync(p, 'r') - const buffer = Buffer.alloc(6) - fs.readSync(fd, buffer, 0, 6, 0) - expect(String(buffer).trim()).to.equal('file1') - fs.closeSync(fd) + const file = ref2[j]; + const p = path.join(asarDir, 'a.asar', file); + const fd = fs.openSync(p, 'r'); + const buffer = Buffer.alloc(6); + fs.readSync(fd, buffer, 0, 6, 0); + expect(String(buffer).trim()).to.equal('file1'); + fs.closeSync(fd); } - }) + }); it('throws ENOENT error when can not find file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - fs.openSync(p) - }).to.throw(/ENOENT/) - }) - }) + fs.openSync(p); + }).to.throw(/ENOENT/); + }); + }); describe('fs.open', function () { it('opens a normal file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); fs.open(p, 'r', function (err, fd) { - expect(err).to.be.null() - const buffer = Buffer.alloc(6) + expect(err).to.be.null(); + const buffer = Buffer.alloc(6); fs.read(fd, buffer, 0, 6, 0, function (err) { - expect(err).to.be.null() - expect(String(buffer).trim()).to.equal('file1') - fs.close(fd, done) - }) - }) - }) + expect(err).to.be.null(); + expect(String(buffer).trim()).to.equal('file1'); + fs.close(fd, done); + }); + }); + }); it('throws ENOENT error when can not find file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); fs.open(p, 'r', function (err) { - expect(err.code).to.equal('ENOENT') - done() - }) - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); + }); describe('fs.promises.open', function () { it('opens a normal file', async function () { - const p = path.join(asarDir, 'a.asar', 'file1') - const fh = await fs.promises.open(p, 'r') - const buffer = Buffer.alloc(6) - await fh.read(buffer, 0, 6, 0) - expect(String(buffer).trim()).to.equal('file1') - await fh.close() - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + const fh = await fs.promises.open(p, 'r'); + const buffer = Buffer.alloc(6); + await fh.read(buffer, 0, 6, 0); + expect(String(buffer).trim()).to.equal('file1'); + await fh.close(); + }); it('throws ENOENT error when can not find file', async function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.open(p, 'r'), 'ENOENT') - }) - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.open(p, 'r'), 'ENOENT'); + }); + }); describe('fs.mkdir', function () { it('throws error when calling inside asar archive', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); fs.mkdir(p, function (err) { - expect(err.code).to.equal('ENOTDIR') - done() - }) - }) - }) + expect(err.code).to.equal('ENOTDIR'); + done(); + }); + }); + }); describe('fs.promises.mkdir', function () { it('throws error when calling inside asar archive', async function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.mkdir(p), 'ENOTDIR') - }) - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.mkdir(p), 'ENOTDIR'); + }); + }); describe('fs.mkdirSync', function () { it('throws error when calling inside asar archive', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - fs.mkdirSync(p) - }).to.throw(/ENOTDIR/) - }) - }) + fs.mkdirSync(p); + }).to.throw(/ENOTDIR/); + }); + }); describe('fs.exists', function () { it('handles an existing file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); // eslint-disable-next-line fs.exists(p, function (exists) { - expect(exists).to.be.true() - done() - }) - }) + expect(exists).to.be.true(); + done(); + }); + }); it('handles a non-existent file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); // eslint-disable-next-line fs.exists(p, function (exists) { - expect(exists).to.be.false() - done() - }) - }) + expect(exists).to.be.false(); + done(); + }); + }); it('promisified version handles an existing file', (done) => { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); // eslint-disable-next-line util.promisify(fs.exists)(p).then(exists => { - expect(exists).to.be.true() - done() - }) - }) + expect(exists).to.be.true(); + done(); + }); + }); it('promisified version handles a non-existent file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); // eslint-disable-next-line util.promisify(fs.exists)(p).then(exists => { - expect(exists).to.be.false() - done() - }) - }) - }) + expect(exists).to.be.false(); + done(); + }); + }); + }); describe('fs.existsSync', function () { it('handles an existing file', function () { - const p = path.join(asarDir, 'a.asar', 'file1') - expect(fs.existsSync(p)).to.be.true() - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + expect(fs.existsSync(p)).to.be.true(); + }); it('handles a non-existent file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - expect(fs.existsSync(p)).to.be.false() - }) - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + expect(fs.existsSync(p)).to.be.false(); + }); + }); describe('fs.access', function () { it('accesses a normal file', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); fs.access(p, function (err) { - expect(err).to.be.undefined() - done() - }) - }) + expect(err).to.be.undefined(); + done(); + }); + }); it('throws an error when called with write mode', function (done) { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); fs.access(p, fs.constants.R_OK | fs.constants.W_OK, function (err) { - expect(err.code).to.equal('EACCES') - done() - }) - }) + expect(err.code).to.equal('EACCES'); + done(); + }); + }); it('throws an error when called on non-existent file', function (done) { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); fs.access(p, function (err) { - expect(err.code).to.equal('ENOENT') - done() - }) - }) + expect(err.code).to.equal('ENOENT'); + done(); + }); + }); it('allows write mode for unpacked files', function (done) { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); fs.access(p, fs.constants.R_OK | fs.constants.W_OK, function (err) { - expect(err).to.be.null() - done() - }) - }) - }) + expect(err).to.be.null(); + done(); + }); + }); + }); describe('fs.promises.access', function () { it('accesses a normal file', async function () { - const p = path.join(asarDir, 'a.asar', 'file1') - await fs.promises.access(p) - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + await fs.promises.access(p); + }); it('throws an error when called with write mode', async function () { - const p = path.join(asarDir, 'a.asar', 'file1') - await expectToThrowErrorWithCode(() => fs.promises.access(p, fs.constants.R_OK | fs.constants.W_OK), 'EACCES') - }) + const p = path.join(asarDir, 'a.asar', 'file1'); + await expectToThrowErrorWithCode(() => fs.promises.access(p, fs.constants.R_OK | fs.constants.W_OK), 'EACCES'); + }); it('throws an error when called on non-existent file', async function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') - await expectToThrowErrorWithCode(() => fs.promises.access(p), 'ENOENT') - }) + const p = path.join(asarDir, 'a.asar', 'not-exist'); + await expectToThrowErrorWithCode(() => fs.promises.access(p), 'ENOENT'); + }); it('allows write mode for unpacked files', async function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - await fs.promises.access(p, fs.constants.R_OK | fs.constants.W_OK) - }) - }) + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + await fs.promises.access(p, fs.constants.R_OK | fs.constants.W_OK); + }); + }); describe('fs.accessSync', function () { it('accesses a normal file', function () { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); expect(() => { - fs.accessSync(p) - }).to.not.throw() - }) + fs.accessSync(p); + }).to.not.throw(); + }); it('throws an error when called with write mode', function () { - const p = path.join(asarDir, 'a.asar', 'file1') + const p = path.join(asarDir, 'a.asar', 'file1'); expect(() => { - fs.accessSync(p, fs.constants.R_OK | fs.constants.W_OK) - }).to.throw(/EACCES/) - }) + fs.accessSync(p, fs.constants.R_OK | fs.constants.W_OK); + }).to.throw(/EACCES/); + }); it('throws an error when called on non-existent file', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - fs.accessSync(p) - }).to.throw(/ENOENT/) - }) + fs.accessSync(p); + }).to.throw(/ENOENT/); + }); it('allows write mode for unpacked files', function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); expect(() => { - fs.accessSync(p, fs.constants.R_OK | fs.constants.W_OK) - }).to.not.throw() - }) - }) + fs.accessSync(p, fs.constants.R_OK | fs.constants.W_OK); + }).to.not.throw(); + }); + }); describe('child_process.fork', function () { before(function () { if (!features.isRunAsNodeEnabled()) { - this.skip() + this.skip(); } - }) + }); it('opens a normal js file', function (done) { - const child = ChildProcess.fork(path.join(asarDir, 'a.asar', 'ping.js')) + const child = ChildProcess.fork(path.join(asarDir, 'a.asar', 'ping.js')); child.on('message', function (msg) { - expect(msg).to.equal('message') - done() - }) - child.send('message') - }) + expect(msg).to.equal('message'); + done(); + }); + child.send('message'); + }); it('supports asar in the forked js', function (done) { - const file = path.join(asarDir, 'a.asar', 'file1') - const child = ChildProcess.fork(path.join(fixtures, 'module', 'asar.js')) + const file = path.join(asarDir, 'a.asar', 'file1'); + const child = ChildProcess.fork(path.join(fixtures, 'module', 'asar.js')); child.on('message', function (content) { - expect(content).to.equal(fs.readFileSync(file).toString()) - done() - }) - child.send(file) - }) - }) + expect(content).to.equal(fs.readFileSync(file).toString()); + done(); + }); + child.send(file); + }); + }); describe('child_process.exec', function () { - const echo = path.join(asarDir, 'echo.asar', 'echo') + const echo = path.join(asarDir, 'echo.asar', 'echo'); it('should not try to extract the command if there is a reference to a file inside an .asar', function (done) { ChildProcess.exec('echo ' + echo + ' foo bar', function (error, stdout) { - expect(error).to.be.null() - expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n') - done() - }) - }) + expect(error).to.be.null(); + expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n'); + done(); + }); + }); it('can be promisified', () => { return util.promisify(ChildProcess.exec)('echo ' + echo + ' foo bar').then(({ stdout }) => { - expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n') - }) - }) - }) + expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n'); + }); + }); + }); describe('child_process.execSync', function () { - const echo = path.join(asarDir, 'echo.asar', 'echo') + const echo = path.join(asarDir, 'echo.asar', 'echo'); it('should not try to extract the command if there is a reference to a file inside an .asar', function (done) { - const stdout = ChildProcess.execSync('echo ' + echo + ' foo bar') - expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n') - done() - }) - }) + const stdout = ChildProcess.execSync('echo ' + echo + ' foo bar'); + expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n'); + done(); + }); + }); describe('child_process.execFile', function () { - const execFile = ChildProcess.execFile - const execFileSync = ChildProcess.execFileSync - const echo = path.join(asarDir, 'echo.asar', 'echo') + const execFile = ChildProcess.execFile; + const execFileSync = ChildProcess.execFileSync; + const echo = path.join(asarDir, 'echo.asar', 'echo'); before(function () { if (process.platform !== 'darwin') { - this.skip() + this.skip(); } - }) + }); it('executes binaries', function (done) { execFile(echo, ['test'], function (error, stdout) { - expect(error).to.be.null() - expect(stdout).to.equal('test\n') - done() - }) - }) + expect(error).to.be.null(); + expect(stdout).to.equal('test\n'); + done(); + }); + }); it('executes binaries without callback', function (done) { - const process = execFile(echo, ['test']) + const process = execFile(echo, ['test']); process.on('close', function (code) { - expect(code).to.equal(0) - done() - }) + expect(code).to.equal(0); + done(); + }); process.on('error', function () { - expect.fail() - done() - }) - }) + expect.fail(); + done(); + }); + }); it('execFileSync executes binaries', function () { - const output = execFileSync(echo, ['test']) - expect(String(output)).to.equal('test\n') - }) + const output = execFileSync(echo, ['test']); + expect(String(output)).to.equal('test\n'); + }); it('can be promisified', () => { return util.promisify(ChildProcess.execFile)(echo, ['test']).then(({ stdout }) => { - expect(stdout).to.equal('test\n') - }) - }) - }) + expect(stdout).to.equal('test\n'); + }); + }); + }); describe('internalModuleReadJSON', function () { - const internalModuleReadJSON = process.binding('fs').internalModuleReadJSON + const internalModuleReadJSON = process.binding('fs').internalModuleReadJSON; it('read a normal file', function () { - const file1 = path.join(asarDir, 'a.asar', 'file1') - expect(internalModuleReadJSON(file1).toString().trim()).to.equal('file1') - const file2 = path.join(asarDir, 'a.asar', 'file2') - expect(internalModuleReadJSON(file2).toString().trim()).to.equal('file2') - const file3 = path.join(asarDir, 'a.asar', 'file3') - expect(internalModuleReadJSON(file3).toString().trim()).to.equal('file3') - }) + const file1 = path.join(asarDir, 'a.asar', 'file1'); + expect(internalModuleReadJSON(file1).toString().trim()).to.equal('file1'); + const file2 = path.join(asarDir, 'a.asar', 'file2'); + expect(internalModuleReadJSON(file2).toString().trim()).to.equal('file2'); + const file3 = path.join(asarDir, 'a.asar', 'file3'); + expect(internalModuleReadJSON(file3).toString().trim()).to.equal('file3'); + }); it('reads a normal file with unpacked files', function () { - const p = path.join(asarDir, 'unpack.asar', 'a.txt') - expect(internalModuleReadJSON(p).toString().trim()).to.equal('a') - }) - }) + const p = path.join(asarDir, 'unpack.asar', 'a.txt'); + expect(internalModuleReadJSON(p).toString().trim()).to.equal('a'); + }); + }); describe('util.promisify', function () { it('can promisify all fs functions', function () { - const originalFs = require('original-fs') - const { hasOwnProperty } = Object.prototype + const originalFs = require('original-fs'); + const { hasOwnProperty } = Object.prototype; for (const [propertyName, originalValue] of Object.entries(originalFs)) { // Some properties exist but have a value of `undefined` on some platforms. // E.g. `fs.lchmod`, which in only available on MacOS, see // https://nodejs.org/docs/latest-v10.x/api/fs.html#fs_fs_lchmod_path_mode_callback // Also check for `null`s, `hasOwnProperty()` can't handle them. - if (typeof originalValue === 'undefined' || originalValue === null) continue + if (typeof originalValue === 'undefined' || originalValue === null) continue; if (hasOwnProperty.call(originalValue, util.promisify.custom)) { expect(fs).to.have.own.property(propertyName) - .that.has.own.property(util.promisify.custom) + .that.has.own.property(util.promisify.custom); } } - }) - }) + }); + }); describe('process.noAsar', function () { - const errorName = process.platform === 'win32' ? 'ENOENT' : 'ENOTDIR' + const errorName = process.platform === 'win32' ? 'ENOENT' : 'ENOTDIR'; beforeEach(function () { - process.noAsar = true - }) + process.noAsar = true; + }); afterEach(function () { - process.noAsar = false - }) + process.noAsar = false; + }); it('disables asar support in sync API', function () { - const file = path.join(asarDir, 'a.asar', 'file1') - const dir = path.join(asarDir, 'a.asar', 'dir1') + const file = path.join(asarDir, 'a.asar', 'file1'); + const dir = path.join(asarDir, 'a.asar', 'dir1'); expect(() => { - fs.readFileSync(file) - }).to.throw(new RegExp(errorName)) + fs.readFileSync(file); + }).to.throw(new RegExp(errorName)); expect(() => { - fs.lstatSync(file) - }).to.throw(new RegExp(errorName)) + fs.lstatSync(file); + }).to.throw(new RegExp(errorName)); expect(() => { - fs.realpathSync(file) - }).to.throw(new RegExp(errorName)) + fs.realpathSync(file); + }).to.throw(new RegExp(errorName)); expect(() => { - fs.readdirSync(dir) - }).to.throw(new RegExp(errorName)) - }) + fs.readdirSync(dir); + }).to.throw(new RegExp(errorName)); + }); it('disables asar support in async API', function (done) { - const file = path.join(asarDir, 'a.asar', 'file1') - const dir = path.join(asarDir, 'a.asar', 'dir1') + const file = path.join(asarDir, 'a.asar', 'file1'); + const dir = path.join(asarDir, 'a.asar', 'dir1'); fs.readFile(file, function (error) { - expect(error.code).to.equal(errorName) + expect(error.code).to.equal(errorName); fs.lstat(file, function (error) { - expect(error.code).to.equal(errorName) + expect(error.code).to.equal(errorName); fs.realpath(file, function (error) { - expect(error.code).to.equal(errorName) + expect(error.code).to.equal(errorName); fs.readdir(dir, function (error) { - expect(error.code).to.equal(errorName) - done() - }) - }) - }) - }) - }) + expect(error.code).to.equal(errorName); + done(); + }); + }); + }); + }); + }); it('disables asar support in promises API', async function () { - const file = path.join(asarDir, 'a.asar', 'file1') - const dir = path.join(asarDir, 'a.asar', 'dir1') - await expect(fs.promises.readFile(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)) - await expect(fs.promises.lstat(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)) - await expect(fs.promises.realpath(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)) - await expect(fs.promises.readdir(dir)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)) - }) + const file = path.join(asarDir, 'a.asar', 'file1'); + const dir = path.join(asarDir, 'a.asar', 'dir1'); + await expect(fs.promises.readFile(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)); + await expect(fs.promises.lstat(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)); + await expect(fs.promises.realpath(file)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)); + await expect(fs.promises.readdir(dir)).to.be.eventually.rejectedWith(Error, new RegExp(errorName)); + }); it('treats *.asar as normal file', function () { - const originalFs = require('original-fs') - const asar = path.join(asarDir, 'a.asar') - const content1 = fs.readFileSync(asar) - const content2 = originalFs.readFileSync(asar) - expect(content1.compare(content2)).to.equal(0) + const originalFs = require('original-fs'); + const asar = path.join(asarDir, 'a.asar'); + const content1 = fs.readFileSync(asar); + const content2 = originalFs.readFileSync(asar); + expect(content1.compare(content2)).to.equal(0); expect(() => { - fs.readdirSync(asar) - }).to.throw(/ENOTDIR/) - }) + fs.readdirSync(asar); + }).to.throw(/ENOTDIR/); + }); it('is reset to its original value when execSync throws an error', function () { - process.noAsar = false + process.noAsar = false; expect(() => { - ChildProcess.execSync(path.join(__dirname, 'does-not-exist.txt')) - }).to.throw() - expect(process.noAsar).to.be.false() - }) - }) + ChildProcess.execSync(path.join(__dirname, 'does-not-exist.txt')); + }).to.throw(); + expect(process.noAsar).to.be.false(); + }); + }); describe('process.env.ELECTRON_NO_ASAR', function () { before(function () { if (!features.isRunAsNodeEnabled()) { - this.skip() + this.skip(); } - }) + }); it('disables asar support in forked processes', function (done) { const forked = ChildProcess.fork(path.join(__dirname, 'fixtures', 'module', 'no-asar.js'), [], { env: { ELECTRON_NO_ASAR: true } - }) + }); forked.on('message', function (stats) { - expect(stats.isFile).to.be.true() - expect(stats.size).to.equal(778) - done() - }) - }) + expect(stats.isFile).to.be.true(); + expect(stats.size).to.equal(778); + done(); + }); + }); it('disables asar support in spawned processes', function (done) { const spawned = ChildProcess.spawn(process.execPath, [path.join(__dirname, 'fixtures', 'module', 'no-asar.js')], { @@ -1331,140 +1331,140 @@ describe('asar package', function () { ELECTRON_NO_ASAR: true, ELECTRON_RUN_AS_NODE: true } - }) + }); - let output = '' + let output = ''; spawned.stdout.on('data', function (data) { - output += data - }) + output += data; + }); spawned.stdout.on('close', function () { - const stats = JSON.parse(output) - expect(stats.isFile).to.be.true() - expect(stats.size).to.equal(778) - done() - }) - }) - }) - }) + const stats = JSON.parse(output); + expect(stats.isFile).to.be.true(); + expect(stats.size).to.equal(778); + done(); + }); + }); + }); + }); describe('asar protocol', function () { it('can request a file in package', function (done) { - const p = path.resolve(asarDir, 'a.asar', 'file1') + const p = path.resolve(asarDir, 'a.asar', 'file1'); $.get('file://' + p, function (data) { - expect(data.trim()).to.equal('file1') - done() - }) - }) + expect(data.trim()).to.equal('file1'); + done(); + }); + }); it('can request a file in package with unpacked files', function (done) { - const p = path.resolve(asarDir, 'unpack.asar', 'a.txt') + const p = path.resolve(asarDir, 'unpack.asar', 'a.txt'); $.get('file://' + p, function (data) { - expect(data.trim()).to.equal('a') - done() - }) - }) + expect(data.trim()).to.equal('a'); + done(); + }); + }); it('can request a linked file in package', function (done) { - const p = path.resolve(asarDir, 'a.asar', 'link2', 'link1') + const p = path.resolve(asarDir, 'a.asar', 'link2', 'link1'); $.get('file://' + p, function (data) { - expect(data.trim()).to.equal('file1') - done() - }) - }) + expect(data.trim()).to.equal('file1'); + done(); + }); + }); it('can request a file in filesystem', function (done) { - const p = path.resolve(asarDir, 'file') + const p = path.resolve(asarDir, 'file'); $.get('file://' + p, function (data) { - expect(data.trim()).to.equal('file') - done() - }) - }) + expect(data.trim()).to.equal('file'); + done(); + }); + }); it('gets 404 when file is not found', function (done) { - const p = path.resolve(asarDir, 'a.asar', 'no-exist') + const p = path.resolve(asarDir, 'a.asar', 'no-exist'); $.ajax({ url: 'file://' + p, error: function (err) { - expect(err.status).to.equal(404) - done() + expect(err.status).to.equal(404); + done(); } - }) - }) - }) + }); + }); + }); describe('original-fs module', function () { - const originalFs = require('original-fs') + const originalFs = require('original-fs'); it('treats .asar as file', function () { - const file = path.join(asarDir, 'a.asar') - const stats = originalFs.statSync(file) - expect(stats.isFile()).to.be.true() - }) + const file = path.join(asarDir, 'a.asar'); + const stats = originalFs.statSync(file); + expect(stats.isFile()).to.be.true(); + }); it('is available in forked scripts', function (done) { if (!features.isRunAsNodeEnabled()) { - this.skip() - done() + this.skip(); + done(); } - const child = ChildProcess.fork(path.join(fixtures, 'module', 'original-fs.js')) + const child = ChildProcess.fork(path.join(fixtures, 'module', 'original-fs.js')); child.on('message', function (msg) { - expect(msg).to.equal('object') - done() - }) - child.send('message') - }) + expect(msg).to.equal('object'); + done(); + }); + child.send('message'); + }); it('can be used with streams', () => { - originalFs.createReadStream(path.join(asarDir, 'a.asar')) - }) + originalFs.createReadStream(path.join(asarDir, 'a.asar')); + }); it('has the same APIs as fs', function () { - expect(Object.keys(require('fs'))).to.deep.equal(Object.keys(require('original-fs'))) - expect(Object.keys(require('fs').promises)).to.deep.equal(Object.keys(require('original-fs').promises)) - }) - }) + expect(Object.keys(require('fs'))).to.deep.equal(Object.keys(require('original-fs'))); + expect(Object.keys(require('fs').promises)).to.deep.equal(Object.keys(require('original-fs').promises)); + }); + }); describe('graceful-fs module', function () { - const gfs = require('graceful-fs') + const gfs = require('graceful-fs'); it('recognize asar archvies', function () { - const p = path.join(asarDir, 'a.asar', 'link1') - expect(gfs.readFileSync(p).toString().trim()).to.equal('file1') - }) + const p = path.join(asarDir, 'a.asar', 'link1'); + expect(gfs.readFileSync(p).toString().trim()).to.equal('file1'); + }); it('does not touch global fs object', function () { - expect(fs.readdir).to.not.equal(gfs.readdir) - }) - }) + expect(fs.readdir).to.not.equal(gfs.readdir); + }); + }); describe('mkdirp module', function () { - const mkdirp = require('mkdirp') + const mkdirp = require('mkdirp'); it('throws error when calling inside asar archive', function () { - const p = path.join(asarDir, 'a.asar', 'not-exist') + const p = path.join(asarDir, 'a.asar', 'not-exist'); expect(() => { - mkdirp.sync(p) - }).to.throw(/ENOTDIR/) - }) - }) + mkdirp.sync(p); + }).to.throw(/ENOTDIR/); + }); + }); describe('native-image', function () { it('reads image from asar archive', function () { - const p = path.join(asarDir, 'logo.asar', 'logo.png') - const logo = nativeImage.createFromPath(p) + const p = path.join(asarDir, 'logo.asar', 'logo.png'); + const logo = nativeImage.createFromPath(p); expect(logo.getSize()).to.deep.equal({ width: 55, height: 55 - }) - }) + }); + }); it('reads image from asar archive with unpacked files', function () { - const p = path.join(asarDir, 'unpack.asar', 'atom.png') - const logo = nativeImage.createFromPath(p) + const p = path.join(asarDir, 'unpack.asar', 'atom.png'); + const logo = nativeImage.createFromPath(p); expect(logo.getSize()).to.deep.equal({ width: 1024, height: 1024 - }) - }) - }) -}) + }); + }); + }); +}); diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index 465948d13d006..392c08bbcbf6f 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -1,38 +1,39 @@ -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const fs = require('fs') -const http = require('http') -const path = require('path') -const ws = require('ws') -const url = require('url') -const ChildProcess = require('child_process') -const { ipcRenderer } = require('electron') -const { emittedOnce } = require('./events-helpers') -const { resolveGetters } = require('./expect-helpers') -const features = process.electronBinding('features') - -const { expect } = chai -chai.use(dirtyChai) +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const fs = require('fs'); +const http = require('http'); +const path = require('path'); +const ws = require('ws'); +const url = require('url'); +const ChildProcess = require('child_process'); +const { ipcRenderer } = require('electron'); +const { emittedOnce } = require('./events-helpers'); +const { resolveGetters } = require('./expect-helpers'); +const features = process.electronBinding('features'); + +const { expect } = chai; +chai.use(dirtyChai); /* Most of the APIs here don't use standard callbacks */ /* eslint-disable standard/no-callback-literal */ describe('chromium feature', () => { - const fixtures = path.resolve(__dirname, 'fixtures') - let listener = null + const fixtures = path.resolve(__dirname, 'fixtures'); + let listener = null; afterEach(() => { if (listener != null) { - window.removeEventListener('message', listener) + window.removeEventListener('message', listener); } - listener = null - }) + listener = null; + }); - describe('heap snapshot', () => { + // TODO(nornagon): re-enable this once it works again. + xdescribe('heap snapshot', () => { it('does not crash', function () { - process.electronBinding('v8_util').takeHeapSnapshot() - }) - }) + process.electronBinding('v8_util').takeHeapSnapshot(); + }); + }); describe('navigator.webkitGetUserMedia', () => { it('calls its callbacks', (done) => { @@ -40,74 +41,67 @@ describe('chromium feature', () => { audio: true, video: false }, () => done(), - () => done()) - }) - }) + () => done()); + }); + }); describe('navigator.language', () => { it('should not be empty', () => { - expect(navigator.language).to.not.equal('') - }) - }) + expect(navigator.language).to.not.equal(''); + }); + }); describe('navigator.geolocation', () => { before(function () { if (!features.isFakeLocationProviderEnabled()) { - return this.skip() + return this.skip(); } - }) + }); it('returns position when permission is granted', (done) => { navigator.geolocation.getCurrentPosition((position) => { - expect(position).to.have.a.property('coords') - expect(position).to.have.a.property('timestamp') - done() + expect(position).to.have.a.property('coords'); + expect(position).to.have.a.property('timestamp'); + done(); }, (error) => { - done(error) - }) - }) - }) + done(error); + }); + }); + }); describe('window.open', () => { - it('returns a BrowserWindowProxy object', () => { - const b = window.open('about:blank', '', 'show=no') - expect(b.closed).to.be.false() - expect(b.constructor.name).to.equal('BrowserWindowProxy') - b.close() - }) - it('accepts "nodeIntegration" as feature', (done) => { - let b = null + let b = null; listener = (event) => { - expect(event.data.isProcessGlobalUndefined).to.be.true() - b.close() - done() - } - window.addEventListener('message', listener) - b = window.open(`file://${fixtures}/pages/window-opener-node.html`, '', 'nodeIntegration=no,show=no') - }) + expect(event.data.isProcessGlobalUndefined).to.be.true(); + b.close(); + done(); + }; + window.addEventListener('message', listener); + b = window.open(`file://${fixtures}/pages/window-opener-node.html`, '', 'nodeIntegration=no,show=no'); + }); it('inherit options of parent window', (done) => { - let b = null + let b = null; listener = (event) => { - const width = outerWidth - const height = outerHeight - expect(event.data).to.equal(`size: ${width} ${height}`) - b.close() - done() - } - window.addEventListener('message', listener) - b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no') - }) + const width = outerWidth; + const height = outerHeight; + expect(event.data).to.equal(`size: ${width} ${height}`); + b.close(); + done(); + }; + window.addEventListener('message', listener); + b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no'); + }); it('disables node integration when it is disabled on the parent window', (done) => { - let b = null + let b = null; listener = (event) => { - expect(event.data.isProcessGlobalUndefined).to.be.true() - b.close() - done() - } - window.addEventListener('message', listener) + expect(event.data.isProcessGlobalUndefined).to.be.true(); + b.close(); + done(); + }; + window.addEventListener('message', listener); const windowUrl = require('url').format({ pathname: `${fixtures}/pages/window-opener-no-node-integration.html`, @@ -116,18 +110,18 @@ describe('chromium feature', () => { p: `${fixtures}/pages/window-opener-node.html` }, slashes: true - }) - b = window.open(windowUrl, '', 'nodeIntegration=no,show=no') - }) + }); + b = window.open(windowUrl, '', 'nodeIntegration=no,show=no'); + }); it('disables the tag when it is disabled on the parent window', (done) => { - let b = null + let b = null; listener = (event) => { - expect(event.data.isWebViewGlobalUndefined).to.be.true() - b.close() - done() - } - window.addEventListener('message', listener) + expect(event.data.isWebViewGlobalUndefined).to.be.true(); + b.close(); + done(); + }; + window.addEventListener('message', listener); const windowUrl = require('url').format({ pathname: `${fixtures}/pages/window-opener-no-webview-tag.html`, @@ -136,89 +130,89 @@ describe('chromium feature', () => { p: `${fixtures}/pages/window-opener-webview.html` }, slashes: true - }) - b = window.open(windowUrl, '', 'webviewTag=no,nodeIntegration=yes,show=no') - }) + }); + b = window.open(windowUrl, '', 'webviewTag=no,nodeIntegration=yes,show=no'); + }); it('does not override child options', (done) => { - let b = null + let b = null; const size = { width: 350, height: 450 - } + }; listener = (event) => { - expect(event.data).to.equal(`size: ${size.width} ${size.height}`) - b.close() - done() - } - window.addEventListener('message', listener) - b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no,width=' + size.width + ',height=' + size.height) - }) + expect(event.data).to.equal(`size: ${size.width} ${size.height}`); + b.close(); + done(); + }; + window.addEventListener('message', listener); + b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no,width=' + size.width + ',height=' + size.height); + }); it('throws an exception when the arguments cannot be converted to strings', () => { expect(() => { - window.open('', { toString: null }) - }).to.throw('Cannot convert object to primitive value') + window.open('', { toString: null }); + }).to.throw('Cannot convert object to primitive value'); expect(() => { - window.open('', '', { toString: 3 }) - }).to.throw('Cannot convert object to primitive value') - }) + window.open('', '', { toString: 3 }); + }).to.throw('Cannot convert object to primitive value'); + }); it('does not throw an exception when the features include webPreferences', () => { - let b = null + let b = null; expect(() => { - b = window.open('', '', 'webPreferences=') - }).to.not.throw() - b.close() - }) - }) + b = window.open('', '', 'webPreferences='); + }).to.not.throw(); + b.close(); + }); + }); describe('window.opener', () => { it('is not null for window opened by window.open', (done) => { - let b = null + let b = null; listener = (event) => { - expect(event.data).to.equal('object') - b.close() - done() - } - window.addEventListener('message', listener) - b = window.open(`file://${fixtures}/pages/window-opener.html`, '', 'show=no') - }) - }) + expect(event.data).to.equal('object'); + b.close(); + done(); + }; + window.addEventListener('message', listener); + b = window.open(`file://${fixtures}/pages/window-opener.html`, '', 'show=no'); + }); + }); describe('window.postMessage', () => { it('throws an exception when the targetOrigin cannot be converted to a string', () => { - const b = window.open('') + const b = window.open(''); expect(() => { - b.postMessage('test', { toString: null }) - }).to.throw('Cannot convert object to primitive value') - b.close() - }) - }) + b.postMessage('test', { toString: null }); + }).to.throw('Cannot convert object to primitive value'); + b.close(); + }); + }); describe('window.opener.postMessage', () => { it('sets source and origin correctly', (done) => { - let b = null + let b = null; listener = (event) => { - window.removeEventListener('message', listener) - b.close() - expect(event.source).to.equal(b) - expect(event.origin).to.equal('file://') - done() - } - window.addEventListener('message', listener) - b = window.open(`file://${fixtures}/pages/window-opener-postMessage.html`, '', 'show=no') - }) + window.removeEventListener('message', listener); + expect(event.source).to.deep.equal(b); + b.close(); + expect(event.origin).to.equal('file://'); + done(); + }; + window.addEventListener('message', listener); + b = window.open(`file://${fixtures}/pages/window-opener-postMessage.html`, '', 'show=no'); + }); it('supports windows opened from a ', (done) => { - const webview = new WebView() + const webview = new WebView(); webview.addEventListener('console-message', (e) => { - webview.remove() - expect(e.message).to.equal('message') - done() - }) - webview.allowpopups = true + webview.remove(); + expect(e.message).to.equal('message'); + done(); + }); + webview.allowpopups = true; webview.src = url.format({ pathname: `${fixtures}/pages/webview-opener-postMessage.html`, protocol: 'file', @@ -226,287 +220,303 @@ describe('chromium feature', () => { p: `${fixtures}/pages/window-opener-postMessage.html` }, slashes: true - }) - document.body.appendChild(webview) - }) + }); + document.body.appendChild(webview); + }); describe('targetOrigin argument', () => { - let serverURL - let server + let serverURL; + let server; beforeEach((done) => { server = http.createServer((req, res) => { - res.writeHead(200) - const filePath = path.join(fixtures, 'pages', 'window-opener-targetOrigin.html') - res.end(fs.readFileSync(filePath, 'utf8')) - }) + res.writeHead(200); + const filePath = path.join(fixtures, 'pages', 'window-opener-targetOrigin.html'); + res.end(fs.readFileSync(filePath, 'utf8')); + }); server.listen(0, '127.0.0.1', () => { - serverURL = `http://127.0.0.1:${server.address().port}` - done() - }) - }) + serverURL = `http://127.0.0.1:${server.address().port}`; + done(); + }); + }); afterEach(() => { - server.close() - }) + server.close(); + }); it('delivers messages that match the origin', (done) => { - let b = null + let b = null; listener = (event) => { - window.removeEventListener('message', listener) - b.close() - expect(event.data).to.equal('deliver') - done() - } - window.addEventListener('message', listener) - b = window.open(serverURL, '', 'show=no') - }) - }) - }) + window.removeEventListener('message', listener); + b.close(); + expect(event.data).to.equal('deliver'); + done(); + }; + window.addEventListener('message', listener); + b = window.open(serverURL, '', 'show=no'); + }); + }); + }); describe('webgl', () => { before(function () { if (process.platform === 'win32') { - this.skip() + this.skip(); } - }) + }); it('can be get as context in canvas', () => { if (process.platform === 'linux') { // FIXME(alexeykuzmin): Skip the test. // this.skip() - return + return; } - const webgl = document.createElement('canvas').getContext('webgl') - expect(webgl).to.not.be.null() - }) - }) + const webgl = document.createElement('canvas').getContext('webgl'); + expect(webgl).to.not.be.null(); + }); + }); describe('web workers', () => { it('Worker can work', (done) => { - const worker = new Worker('../fixtures/workers/worker.js') - const message = 'ping' + const worker = new Worker('../fixtures/workers/worker.js'); + const message = 'ping'; worker.onmessage = (event) => { - expect(event.data).to.equal(message) - worker.terminate() - done() - } - worker.postMessage(message) - }) + expect(event.data).to.equal(message); + worker.terminate(); + done(); + }; + worker.postMessage(message); + }); it('Worker has no node integration by default', (done) => { - const worker = new Worker('../fixtures/workers/worker_node.js') + const worker = new Worker('../fixtures/workers/worker_node.js'); worker.onmessage = (event) => { - expect(event.data).to.equal('undefined undefined undefined undefined') - worker.terminate() - done() - } - }) + expect(event.data).to.equal('undefined undefined undefined undefined'); + worker.terminate(); + done(); + }; + }); it('Worker has node integration with nodeIntegrationInWorker', (done) => { - const webview = new WebView() + const webview = new WebView(); webview.addEventListener('ipc-message', (e) => { - expect(e.channel).to.equal('object function object function') - webview.remove() - done() - }) - webview.src = `file://${fixtures}/pages/worker.html` - webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker') - document.body.appendChild(webview) - }) + expect(e.channel).to.equal('object function object function'); + webview.remove(); + done(); + }); + webview.src = `file://${fixtures}/pages/worker.html`; + webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker'); + document.body.appendChild(webview); + }); // FIXME: disabled during chromium update due to crash in content::WorkerScriptFetchInitiator::CreateScriptLoaderOnIO xdescribe('SharedWorker', () => { it('can work', (done) => { - const worker = new SharedWorker('../fixtures/workers/shared_worker.js') - const message = 'ping' + const worker = new SharedWorker('../fixtures/workers/shared_worker.js'); + const message = 'ping'; worker.port.onmessage = (event) => { - expect(event.data).to.equal(message) - done() - } - worker.port.postMessage(message) - }) + expect(event.data).to.equal(message); + done(); + }; + worker.port.postMessage(message); + }); it('has no node integration by default', (done) => { - const worker = new SharedWorker('../fixtures/workers/shared_worker_node.js') + const worker = new SharedWorker('../fixtures/workers/shared_worker_node.js'); worker.port.onmessage = (event) => { - expect(event.data).to.equal('undefined undefined undefined undefined') - done() - } - }) + expect(event.data).to.equal('undefined undefined undefined undefined'); + done(); + }; + }); it('has node integration with nodeIntegrationInWorker', (done) => { - const webview = new WebView() + const webview = new WebView(); webview.addEventListener('console-message', (e) => { - console.log(e) - }) + console.log(e); + }); webview.addEventListener('ipc-message', (e) => { - expect(e.channel).to.equal('object function object function') - webview.remove() - done() - }) - webview.src = `file://${fixtures}/pages/shared_worker.html` - webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker') - document.body.appendChild(webview) - }) - }) - }) + expect(e.channel).to.equal('object function object function'); + webview.remove(); + done(); + }); + webview.src = `file://${fixtures}/pages/shared_worker.html`; + webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker'); + document.body.appendChild(webview); + }); + }); + }); describe('iframe', () => { - let iframe = null + let iframe = null; beforeEach(() => { - iframe = document.createElement('iframe') - }) + iframe = document.createElement('iframe'); + }); afterEach(() => { - document.body.removeChild(iframe) - }) + document.body.removeChild(iframe); + }); it('does not have node integration', (done) => { - iframe.src = `file://${fixtures}/pages/set-global.html` - document.body.appendChild(iframe) + iframe.src = `file://${fixtures}/pages/set-global.html`; + document.body.appendChild(iframe); iframe.onload = () => { - expect(iframe.contentWindow.test).to.equal('undefined undefined undefined') - done() - } - }) - }) + expect(iframe.contentWindow.test).to.equal('undefined undefined undefined'); + done(); + }; + }); + }); describe('storage', () => { - describe('DOM storage quota override', () => { + describe('DOM storage quota increase', () => { ['localStorage', 'sessionStorage'].forEach((storageName) => { - it(`allows saving at least 50MiB in ${storageName}`, () => { - const storage = window[storageName] - const testKeyName = '_electronDOMStorageQuotaOverrideTest' - // 25 * 2^20 UTF-16 characters will require 50MiB - const arraySize = 25 * Math.pow(2, 20) - storage[testKeyName] = new Array(arraySize).fill('X').join('') - expect(storage[testKeyName]).to.have.lengthOf(arraySize) - delete storage[testKeyName] - }) - }) - }) + const storage = window[storageName]; + it(`allows saving at least 40MiB in ${storageName}`, (done) => { + // Although JavaScript strings use UTF-16, the underlying + // storage provider may encode strings differently, muddling the + // translation between character and byte counts. However, + // a string of 40 * 2^20 characters will require at least 40MiB + // and presumably no more than 80MiB, a size guaranteed to + // to exceed the original 10MiB quota yet stay within the + // new 100MiB quota. + // Note that both the key name and value affect the total size. + const testKeyName = '_electronDOMStorageQuotaIncreasedTest'; + const length = 40 * Math.pow(2, 20) - testKeyName.length; + storage.setItem(testKeyName, 'X'.repeat(length)); + // Wait at least one turn of the event loop to help avoid false positives + // Although not entirely necessary, the previous version of this test case + // failed to detect a real problem (perhaps related to DOM storage data caching) + // wherein calling `getItem` immediately after `setItem` would appear to work + // but then later (e.g. next tick) it would not. + setTimeout(() => { + expect(storage.getItem(testKeyName)).to.have.lengthOf(length); + storage.removeItem(testKeyName); + done(); + }, 1); + }); + it(`throws when attempting to use more than 128MiB in ${storageName}`, () => { + expect(() => { + const testKeyName = '_electronDOMStorageQuotaStillEnforcedTest'; + const length = 128 * Math.pow(2, 20) - testKeyName.length; + try { + storage.setItem(testKeyName, 'X'.repeat(length)); + } finally { + storage.removeItem(testKeyName); + } + }).to.throw(); + }); + }); + }); it('requesting persitent quota works', (done) => { navigator.webkitPersistentStorage.requestQuota(1024 * 1024, (grantedBytes) => { - expect(grantedBytes).to.equal(1048576) - done() - }) - }) - }) + expect(grantedBytes).to.equal(1048576); + done(); + }); + }); + }); describe('websockets', () => { - let wss = null - let server = null - const WebSocketServer = ws.Server + let wss = null; + let server = null; + const WebSocketServer = ws.Server; afterEach(() => { - wss.close() - server.close() - }) + wss.close(); + server.close(); + }); it('has user agent', (done) => { - server = http.createServer() + server = http.createServer(); server.listen(0, '127.0.0.1', () => { - const port = server.address().port - wss = new WebSocketServer({ server: server }) - wss.on('error', done) + const port = server.address().port; + wss = new WebSocketServer({ server: server }); + wss.on('error', done); wss.on('connection', (ws, upgradeReq) => { if (upgradeReq.headers['user-agent']) { - done() + done(); } else { - done('user agent is empty') + done('user agent is empty'); } - }) - const socket = new WebSocket(`ws://127.0.0.1:${port}`) - }) - }) - }) + }); + const socket = new WebSocket(`ws://127.0.0.1:${port}`); + }); + }); + }); describe('Promise', () => { it('resolves correctly in Node.js calls', (done) => { - document.registerElement('x-element', { - prototype: Object.create(HTMLElement.prototype, { - createdCallback: { - value: () => {} - } - }) - }) + class XElement extends HTMLElement {} + customElements.define('x-element', XElement); setImmediate(() => { - let called = false + let called = false; Promise.resolve().then(() => { - done(called ? void 0 : new Error('wrong sequence')) - }) - document.createElement('x-element') - called = true - }) - }) + done(called ? void 0 : new Error('wrong sequence')); + }); + document.createElement('x-element'); + called = true; + }); + }); it('resolves correctly in Electron calls', (done) => { - document.registerElement('y-element', { - prototype: Object.create(HTMLElement.prototype, { - createdCallback: { - value: () => {} - } - }) - }) + class YElement extends HTMLElement {} + customElements.define('y-element', YElement); ipcRenderer.invoke('ping').then(() => { - let called = false + let called = false; Promise.resolve().then(() => { - done(called ? void 0 : new Error('wrong sequence')) - }) - document.createElement('y-element') - called = true - }) - }) - }) + done(called ? void 0 : new Error('wrong sequence')); + }); + document.createElement('y-element'); + called = true; + }); + }); + }); describe('fetch', () => { it('does not crash', (done) => { const server = http.createServer((req, res) => { - res.end('test') - server.close() - }) + res.end('test'); + server.close(); + }); server.listen(0, '127.0.0.1', () => { - const port = server.address().port + const port = server.address().port; fetch(`http://127.0.0.1:${port}`).then((res) => res.body.getReader()) .then((reader) => { reader.read().then((r) => { - reader.cancel() - done() - }) - }).catch((e) => done(e)) - }) - }) - }) + reader.cancel(); + done(); + }); + }).catch((e) => done(e)); + }); + }); + }); describe('window.alert(message, title)', () => { it('throws an exception when the arguments cannot be converted to strings', () => { expect(() => { - window.alert({ toString: null }) - }).to.throw('Cannot convert object to primitive value') - }) - }) + window.alert({ toString: null }); + }).to.throw('Cannot convert object to primitive value'); + }); + }); describe('window.confirm(message, title)', () => { it('throws an exception when the arguments cannot be converted to strings', () => { expect(() => { - window.confirm({ toString: null }, 'title') - }).to.throw('Cannot convert object to primitive value') - }) - }) + window.confirm({ toString: null }, 'title'); + }).to.throw('Cannot convert object to primitive value'); + }); + }); describe('window.history', () => { describe('window.history.go(offset)', () => { it('throws an exception when the argumnet cannot be converted to a string', () => { expect(() => { - window.history.go({ toString: null }) - }).to.throw('Cannot convert object to primitive value') - }) - }) - }) + window.history.go({ toString: null }); + }).to.throw('Cannot convert object to primitive value'); + }); + }); + }); // TODO(nornagon): this is broken on CI, it triggers: // [FATAL:speech_synthesis.mojom-shared.h(237)] The outgoing message will @@ -515,50 +525,50 @@ describe('chromium feature', () => { describe.skip('SpeechSynthesis', () => { before(function () { if (!features.isTtsEnabled()) { - this.skip() + this.skip(); } - }) + }); it('should emit lifecycle events', async () => { const sentence = `long sentence which will take at least a few seconds to - utter so that it's possible to pause and resume before the end` - const utter = new SpeechSynthesisUtterance(sentence) + utter so that it's possible to pause and resume before the end`; + const utter = new SpeechSynthesisUtterance(sentence); // Create a dummy utterence so that speech synthesis state // is initialized for later calls. - speechSynthesis.speak(new SpeechSynthesisUtterance()) - speechSynthesis.cancel() - speechSynthesis.speak(utter) + speechSynthesis.speak(new SpeechSynthesisUtterance()); + speechSynthesis.cancel(); + speechSynthesis.speak(utter); // paused state after speak() - expect(speechSynthesis.paused).to.be.false() - await new Promise((resolve) => { utter.onstart = resolve }) + expect(speechSynthesis.paused).to.be.false(); + await new Promise((resolve) => { utter.onstart = resolve; }); // paused state after start event - expect(speechSynthesis.paused).to.be.false() + expect(speechSynthesis.paused).to.be.false(); - speechSynthesis.pause() + speechSynthesis.pause(); // paused state changes async, right before the pause event - expect(speechSynthesis.paused).to.be.false() - await new Promise((resolve) => { utter.onpause = resolve }) - expect(speechSynthesis.paused).to.be.true() + expect(speechSynthesis.paused).to.be.false(); + await new Promise((resolve) => { utter.onpause = resolve; }); + expect(speechSynthesis.paused).to.be.true(); - speechSynthesis.resume() - await new Promise((resolve) => { utter.onresume = resolve }) + speechSynthesis.resume(); + await new Promise((resolve) => { utter.onresume = resolve; }); // paused state after resume event - expect(speechSynthesis.paused).to.be.false() + expect(speechSynthesis.paused).to.be.false(); - await new Promise((resolve) => { utter.onend = resolve }) - }) - }) -}) + await new Promise((resolve) => { utter.onend = resolve; }); + }); + }); +}); describe('console functions', () => { it('should exist', () => { - expect(console.log, 'log').to.be.a('function') - expect(console.error, 'error').to.be.a('function') - expect(console.warn, 'warn').to.be.a('function') - expect(console.info, 'info').to.be.a('function') - expect(console.debug, 'debug').to.be.a('function') - expect(console.trace, 'trace').to.be.a('function') - expect(console.time, 'time').to.be.a('function') - expect(console.timeEnd, 'timeEnd').to.be.a('function') - }) -}) + expect(console.log, 'log').to.be.a('function'); + expect(console.error, 'error').to.be.a('function'); + expect(console.warn, 'warn').to.be.a('function'); + expect(console.info, 'info').to.be.a('function'); + expect(console.debug, 'debug').to.be.a('function'); + expect(console.trace, 'trace').to.be.a('function'); + expect(console.time, 'time').to.be.a('function'); + expect(console.timeEnd, 'timeEnd').to.be.a('function'); + }); +}); diff --git a/spec/events-helpers.js b/spec/events-helpers.js index 64a4fba447eb3..507f68eb113f1 100644 --- a/spec/events-helpers.js +++ b/spec/events-helpers.js @@ -10,9 +10,9 @@ */ const waitForEvent = (target, eventName) => { return new Promise(resolve => { - target.addEventListener(eventName, resolve, { once: true }) - }) -} + target.addEventListener(eventName, resolve, { once: true }); + }); +}; /** * @param {!EventEmitter} emitter @@ -20,23 +20,23 @@ const waitForEvent = (target, eventName) => { * @return {!Promise} With Event as the first item. */ const emittedOnce = (emitter, eventName) => { - return emittedNTimes(emitter, eventName, 1).then(([result]) => result) -} + return emittedNTimes(emitter, eventName, 1).then(([result]) => result); +}; const emittedNTimes = (emitter, eventName, times) => { - const events = [] + const events = []; return new Promise(resolve => { const handler = (...args) => { - events.push(args) + events.push(args); if (events.length === times) { - emitter.removeListener(eventName, handler) - resolve(events) + emitter.removeListener(eventName, handler); + resolve(events); } - } - emitter.on(eventName, handler) - }) -} + }; + emitter.on(eventName, handler); + }); +}; -exports.emittedOnce = emittedOnce -exports.emittedNTimes = emittedNTimes -exports.waitForEvent = waitForEvent +exports.emittedOnce = emittedOnce; +exports.emittedNTimes = emittedNTimes; +exports.waitForEvent = waitForEvent; diff --git a/spec/expect-helpers.js b/spec/expect-helpers.js index a825f6d2a5c2c..18d72d1ec66b8 100644 --- a/spec/expect-helpers.js +++ b/spec/expect-helpers.js @@ -1,18 +1,18 @@ function resolveSingleObjectGetters (object) { if (object && typeof object === 'object') { - const newObject = {} - for (const key in object) { - newObject[key] = resolveGetters(object[key])[0] + const newObject = {}; + for (const key in object) { // eslint-disable-line guard-for-in + newObject[key] = resolveGetters(object[key])[0]; } - return newObject + return newObject; } - return object + return object; } function resolveGetters (...args) { - return args.map(resolveSingleObjectGetters) + return args.map(resolveSingleObjectGetters); } module.exports = { resolveGetters -} +}; diff --git a/spec/fixtures/api/app-path/lib/index.js b/spec/fixtures/api/app-path/lib/index.js index d1a5732edca51..a4510fd01c7a8 100644 --- a/spec/fixtures/api/app-path/lib/index.js +++ b/spec/fixtures/api/app-path/lib/index.js @@ -1,10 +1,10 @@ -const { app } = require('electron') +const { app } = require('electron'); const payload = { appPath: app.getAppPath() -} +}; -process.stdout.write(JSON.stringify(payload)) -process.stdout.end() +process.stdout.write(JSON.stringify(payload)); +process.stdout.end(); -process.exit() +process.exit(); diff --git a/spec/fixtures/api/command-line/main.js b/spec/fixtures/api/command-line/main.js index 39e62cafbbc87..4a840e08744ca 100644 --- a/spec/fixtures/api/command-line/main.js +++ b/spec/fixtures/api/command-line/main.js @@ -1,15 +1,13 @@ -const { app } = require('electron') +const { app } = require('electron'); app.on('ready', () => { const payload = { hasSwitch: app.commandLine.hasSwitch('foobar'), getSwitchValue: app.commandLine.getSwitchValue('foobar') - } + }; - process.stdout.write(JSON.stringify(payload)) - process.stdout.end() + process.stdout.write(JSON.stringify(payload)); + process.stdout.end(); - setImmediate(() => { - app.quit() - }) -}) + app.quit(); +}); diff --git a/spec/fixtures/api/cookie-app/main.js b/spec/fixtures/api/cookie-app/main.js index 3e63186537898..7e21e86ed120a 100644 --- a/spec/fixtures/api/cookie-app/main.js +++ b/spec/fixtures/api/cookie-app/main.js @@ -1,49 +1,47 @@ -const { app, session } = require('electron') +const { app, session } = require('electron'); app.on('ready', async function () { - const url = 'http://foo.bar' - const persistentSession = session.fromPartition('persist:ence-test') - const name = 'test' - const value = 'true' + const url = 'http://foo.bar'; + const persistentSession = session.fromPartition('persist:ence-test'); + const name = 'test'; + const value = 'true'; const set = () => persistentSession.cookies.set({ url, name, value, expirationDate: Date.now() + 60000 - }) + }); const get = () => persistentSession.cookies.get({ url - }) + }); const maybeRemove = async (pred) => new Promise(async (resolve, reject) => { try { if (pred()) { - await persistentSession.cookies.remove(url, name) + await persistentSession.cookies.remove(url, name); } - resolve() + resolve(); } catch (error) { - reject(error) + reject(error); } - }) + }); try { - await maybeRemove(() => process.env.PHASE === 'one') - const one = await get() - await set() - const two = await get() - await maybeRemove(() => process.env.PHASE === 'two') - const three = await get() + await maybeRemove(() => process.env.PHASE === 'one'); + const one = await get(); + await set(); + const two = await get(); + await maybeRemove(() => process.env.PHASE === 'two'); + const three = await get(); - process.stdout.write(`${one.length}${two.length}${three.length}`) + process.stdout.write(`${one.length}${two.length}${three.length}`); } catch (e) { - process.stdout.write('ERROR') + process.stdout.write('ERROR'); } finally { - process.stdout.end() + process.stdout.end(); - setImmediate(() => { - app.quit() - }) + app.quit(); } -}) +}); diff --git a/spec/fixtures/api/default-menu/main.js b/spec/fixtures/api/default-menu/main.js index 786d9ab4b0d3f..d3dca3463d654 100644 --- a/spec/fixtures/api/default-menu/main.js +++ b/spec/fixtures/api/default-menu/main.js @@ -1,32 +1,32 @@ -const { app, Menu } = require('electron') +const { app, Menu } = require('electron'); function output (value) { - process.stdout.write(JSON.stringify(value)) - process.stdout.end() + process.stdout.write(JSON.stringify(value)); + process.stdout.end(); - app.quit() + app.quit(); } try { - let expectedMenu + let expectedMenu; if (app.commandLine.hasSwitch('custom-menu')) { - expectedMenu = new Menu() - Menu.setApplicationMenu(expectedMenu) + expectedMenu = new Menu(); + Menu.setApplicationMenu(expectedMenu); } else if (app.commandLine.hasSwitch('null-menu')) { - expectedMenu = null - Menu.setApplicationMenu(null) + expectedMenu = null; + Menu.setApplicationMenu(null); } app.on('ready', () => { setImmediate(() => { try { - output(Menu.getApplicationMenu() === expectedMenu) + output(Menu.getApplicationMenu() === expectedMenu); } catch (error) { - output(null) + output(null); } - }) - }) + }); + }); } catch (error) { - output(null) + output(null); } diff --git a/spec/fixtures/api/electron-main-module/app/index.js b/spec/fixtures/api/electron-main-module/app/index.js index c56845bca42d5..873192591cd3d 100644 --- a/spec/fixtures/api/electron-main-module/app/index.js +++ b/spec/fixtures/api/electron-main-module/app/index.js @@ -1,8 +1,8 @@ try { - require('some-module') + require('some-module'); } catch (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } -process.exit(0) +process.exit(0); diff --git a/spec/fixtures/api/electron-module-app/.gitignore b/spec/fixtures/api/electron-module-app/.gitignore new file mode 100644 index 0000000000000..736e8ae58ad87 --- /dev/null +++ b/spec/fixtures/api/electron-module-app/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/spec/fixtures/api/electron-module-app/node_modules/foo/index.js b/spec/fixtures/api/electron-module-app/node_modules/foo/index.js index 11d763e517426..41f3ba446f69d 100644 --- a/spec/fixtures/api/electron-module-app/node_modules/foo/index.js +++ b/spec/fixtures/api/electron-module-app/node_modules/foo/index.js @@ -1 +1 @@ -exports.bar = function () {} +exports.bar = function () {}; diff --git a/spec/fixtures/api/exit-closes-all-windows-app/main.js b/spec/fixtures/api/exit-closes-all-windows-app/main.js index 20d5a1dac2a1c..cd49e42865cec 100644 --- a/spec/fixtures/api/exit-closes-all-windows-app/main.js +++ b/spec/fixtures/api/exit-closes-all-windows-app/main.js @@ -1,19 +1,19 @@ -const { app, BrowserWindow } = require('electron') +const { app, BrowserWindow } = require('electron'); -const windows = [] +const windows = []; function createWindow (id) { - const window = new BrowserWindow({ show: false }) - window.loadURL(`data:,window${id}`) - windows.push(window) + const window = new BrowserWindow({ show: false }); + window.loadURL(`data:,window${id}`); + windows.push(window); } app.once('ready', () => { for (let i = 1; i <= 5; i++) { - createWindow(i) + createWindow(i); } setImmediate(function () { - app.exit(123) - }) -}) + app.exit(123); + }); +}); diff --git a/spec/fixtures/api/globals.html b/spec/fixtures/api/globals.html new file mode 100644 index 0000000000000..f1bfc56037c79 --- /dev/null +++ b/spec/fixtures/api/globals.html @@ -0,0 +1,13 @@ + + + + Document + + + + + \ No newline at end of file diff --git a/spec/fixtures/api/gpu-info.js b/spec/fixtures/api/gpu-info.js index 1954f9b4b8a80..b59c83960e458 100644 --- a/spec/fixtures/api/gpu-info.js +++ b/spec/fixtures/api/gpu-info.js @@ -1,17 +1,17 @@ -const { app } = require('electron') +const { app } = require('electron'); -app.commandLine.appendSwitch('--disable-software-rasterizer') +app.commandLine.appendSwitch('--disable-software-rasterizer'); app.on('ready', () => { - const infoType = process.argv.pop() + const infoType = process.argv.pop(); app.getGPUInfo(infoType).then( (gpuInfo) => { - console.log(JSON.stringify(gpuInfo)) - app.exit(0) + console.log(JSON.stringify(gpuInfo)); + app.exit(0); }, (error) => { - console.error(error) - app.exit(1) + console.error(error); + app.exit(1); } - ) -}) + ); +}); diff --git a/spec/fixtures/api/ipc-main-listeners/main.js b/spec/fixtures/api/ipc-main-listeners/main.js index d56348136bc1d..e4c2a94ceb8f2 100644 --- a/spec/fixtures/api/ipc-main-listeners/main.js +++ b/spec/fixtures/api/ipc-main-listeners/main.js @@ -1,10 +1,8 @@ -const { app, ipcMain } = require('electron') +const { app, ipcMain } = require('electron'); app.on('ready', () => { - process.stdout.write(JSON.stringify(ipcMain.eventNames())) - process.stdout.end() + process.stdout.write(JSON.stringify(ipcMain.eventNames())); + process.stdout.end(); - setImmediate(() => { - app.quit() - }) -}) + app.quit(); +}); diff --git a/spec/fixtures/api/isolated-fetch-preload.js b/spec/fixtures/api/isolated-fetch-preload.js index 283af99fe16e1..e2c7b75857f77 100644 --- a/spec/fixtures/api/isolated-fetch-preload.js +++ b/spec/fixtures/api/isolated-fetch-preload.js @@ -1,6 +1,6 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); // Ensure fetch works from isolated world origin fetch('https://localhost:1234').catch(err => { - ipcRenderer.send('isolated-fetch-error', err.message) -}) + ipcRenderer.send('isolated-fetch-error', err.message); +}); diff --git a/spec/fixtures/api/isolated-preload.js b/spec/fixtures/api/isolated-preload.js index 20c2181bacbaf..c0ca37505ae6f 100644 --- a/spec/fixtures/api/isolated-preload.js +++ b/spec/fixtures/api/isolated-preload.js @@ -1,8 +1,8 @@ -const { ipcRenderer, webFrame } = require('electron') +const { ipcRenderer, webFrame } = require('electron'); -window.foo = 3 +window.foo = 3; -webFrame.executeJavaScript('window.preloadExecuteJavaScriptProperty = 1234;') +webFrame.executeJavaScript('window.preloadExecuteJavaScriptProperty = 1234;'); window.addEventListener('message', (event) => { ipcRenderer.send('isolated-world', { @@ -16,5 +16,5 @@ window.addEventListener('message', (event) => { typeofPreloadExecuteJavaScriptProperty: typeof window.preloadExecuteJavaScriptProperty }, pageContext: event.data - }) -}) + }); +}); diff --git a/spec/fixtures/api/leak-exit-browserview.js b/spec/fixtures/api/leak-exit-browserview.js index 534fea59bbca7..535f1594697cb 100644 --- a/spec/fixtures/api/leak-exit-browserview.js +++ b/spec/fixtures/api/leak-exit-browserview.js @@ -1,6 +1,6 @@ -const { BrowserView, app } = require('electron') +const { BrowserView, app } = require('electron'); app.on('ready', function () { new BrowserView({}) // eslint-disable-line - process.nextTick(() => app.quit()) -}) + app.quit(); +}); diff --git a/spec/fixtures/api/leak-exit-webcontents.js b/spec/fixtures/api/leak-exit-webcontents.js index 5bf9e205793ce..ee417ca04f159 100644 --- a/spec/fixtures/api/leak-exit-webcontents.js +++ b/spec/fixtures/api/leak-exit-webcontents.js @@ -1,6 +1,6 @@ -const { app, webContents } = require('electron') +const { app, webContents } = require('electron'); app.on('ready', function () { - webContents.create({}) + webContents.create({}); - process.nextTick(() => app.quit()) -}) + app.quit(); +}); diff --git a/spec/fixtures/api/loaded-from-dataurl.js b/spec/fixtures/api/loaded-from-dataurl.js index c4dbdd044bdb2..ccb1454acfc27 100644 --- a/spec/fixtures/api/loaded-from-dataurl.js +++ b/spec/fixtures/api/loaded-from-dataurl.js @@ -1 +1 @@ -require('electron').ipcRenderer.send('answer', 'test') +require('electron').ipcRenderer.send('answer', 'test'); diff --git a/spec/fixtures/api/locale-check/main.js b/spec/fixtures/api/locale-check/main.js index c89e129904245..234f1356f44d9 100644 --- a/spec/fixtures/api/locale-check/main.js +++ b/spec/fixtures/api/locale-check/main.js @@ -1,10 +1,8 @@ -const { app } = require('electron') +const { app } = require('electron'); app.on('ready', () => { - process.stdout.write(app.getLocale()) - process.stdout.end() + process.stdout.write(app.getLocale()); + process.stdout.end(); - setImmediate(() => { - app.quit() - }) -}) + app.quit(); +}); diff --git a/spec/fixtures/api/mixed-sandbox-app/electron-app-mixed-sandbox-preload.js b/spec/fixtures/api/mixed-sandbox-app/electron-app-mixed-sandbox-preload.js index abe0eeea87edc..fb2d6a30bcf3d 100644 --- a/spec/fixtures/api/mixed-sandbox-app/electron-app-mixed-sandbox-preload.js +++ b/spec/fixtures/api/mixed-sandbox-app/electron-app-mixed-sandbox-preload.js @@ -1 +1 @@ -require('electron').ipcRenderer.send('argv', process.argv) +require('electron').ipcRenderer.send('argv', process.argv); diff --git a/spec/fixtures/api/mixed-sandbox-app/main.js b/spec/fixtures/api/mixed-sandbox-app/main.js index 0479ca1d77040..2158ed9900e5d 100644 --- a/spec/fixtures/api/mixed-sandbox-app/main.js +++ b/spec/fixtures/api/mixed-sandbox-app/main.js @@ -1,40 +1,40 @@ -const { app, BrowserWindow, ipcMain } = require('electron') -const net = require('net') -const path = require('path') +const { app, BrowserWindow, ipcMain } = require('electron'); +const net = require('net'); +const path = require('path'); process.on('uncaughtException', () => { - app.exit(1) -}) + app.exit(1); +}); if (process.argv.includes('--app-enable-sandbox')) { - app.enableSandbox() + app.enableSandbox(); } -let currentWindowSandboxed = false +let currentWindowSandboxed = false; app.once('ready', () => { function testWindow (isSandboxed, callback) { - currentWindowSandboxed = isSandboxed + currentWindowSandboxed = isSandboxed; const currentWindow = new BrowserWindow({ show: false, webPreferences: { preload: path.join(__dirname, 'electron-app-mixed-sandbox-preload.js'), sandbox: isSandboxed } - }) - currentWindow.loadURL('about:blank') + }); + currentWindow.loadURL('about:blank'); currentWindow.webContents.once('devtools-opened', () => { if (isSandboxed) { - argv.sandboxDevtools = true + argv.sandboxDevtools = true; } else { - argv.noSandboxDevtools = true + argv.noSandboxDevtools = true; } if (callback) { - callback() + callback(); } - finish() - }) - currentWindow.webContents.openDevTools() + finish(); + }); + currentWindow.webContents.openDevTools(); } const argv = { @@ -42,36 +42,36 @@ app.once('ready', () => { noSandbox: null, sandboxDevtools: null, noSandboxDevtools: null - } + }; - let connected = false + let connected = false; testWindow(true, () => { - testWindow() - }) + testWindow(); + }); function finish () { if (connected && argv.sandbox != null && argv.noSandbox != null && argv.noSandboxDevtools != null && argv.sandboxDevtools != null) { client.once('end', () => { - app.exit(0) - }) - client.end(JSON.stringify(argv)) + app.exit(0); + }); + client.end(JSON.stringify(argv)); } } - const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox' + const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-mixed-sandbox' : '/tmp/electron-mixed-sandbox'; const client = net.connect(socketPath, () => { - connected = true - finish() - }) + connected = true; + finish(); + }); ipcMain.on('argv', (event, value) => { if (currentWindowSandboxed) { - argv.sandbox = value + argv.sandbox = value; } else { - argv.noSandbox = value + argv.noSandbox = value; } - finish() - }) -}) + finish(); + }); +}); diff --git a/spec/fixtures/api/native-window-open-isolated-preload.js b/spec/fixtures/api/native-window-open-isolated-preload.js index 9491e4efcc465..de5d41564b42f 100644 --- a/spec/fixtures/api/native-window-open-isolated-preload.js +++ b/spec/fixtures/api/native-window-open-isolated-preload.js @@ -1,5 +1,5 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.addEventListener('message', (event) => { - ipcRenderer.send('answer', event.data) -}) + ipcRenderer.send('answer', event.data); +}); diff --git a/spec/fixtures/api/new-window-preload.js b/spec/fixtures/api/new-window-preload.js index 6d9add91f0403..b22a1402c67ab 100644 --- a/spec/fixtures/api/new-window-preload.js +++ b/spec/fixtures/api/new-window-preload.js @@ -1,4 +1,4 @@ -const { ipcRenderer, remote } = require('electron') +const { ipcRenderer, remote } = require('electron'); -ipcRenderer.send('answer', process.argv, remote.getCurrentWindow().webContents.getWebPreferences()) -window.close() +ipcRenderer.send('answer', process.argv, remote.getCurrentWindow().webContents.getWebPreferences()); +window.close(); diff --git a/spec/fixtures/api/new-window-webview-preload.js b/spec/fixtures/api/new-window-webview-preload.js index ba5d5ded82db1..1336da90a9fa9 100644 --- a/spec/fixtures/api/new-window-webview-preload.js +++ b/spec/fixtures/api/new-window-webview-preload.js @@ -1,3 +1,3 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); -window.ipcRenderer = ipcRenderer +window.ipcRenderer = ipcRenderer; diff --git a/spec/fixtures/api/quit-app/main.js b/spec/fixtures/api/quit-app/main.js index 9912071b1f365..226f0023b37cf 100644 --- a/spec/fixtures/api/quit-app/main.js +++ b/spec/fixtures/api/quit-app/main.js @@ -1,12 +1,12 @@ -const { app } = require('electron') +const { app } = require('electron'); app.on('ready', function () { // This setImmediate call gets the spec passing on Linux setImmediate(function () { - app.exit(123) - }) -}) + app.exit(123); + }); +}); process.on('exit', function (code) { - console.log('Exit event with code: ' + code) -}) + console.log('Exit event with code: ' + code); +}); diff --git a/spec/fixtures/api/relaunch/main.js b/spec/fixtures/api/relaunch/main.js index 69b634f3c2a00..92855074a9dca 100644 --- a/spec/fixtures/api/relaunch/main.js +++ b/spec/fixtures/api/relaunch/main.js @@ -1,23 +1,23 @@ -const { app } = require('electron') -const net = require('net') +const { app } = require('electron'); +const net = require('net'); -const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch' +const socketPath = process.platform === 'win32' ? '\\\\.\\pipe\\electron-app-relaunch' : '/tmp/electron-app-relaunch'; process.on('uncaughtException', () => { - app.exit(1) -}) + app.exit(1); +}); app.once('ready', () => { - const lastArg = process.argv[process.argv.length - 1] - const client = net.connect(socketPath) + const lastArg = process.argv[process.argv.length - 1]; + const client = net.connect(socketPath); client.once('connect', () => { - client.end(String(lastArg === '--second')) - }) + client.end(String(lastArg === '--second')); + }); client.once('end', () => { - app.exit(0) - }) + app.exit(0); + }); if (lastArg !== '--second') { - app.relaunch({ args: process.argv.slice(1).concat('--second') }) + app.relaunch({ args: process.argv.slice(1).concat('--second') }); } -}) +}); diff --git a/spec/fixtures/api/shared-worker/shared-worker1.js b/spec/fixtures/api/shared-worker/shared-worker1.js index 468144a9d02e5..f749b244662b9 100644 --- a/spec/fixtures/api/shared-worker/shared-worker1.js +++ b/spec/fixtures/api/shared-worker/shared-worker1.js @@ -1,4 +1,4 @@ self.onconnect = function (e) { - const port = e.ports[0] - port.postMessage('ready') -} + const port = e.ports[0]; + port.postMessage('ready'); +}; diff --git a/spec/fixtures/api/shared-worker/shared-worker2.js b/spec/fixtures/api/shared-worker/shared-worker2.js index 468144a9d02e5..f749b244662b9 100644 --- a/spec/fixtures/api/shared-worker/shared-worker2.js +++ b/spec/fixtures/api/shared-worker/shared-worker2.js @@ -1,4 +1,4 @@ self.onconnect = function (e) { - const port = e.ports[0] - port.postMessage('ready') -} + const port = e.ports[0]; + port.postMessage('ready'); +}; diff --git a/spec/fixtures/api/singleton/main.js b/spec/fixtures/api/singleton/main.js index 9c1b0a17e4cfb..887ed46e4f507 100644 --- a/spec/fixtures/api/singleton/main.js +++ b/spec/fixtures/api/singleton/main.js @@ -1,18 +1,18 @@ -const { app } = require('electron') +const { app } = require('electron'); app.once('ready', () => { - console.log('started') // ping parent -}) + console.log('started'); // ping parent +}); -const gotTheLock = app.requestSingleInstanceLock() +const gotTheLock = app.requestSingleInstanceLock(); app.on('second-instance', (event, args) => { setImmediate(() => { - console.log(JSON.stringify(args)) - app.exit(0) - }) -}) + console.log(JSON.stringify(args)); + app.exit(0); + }); +}); if (!gotTheLock) { - app.exit(1) + app.exit(1); } diff --git a/spec/fixtures/api/site-instance-overrides/main.js b/spec/fixtures/api/site-instance-overrides/main.js index 20d10b8ff54c9..629b916888042 100644 --- a/spec/fixtures/api/site-instance-overrides/main.js +++ b/spec/fixtures/api/site-instance-overrides/main.js @@ -1,26 +1,26 @@ -const { app, BrowserWindow, ipcMain } = require('electron') -const path = require('path') +const { app, BrowserWindow, ipcMain } = require('electron'); +const path = require('path'); process.on('uncaughtException', (e) => { - console.error(e) - process.exit(1) -}) + console.error(e); + process.exit(1); +}); -app.allowRendererProcessReuse = JSON.parse(process.argv[2]) +app.allowRendererProcessReuse = JSON.parse(process.argv[2]); -const pids = [] -let win +const pids = []; +let win; ipcMain.on('pid', (event, pid) => { - pids.push(pid) + pids.push(pid); if (pids.length === 2) { - console.log(JSON.stringify(pids)) - if (win) win.close() - app.quit() + console.log(JSON.stringify(pids)); + if (win) win.close(); + app.quit(); } else { - if (win) win.reload() + if (win) win.reload(); } -}) +}); app.whenReady().then(() => { win = new BrowserWindow({ @@ -28,6 +28,6 @@ app.whenReady().then(() => { webPreferences: { preload: path.resolve(__dirname, 'preload.js') } - }) - win.loadFile('index.html') -}) + }); + win.loadFile('index.html'); +}); diff --git a/spec/fixtures/api/site-instance-overrides/preload.js b/spec/fixtures/api/site-instance-overrides/preload.js index 721369deb3449..cfe37266b5e8b 100644 --- a/spec/fixtures/api/site-instance-overrides/preload.js +++ b/spec/fixtures/api/site-instance-overrides/preload.js @@ -1,3 +1,3 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); -ipcRenderer.send('pid', process.pid) +ipcRenderer.send('pid', process.pid); diff --git a/spec/fixtures/api/window-all-closed/main.js b/spec/fixtures/api/window-all-closed/main.js index 67450d93e751c..2dce11691bdb5 100644 --- a/spec/fixtures/api/window-all-closed/main.js +++ b/spec/fixtures/api/window-all-closed/main.js @@ -1,20 +1,22 @@ -const { app, BrowserWindow } = require('electron') +const { app, BrowserWindow } = require('electron'); +// Suppress deprecation logs +app.allowRendererProcessReuse = true; -let handled = false +let handled = false; if (app.commandLine.hasSwitch('handle-event')) { app.on('window-all-closed', () => { - handled = true - app.quit() - }) + handled = true; + app.quit(); + }); } app.on('quit', () => { - process.stdout.write(JSON.stringify(handled)) - process.stdout.end() -}) + process.stdout.write(JSON.stringify(handled)); + process.stdout.end(); +}); app.on('ready', () => { - const win = new BrowserWindow() - win.close() -}) + const win = new BrowserWindow(); + win.close(); +}); diff --git a/spec/fixtures/api/window-open-preload.js b/spec/fixtures/api/window-open-preload.js index ced0c05b1badd..8d7e5fec4673e 100644 --- a/spec/fixtures/api/window-open-preload.js +++ b/spec/fixtures/api/window-open-preload.js @@ -1,9 +1,9 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); setImmediate(function () { if (window.location.toString() === 'bar://page') { - const windowOpenerIsNull = window.opener == null - ipcRenderer.send('answer', process.argv, typeof global.process, windowOpenerIsNull) - window.close() + const windowOpenerIsNull = window.opener == null; + ipcRenderer.send('answer', process.argv, typeof global.process, windowOpenerIsNull); + window.close(); } -}) +}); diff --git a/spec/fixtures/auto-update/check/index.js b/spec/fixtures/auto-update/check/index.js index 1f82a3acbac60..7b89a831a6859 100644 --- a/spec/fixtures/auto-update/check/index.js +++ b/spec/fixtures/auto-update/check/index.js @@ -1,23 +1,23 @@ process.on('uncaughtException', (err) => { - console.error(err) - process.exit(1) -}) + console.error(err); + process.exit(1); +}); -const { autoUpdater } = require('electron') +const { autoUpdater } = require('electron'); autoUpdater.on('error', (err) => { - console.error(err) - process.exit(1) -}) + console.error(err); + process.exit(1); +}); -const feedUrl = process.argv[1] +const feedUrl = process.argv[1]; autoUpdater.setFeedURL({ url: feedUrl -}) +}); -autoUpdater.checkForUpdates() +autoUpdater.checkForUpdates(); autoUpdater.on('update-not-available', () => { - process.exit(0) -}) + process.exit(0); +}); diff --git a/spec/fixtures/auto-update/initial/index.js b/spec/fixtures/auto-update/initial/index.js index ef1332ebbac9f..1f1c2571b1645 100644 --- a/spec/fixtures/auto-update/initial/index.js +++ b/spec/fixtures/auto-update/initial/index.js @@ -1,18 +1,18 @@ process.on('uncaughtException', (err) => { - console.error(err) - process.exit(1) -}) + console.error(err); + process.exit(1); +}); -const { autoUpdater } = require('electron') +const { autoUpdater } = require('electron'); -const feedUrl = process.argv[1] +const feedUrl = process.argv[1]; -console.log('Setting Feed URL') +console.log('Setting Feed URL'); autoUpdater.setFeedURL({ url: feedUrl -}) +}); -console.log('Feed URL Set:', feedUrl) +console.log('Feed URL Set:', feedUrl); -process.exit(0) +process.exit(0); diff --git a/spec/fixtures/auto-update/update/index.js b/spec/fixtures/auto-update/update/index.js index aec688cbb96d1..e391362f12f04 100644 --- a/spec/fixtures/auto-update/update/index.js +++ b/spec/fixtures/auto-update/update/index.js @@ -1,42 +1,42 @@ -const fs = require('fs') -const path = require('path') +const fs = require('fs'); +const path = require('path'); process.on('uncaughtException', (err) => { - console.error(err) - process.exit(1) -}) + console.error(err); + process.exit(1); +}); -const { app, autoUpdater } = require('electron') +const { app, autoUpdater } = require('electron'); autoUpdater.on('error', (err) => { - console.error(err) - process.exit(1) -}) + console.error(err); + process.exit(1); +}); -const urlPath = path.resolve(__dirname, '../../../../url.txt') -let feedUrl = process.argv[1] +const urlPath = path.resolve(__dirname, '../../../../url.txt'); +let feedUrl = process.argv[1]; if (!feedUrl || !feedUrl.startsWith('http')) { - feedUrl = `${fs.readFileSync(urlPath, 'utf8')}/${app.getVersion()}` + feedUrl = `${fs.readFileSync(urlPath, 'utf8')}/${app.getVersion()}`; } else { - fs.writeFileSync(urlPath, `${feedUrl}/updated`) + fs.writeFileSync(urlPath, `${feedUrl}/updated`); } autoUpdater.setFeedURL({ url: feedUrl -}) +}); -autoUpdater.checkForUpdates() +autoUpdater.checkForUpdates(); autoUpdater.on('update-available', () => { - console.log('Update Available') -}) + console.log('Update Available'); +}); autoUpdater.on('update-downloaded', () => { - console.log('Update Downloaded') - autoUpdater.quitAndInstall() -}) + console.log('Update Downloaded'); + autoUpdater.quitAndInstall(); +}); autoUpdater.on('update-not-available', () => { - console.error('No update available') - process.exit(1) -}) + console.error('No update available'); + process.exit(1); +}); diff --git a/spec/fixtures/crash-app/main.js b/spec/fixtures/crash-app/main.js index e279216218822..592dbe7fb011b 100644 --- a/spec/fixtures/crash-app/main.js +++ b/spec/fixtures/crash-app/main.js @@ -1,5 +1,5 @@ -const { app } = require('electron') +const { app } = require('electron'); app.on('ready', () => { - process.crash() -}) + process.crash(); +}); diff --git a/spec/fixtures/extensions/chrome-api/background.js b/spec/fixtures/extensions/chrome-api/background.js index c86ff51ddb9dd..b79f9aecea642 100644 --- a/spec/fixtures/extensions/chrome-api/background.js +++ b/spec/fixtures/extensions/chrome-api/background.js @@ -1,20 +1,20 @@ /* global chrome */ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - const { method, args = [] } = message - const tabId = sender.tab.id + const { method, args = [] } = message; + const tabId = sender.tab.id; switch (method) { case 'sendMessage': { - const [message] = args - chrome.tabs.sendMessage(tabId, { message, tabId }, undefined, sendResponse) - break + const [message] = args; + chrome.tabs.sendMessage(tabId, { message, tabId }, undefined, sendResponse); + break; } case 'executeScript': { - const [code] = args - chrome.tabs.executeScript(tabId, { code }, ([result]) => sendResponse(result)) - break + const [code] = args; + chrome.tabs.executeScript(tabId, { code }, ([result]) => sendResponse(result)); + break; } } -}) +}); diff --git a/spec/fixtures/extensions/chrome-api/main.js b/spec/fixtures/extensions/chrome-api/main.js index 2784e82c37fd0..e536d3d90d0d4 100644 --- a/spec/fixtures/extensions/chrome-api/main.js +++ b/spec/fixtures/extensions/chrome-api/main.js @@ -1,28 +1,39 @@ /* global chrome */ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - sendResponse(message) -}) + sendResponse(message); +}); const testMap = { + connect () { + let success = false; + try { + chrome.runtime.connect(chrome.runtime.id); + chrome.runtime.connect(chrome.runtime.id, { name: 'content-script' }); + chrome.runtime.connect({ name: 'content-script' }); + success = true; + } finally { + console.log(JSON.stringify(success)); + } + }, getManifest () { - const manifest = chrome.runtime.getManifest() - console.log(JSON.stringify(manifest)) + const manifest = chrome.runtime.getManifest(); + console.log(JSON.stringify(manifest)); }, sendMessage (message) { chrome.runtime.sendMessage({ method: 'sendMessage', args: [message] }, response => { - console.log(JSON.stringify(response)) - }) + console.log(JSON.stringify(response)); + }); }, executeScript (code) { chrome.runtime.sendMessage({ method: 'executeScript', args: [code] }, response => { - console.log(JSON.stringify(response)) - }) + console.log(JSON.stringify(response)); + }); } -} +}; const dispatchTest = (event) => { - const { method, args = [] } = JSON.parse(event.data) - testMap[method](...args) -} -window.addEventListener('message', dispatchTest, false) + const { method, args = [] } = JSON.parse(event.data); + testMap[method](...args); +}; +window.addEventListener('message', dispatchTest, false); diff --git a/spec/fixtures/extensions/content-script-document-end/end.js b/spec/fixtures/extensions/content-script-document-end/end.js index 787b050adc8cf..2ce126d3f81bf 100644 --- a/spec/fixtures/extensions/content-script-document-end/end.js +++ b/spec/fixtures/extensions/content-script-document-end/end.js @@ -1 +1 @@ -document.documentElement.style.backgroundColor = 'red' +document.documentElement.style.backgroundColor = 'red'; diff --git a/spec/fixtures/extensions/content-script-document-idle/idle.js b/spec/fixtures/extensions/content-script-document-idle/idle.js index b45176a31eaf8..b01f93ba591ab 100644 --- a/spec/fixtures/extensions/content-script-document-idle/idle.js +++ b/spec/fixtures/extensions/content-script-document-idle/idle.js @@ -1 +1 @@ -document.body.style.backgroundColor = 'red' +document.body.style.backgroundColor = 'red'; diff --git a/spec/fixtures/extensions/content-script-document-start/start.js b/spec/fixtures/extensions/content-script-document-start/start.js index 787b050adc8cf..2ce126d3f81bf 100644 --- a/spec/fixtures/extensions/content-script-document-start/start.js +++ b/spec/fixtures/extensions/content-script-document-start/start.js @@ -1 +1 @@ -document.documentElement.style.backgroundColor = 'red' +document.documentElement.style.backgroundColor = 'red'; diff --git a/spec/fixtures/extensions/content-script/all_frames-preload.js b/spec/fixtures/extensions/content-script/all_frames-preload.js index 54133d143c9fc..424124917ac0e 100644 --- a/spec/fixtures/extensions/content-script/all_frames-preload.js +++ b/spec/fixtures/extensions/content-script/all_frames-preload.js @@ -1,14 +1,14 @@ -const { ipcRenderer, webFrame } = require('electron') +const { ipcRenderer, webFrame } = require('electron'); if (process.isMainFrame) { // https://github.com/electron/electron/issues/17252 ipcRenderer.on('executeJavaScriptInFrame', (event, frameRoutingId, code, responseId) => { - const frame = webFrame.findFrameByRoutingId(frameRoutingId) + const frame = webFrame.findFrameByRoutingId(frameRoutingId); if (!frame) { - throw new Error(`Can't find frame for routing ID ${frameRoutingId}`) + throw new Error(`Can't find frame for routing ID ${frameRoutingId}`); } frame.executeJavaScript(code, false).then(result => { - event.sender.send(`executeJavaScriptInFrame_${responseId}`, result) - }) - }) + event.sender.send(`executeJavaScriptInFrame_${responseId}`, result); + }); + }); } diff --git a/spec/fixtures/module/access-blink-apis.js b/spec/fixtures/module/access-blink-apis.js index 19f2d51cc8712..625451dac3b52 100644 --- a/spec/fixtures/module/access-blink-apis.js +++ b/spec/fixtures/module/access-blink-apis.js @@ -1,17 +1,17 @@ -window.delayed = true +window.delayed = true; global.getGlobalNames = () => { return Object.getOwnPropertyNames(global) .filter(key => typeof global[key] === 'function') .filter(key => key !== 'WebView') - .sort() -} + .sort(); +}; -const atPreload = global.getGlobalNames() +const atPreload = global.getGlobalNames(); window.addEventListener('load', () => { window.test = { atPreload, atLoad: global.getGlobalNames() - } -}) + }; +}); diff --git a/spec/fixtures/module/asar.js b/spec/fixtures/module/asar.js index 624daaeaf7a28..6a973ad386045 100644 --- a/spec/fixtures/module/asar.js +++ b/spec/fixtures/module/asar.js @@ -1,4 +1,4 @@ -const fs = require('fs') +const fs = require('fs'); process.on('message', function (file) { - process.send(fs.readFileSync(file).toString()) -}) + process.send(fs.readFileSync(file).toString()); +}); diff --git a/spec/fixtures/module/call.js b/spec/fixtures/module/call.js index d09d677199b19..60315154e35e9 100644 --- a/spec/fixtures/module/call.js +++ b/spec/fixtures/module/call.js @@ -1,7 +1,7 @@ exports.call = function (func) { - return func() -} + return func(); +}; exports.constructor = function () { - this.test = 'test' -} + this.test = 'test'; +}; diff --git a/spec/fixtures/module/check-arguments.js b/spec/fixtures/module/check-arguments.js index 96f788e5b1a44..8a5ef8dde197d 100644 --- a/spec/fixtures/module/check-arguments.js +++ b/spec/fixtures/module/check-arguments.js @@ -1,4 +1,4 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.onload = function () { - ipcRenderer.send('answer', process.argv) -} + ipcRenderer.send('answer', process.argv); +}; diff --git a/spec/fixtures/module/circular.js b/spec/fixtures/module/circular.js index e8629c424acd9..e21b595bebb51 100644 --- a/spec/fixtures/module/circular.js +++ b/spec/fixtures/module/circular.js @@ -1,3 +1,3 @@ exports.returnArgs = function (...args) { - return args -} + return args; +}; diff --git a/spec/fixtures/module/class.js b/spec/fixtures/module/class.js index 9b971e52335b6..ca6a83685da50 100644 --- a/spec/fixtures/module/class.js +++ b/spec/fixtures/module/class.js @@ -1,22 +1,22 @@ -'use strict' +'use strict'; -let value = 'old' +let value = 'old'; class BaseClass { method () { - return 'method' + return 'method'; } get readonly () { - return 'readonly' + return 'readonly'; } get value () { - return value + return value; } set value (val) { - value = val + value = val; } } @@ -26,4 +26,4 @@ class DerivedClass extends BaseClass { module.exports = { base: new BaseClass(), derived: new DerivedClass() -} +}; diff --git a/spec/fixtures/module/crash.js b/spec/fixtures/module/crash.js index 2e15f1c6b19bb..a550fc4649fa6 100644 --- a/spec/fixtures/module/crash.js +++ b/spec/fixtures/module/crash.js @@ -8,12 +8,12 @@ process.crashReporter.start({ extra2: 'extra2', _version: process.argv[3] } -}) +}); if (process.platform !== 'linux') { - process.crashReporter.addExtraParameter('newExtra', 'newExtra') - process.crashReporter.addExtraParameter('removeExtra', 'removeExtra') - process.crashReporter.removeExtraParameter('removeExtra') + process.crashReporter.addExtraParameter('newExtra', 'newExtra'); + process.crashReporter.addExtraParameter('removeExtra', 'removeExtra'); + process.crashReporter.removeExtraParameter('removeExtra'); } -process.nextTick(() => process.crash()) +process.nextTick(() => process.crash()); diff --git a/spec/fixtures/module/create_socket.js b/spec/fixtures/module/create_socket.js index 5e1f49255d504..d5eca125a541e 100644 --- a/spec/fixtures/module/create_socket.js +++ b/spec/fixtures/module/create_socket.js @@ -1,4 +1,4 @@ -const net = require('net') -const server = net.createServer(function () {}) -server.listen(process.argv[2]) -process.exit(0) +const net = require('net'); +const server = net.createServer(function () {}); +server.listen(process.argv[2]); +process.exit(0); diff --git a/spec/fixtures/module/delay-exit.js b/spec/fixtures/module/delay-exit.js index fa7895449b32f..8fbe9d64f68d7 100644 --- a/spec/fixtures/module/delay-exit.js +++ b/spec/fixtures/module/delay-exit.js @@ -1,3 +1,3 @@ -const { app } = require('electron') +const { app } = require('electron'); -process.on('message', () => app.quit()) +process.on('message', () => app.quit()); diff --git a/spec/fixtures/module/delete-buffer.js b/spec/fixtures/module/delete-buffer.js index b90af7d6181ed..abbacb741497a 100644 --- a/spec/fixtures/module/delete-buffer.js +++ b/spec/fixtures/module/delete-buffer.js @@ -1,11 +1,11 @@ -const path = require('path') -const { remote } = require('electron') -const { Buffer } = window +const path = require('path'); +const { remote } = require('electron'); +const { Buffer } = window; -delete window.Buffer -delete global.Buffer +delete window.Buffer; +delete global.Buffer; // Test that remote.js doesn't use Buffer global -remote.require(path.join(__dirname, 'print_name.js')).echo(Buffer.from('bar')) +remote.require(path.join(__dirname, 'print_name.js')).echo(Buffer.from('bar')); -window.test = Buffer.from('buffer') +window.test = Buffer.from('buffer'); diff --git a/spec/fixtures/module/empty.js b/spec/fixtures/module/empty.js index 3f0e6f4f0707d..7dafbcc94f339 100644 --- a/spec/fixtures/module/empty.js +++ b/spec/fixtures/module/empty.js @@ -1,5 +1,5 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.addEventListener('message', (event) => { - ipcRenderer.send('leak-result', event.data) -}) + ipcRenderer.send('leak-result', event.data); +}); diff --git a/spec/fixtures/module/error-properties.js b/spec/fixtures/module/error-properties.js index c3a1e3b3a7f6a..ae7a30c0dcc0b 100644 --- a/spec/fixtures/module/error-properties.js +++ b/spec/fixtures/module/error-properties.js @@ -1,11 +1,11 @@ class Foo { set bar (value) { - throw new Error('setting error') + throw new Error('setting error'); } get bar () { - throw new Error('getting error') + throw new Error('getting error'); } } -module.exports = new Foo() +module.exports = new Foo(); diff --git a/spec/fixtures/module/exception.js b/spec/fixtures/module/exception.js index 1465833ff8da4..ca47ca0cf5f24 100644 --- a/spec/fixtures/module/exception.js +++ b/spec/fixtures/module/exception.js @@ -1,3 +1,3 @@ module.exports = function (error) { - throw error -} + throw error; +}; diff --git a/spec/fixtures/module/export-function-with-properties.js b/spec/fixtures/module/export-function-with-properties.js index 9df7d79deb246..a38142d20dfe9 100644 --- a/spec/fixtures/module/export-function-with-properties.js +++ b/spec/fixtures/module/export-function-with-properties.js @@ -1,4 +1,4 @@ function foo () {} -foo.bar = 'baz' +foo.bar = 'baz'; -module.exports = foo +module.exports = foo; diff --git a/spec/fixtures/module/fork_ping.js b/spec/fixtures/module/fork_ping.js index a2c8782600ec1..e9b28bde1d61c 100644 --- a/spec/fixtures/module/fork_ping.js +++ b/spec/fixtures/module/fork_ping.js @@ -1,16 +1,16 @@ -const path = require('path') +const path = require('path'); process.on('uncaughtException', function (error) { - process.send(error.stack) -}) + process.send(error.stack); +}); -const child = require('child_process').fork(path.join(__dirname, '/ping.js')) +const child = require('child_process').fork(path.join(__dirname, '/ping.js')); process.on('message', function (msg) { - child.send(msg) -}) + child.send(msg); +}); child.on('message', function (msg) { - process.send(msg) -}) + process.send(msg); +}); child.on('exit', function (code) { - process.exit(code) -}) + process.exit(code); +}); diff --git a/spec/fixtures/module/function-with-args.js b/spec/fixtures/module/function-with-args.js index ed636e5988a2d..b30317e975f03 100644 --- a/spec/fixtures/module/function-with-args.js +++ b/spec/fixtures/module/function-with-args.js @@ -1,3 +1,3 @@ module.exports = function (cb) { - return cb.length -} + return cb.length; +}; diff --git a/spec/fixtures/module/function-with-missing-properties.js b/spec/fixtures/module/function-with-missing-properties.js index d247485a17b69..3770eca4f5a8c 100644 --- a/spec/fixtures/module/function-with-missing-properties.js +++ b/spec/fixtures/module/function-with-missing-properties.js @@ -1,13 +1,13 @@ exports.setup = function () { - const foo = {} + const foo = {}; foo.bar = function () { - return delete foo.bar.baz && delete foo.bar - } + return delete foo.bar.baz && delete foo.bar; + }; foo.bar.baz = function () { - return 3 - } + return 3; + }; - return foo -} + return foo; +}; diff --git a/spec/fixtures/module/function-with-properties.js b/spec/fixtures/module/function-with-properties.js index 3ae617133e7e9..d6d82d65b0be2 100644 --- a/spec/fixtures/module/function-with-properties.js +++ b/spec/fixtures/module/function-with-properties.js @@ -1,17 +1,17 @@ function foo () { - return 'hello' + return 'hello'; } -foo.bar = 'baz' +foo.bar = 'baz'; foo.nested = { prop: 'yes' -} +}; foo.method1 = function () { - return 'world' -} + return 'world'; +}; foo.method1.prop1 = function () { - return 123 -} + return 123; +}; module.exports = { foo: foo -} +}; diff --git a/spec/fixtures/module/function.js b/spec/fixtures/module/function.js index 8a2bb6c421ec8..485d990798bcf 100644 --- a/spec/fixtures/module/function.js +++ b/spec/fixtures/module/function.js @@ -1 +1 @@ -exports.aFunction = function () { return 1127 } +exports.aFunction = function () { return 1127; }; diff --git a/spec/fixtures/module/get-global-preload.js b/spec/fixtures/module/get-global-preload.js index c1010dd472b2b..1f02aa132bc56 100644 --- a/spec/fixtures/module/get-global-preload.js +++ b/spec/fixtures/module/get-global-preload.js @@ -1 +1 @@ -require('electron').ipcRenderer.send('vars', window.preload1, window.preload2, window.preload3) +require('electron').ipcRenderer.send('vars', window.preload1, window.preload2, window.preload3); diff --git a/spec/fixtures/module/hello-child.js b/spec/fixtures/module/hello-child.js index 09ac18900e162..a40ab2165751b 100644 --- a/spec/fixtures/module/hello-child.js +++ b/spec/fixtures/module/hello-child.js @@ -1,6 +1,6 @@ class Hello { say () { - return 'hi child window' + return 'hi child window'; } } -module.exports = Hello +module.exports = Hello; diff --git a/spec/fixtures/module/hello.js b/spec/fixtures/module/hello.js index 9debd60618ecc..49d2cb827ef29 100644 --- a/spec/fixtures/module/hello.js +++ b/spec/fixtures/module/hello.js @@ -1,6 +1,6 @@ class Hello { say () { - return 'hi' + return 'hi'; } } -module.exports = Hello +module.exports = Hello; diff --git a/spec/fixtures/module/id.js b/spec/fixtures/module/id.js index 2faec9d383216..5bfae457fe0bf 100644 --- a/spec/fixtures/module/id.js +++ b/spec/fixtures/module/id.js @@ -1 +1 @@ -exports.id = 1127 +exports.id = 1127; diff --git a/spec/fixtures/module/inspector-binding.js b/spec/fixtures/module/inspector-binding.js index 64505429508bd..9223153a84ff5 100644 --- a/spec/fixtures/module/inspector-binding.js +++ b/spec/fixtures/module/inspector-binding.js @@ -1,18 +1,18 @@ -const inspector = require('inspector') -const path = require('path') -const { pathToFileURL } = require('url') +const inspector = require('inspector'); +const path = require('path'); +const { pathToFileURL } = require('url'); // This test case will set a breakpoint 4 lines below function debuggedFunction () { - let i - let accum = 0 + let i; + let accum = 0; for (i = 0; i < 5; i++) { - accum += i + accum += i; } - return accum + return accum; } -let scopeCallback = null +let scopeCallback = null; function checkScope (session, scopeId) { session.post('Runtime.getProperties', { @@ -20,57 +20,57 @@ function checkScope (session, scopeId) { 'ownProperties': false, 'accessorPropertiesOnly': false, 'generatePreview': true - }, scopeCallback) + }, scopeCallback); } function debuggerPausedCallback (session, notification) { - const params = notification['params'] - const callFrame = params['callFrames'][0] - const scopeId = callFrame['scopeChain'][0]['object']['objectId'] - checkScope(session, scopeId) + const params = notification['params']; + const callFrame = params['callFrames'][0]; + const scopeId = callFrame['scopeChain'][0]['object']['objectId']; + checkScope(session, scopeId); } function testSampleDebugSession () { - let cur = 0 - const failures = [] + let cur = 0; + const failures = []; const expects = { i: [0, 1, 2, 3, 4], accum: [0, 0, 1, 3, 6] - } + }; scopeCallback = function (error, result) { - if (error) failures.push(error) - const i = cur++ - let v, actual, expected + if (error) failures.push(error); + const i = cur++; + let v, actual, expected; for (v of result['result']) { - actual = v['value']['value'] - expected = expects[v['name']][i] + actual = v['value']['value']; + expected = expects[v['name']][i]; if (actual !== expected) { failures.push(`Iteration ${i} variable: ${v['name']} ` + - `expected: ${expected} actual: ${actual}`) + `expected: ${expected} actual: ${actual}`); } } - } - const session = new inspector.Session() - session.connect() + }; + const session = new inspector.Session(); + session.connect(); session.on('Debugger.paused', - (notification) => debuggerPausedCallback(session, notification)) - let cbAsSecondArgCalled = false - session.post('Debugger.enable', () => { cbAsSecondArgCalled = true }) + (notification) => debuggerPausedCallback(session, notification)); + let cbAsSecondArgCalled = false; + session.post('Debugger.enable', () => { cbAsSecondArgCalled = true; }); session.post('Debugger.setBreakpointByUrl', { 'lineNumber': 9, 'url': pathToFileURL(path.resolve(__dirname, __filename)).toString(), 'columnNumber': 0, 'condition': '' - }) + }); - debuggedFunction() - scopeCallback = null - session.disconnect() + debuggedFunction(); + scopeCallback = null; + session.disconnect(); process.send({ 'cmd': 'assert', 'debuggerEnabled': cbAsSecondArgCalled, 'success': (cur === 5) && (failures.length === 0) - }) + }); } -testSampleDebugSession() +testSampleDebugSession(); diff --git a/spec/fixtures/module/isolated-ping.js b/spec/fixtures/module/isolated-ping.js index 7088d346c8ef1..90392e46fe4de 100644 --- a/spec/fixtures/module/isolated-ping.js +++ b/spec/fixtures/module/isolated-ping.js @@ -1,2 +1,2 @@ -const { ipcRenderer } = require('electron') -ipcRenderer.send('pong') +const { ipcRenderer } = require('electron'); +ipcRenderer.send('pong'); diff --git a/spec/fixtures/module/locale-compare.js b/spec/fixtures/module/locale-compare.js index 4027540044347..48c4ebbdc8a47 100644 --- a/spec/fixtures/module/locale-compare.js +++ b/spec/fixtures/module/locale-compare.js @@ -3,5 +3,5 @@ process.on('message', function () { 'a'.localeCompare('a'), 'ä'.localeCompare('z', 'de'), 'ä'.localeCompare('a', 'sv', { sensitivity: 'base' }) - ]) -}) + ]); +}); diff --git a/spec/fixtures/module/no-asar.js b/spec/fixtures/module/no-asar.js index 400a8525ffa47..8835a22c42d1e 100644 --- a/spec/fixtures/module/no-asar.js +++ b/spec/fixtures/module/no-asar.js @@ -1,15 +1,15 @@ -const fs = require('fs') -const path = require('path') +const fs = require('fs'); +const path = require('path'); -const stats = fs.statSync(path.join(__dirname, '..', 'test.asar', 'a.asar')) +const stats = fs.statSync(path.join(__dirname, '..', 'test.asar', 'a.asar')); const details = { isFile: stats.isFile(), size: stats.size -} +}; if (process.send != null) { - process.send(details) + process.send(details); } else { - console.log(JSON.stringify(details)) + console.log(JSON.stringify(details)); } diff --git a/spec/fixtures/module/no-prototype.js b/spec/fixtures/module/no-prototype.js index 000eb183fffb7..54eaeb8477216 100644 --- a/spec/fixtures/module/no-prototype.js +++ b/spec/fixtures/module/no-prototype.js @@ -1,11 +1,11 @@ -const foo = Object.create(null) -foo.bar = 'baz' -foo.baz = false +const foo = Object.create(null); +foo.bar = 'baz'; +foo.baz = false; module.exports = { foo: foo, bar: 1234, anonymous: new (class {})(), getConstructorName: function (value) { - return value.constructor.name + return value.constructor.name; } -} +}; diff --git a/spec/fixtures/module/node-promise-timer.js b/spec/fixtures/module/node-promise-timer.js new file mode 100644 index 0000000000000..b21c24f5dfc36 --- /dev/null +++ b/spec/fixtures/module/node-promise-timer.js @@ -0,0 +1,23 @@ +const waitMs = (msec) => new Promise((resolve) => setTimeout(resolve, msec)); + +const intervalMsec = 100; +const numIterations = 2; +let curIteration = 0; +let promise; + +for (let i = 0; i < numIterations; i++) { + promise = (promise || waitMs(intervalMsec)).then(() => { + ++curIteration; + return waitMs(intervalMsec); + }); +} + +// https://github.com/electron/electron/issues/21515 was about electron +// exiting before promises finished. This test sets the pending exitCode +// to failure, then resets it to success only if all promises finish. +process.exitCode = 1; +promise.then(() => { + if (curIteration === numIterations) { + process.exitCode = 0; + } +}); diff --git a/spec/fixtures/module/noop.js b/spec/fixtures/module/noop.js index 23d7e9991574b..dcbbff6c93458 100644 --- a/spec/fixtures/module/noop.js +++ b/spec/fixtures/module/noop.js @@ -1 +1 @@ -process.exit(0) +process.exit(0); diff --git a/spec/fixtures/module/original-fs.js b/spec/fixtures/module/original-fs.js index 341dcb2e0de80..7a527c6335853 100644 --- a/spec/fixtures/module/original-fs.js +++ b/spec/fixtures/module/original-fs.js @@ -1,3 +1,3 @@ process.on('message', function () { - process.send(typeof require('original-fs')) -}) + process.send(typeof require('original-fs')); +}); diff --git a/spec/fixtures/module/ping.js b/spec/fixtures/module/ping.js index 90b3d1fb20a18..7479ac7419fa6 100644 --- a/spec/fixtures/module/ping.js +++ b/spec/fixtures/module/ping.js @@ -1,4 +1,4 @@ process.on('message', function (msg) { - process.send(msg) - process.exit(0) -}) + process.send(msg); + process.exit(0); +}); diff --git a/spec/fixtures/module/preload-context.js b/spec/fixtures/module/preload-context.js index 3d3f8bc9755cc..4dbc3a9a58d3b 100644 --- a/spec/fixtures/module/preload-context.js +++ b/spec/fixtures/module/preload-context.js @@ -5,6 +5,6 @@ const types = { electron: typeof electron, window: typeof window, localVar: typeof window.test -} +}; -console.log(JSON.stringify(types)) +console.log(JSON.stringify(types)); diff --git a/spec/fixtures/module/preload-disable-remote.js b/spec/fixtures/module/preload-disable-remote.js index 008acec7b5d60..9b6b96cbf28f5 100644 --- a/spec/fixtures/module/preload-disable-remote.js +++ b/spec/fixtures/module/preload-disable-remote.js @@ -1,8 +1,8 @@ setImmediate(function () { try { - const { remote } = require('electron') - console.log(JSON.stringify(typeof remote)) + const { remote } = require('electron'); + console.log(JSON.stringify(typeof remote)); } catch (e) { - console.log(e.message) + console.log(e.message); } -}) +}); diff --git a/spec/fixtures/module/preload-error-exception.js b/spec/fixtures/module/preload-error-exception.js index 710907d35a33a..ec3582c249628 100644 --- a/spec/fixtures/module/preload-error-exception.js +++ b/spec/fixtures/module/preload-error-exception.js @@ -1 +1 @@ -throw new Error('Hello World!') +throw new Error('Hello World!'); diff --git a/spec/fixtures/module/preload-ipc-ping-pong.js b/spec/fixtures/module/preload-ipc-ping-pong.js index 6ea0b32fdad95..41d4c75382230 100644 --- a/spec/fixtures/module/preload-ipc-ping-pong.js +++ b/spec/fixtures/module/preload-ipc-ping-pong.js @@ -1,9 +1,9 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); ipcRenderer.on('ping', function (event, payload) { - ipcRenderer.sendTo(event.senderId, 'pong', payload) -}) + ipcRenderer.sendTo(event.senderId, 'pong', payload); +}); ipcRenderer.on('ping-æøåü', function (event, payload) { - ipcRenderer.sendTo(event.senderId, 'pong-æøåü', payload) -}) + ipcRenderer.sendTo(event.senderId, 'pong-æøåü', payload); +}); diff --git a/spec/fixtures/module/preload-ipc.js b/spec/fixtures/module/preload-ipc.js index 3f97d1a56b3bb..390fa920dfa09 100644 --- a/spec/fixtures/module/preload-ipc.js +++ b/spec/fixtures/module/preload-ipc.js @@ -1,4 +1,4 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); ipcRenderer.on('ping', function (event, message) { - ipcRenderer.sendToHost('pong', message) -}) + ipcRenderer.sendToHost('pong', message); +}); diff --git a/spec/fixtures/module/preload-node-off-wrapper.js b/spec/fixtures/module/preload-node-off-wrapper.js index dbe1330adcf3c..614f0db1c68bd 100644 --- a/spec/fixtures/module/preload-node-off-wrapper.js +++ b/spec/fixtures/module/preload-node-off-wrapper.js @@ -1,3 +1,3 @@ setImmediate(function () { - require('./preload-required-module') -}) + require('./preload-required-module'); +}); diff --git a/spec/fixtures/module/preload-node-off.js b/spec/fixtures/module/preload-node-off.js index 65db2a7e4dc05..0b21b2ae32d5d 100644 --- a/spec/fixtures/module/preload-node-off.js +++ b/spec/fixtures/module/preload-node-off.js @@ -5,9 +5,9 @@ setImmediate(function () { setImmediate: typeof setImmediate, global: typeof global, Buffer: typeof Buffer - } - console.log(JSON.stringify(types)) + }; + console.log(JSON.stringify(types)); } catch (e) { - console.log(e.message) + console.log(e.message); } -}) +}); diff --git a/spec/fixtures/module/preload-pdf-loaded-in-nested-subframe.js b/spec/fixtures/module/preload-pdf-loaded-in-nested-subframe.js index 3e631a7ffb64c..e72a4a4058db4 100644 --- a/spec/fixtures/module/preload-pdf-loaded-in-nested-subframe.js +++ b/spec/fixtures/module/preload-pdf-loaded-in-nested-subframe.js @@ -1,15 +1,15 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); document.addEventListener('DOMContentLoaded', (event) => { - const outerFrame = document.querySelector('#outer-frame') + const outerFrame = document.querySelector('#outer-frame'); if (outerFrame) { outerFrame.onload = function () { - const pdframe = outerFrame.contentWindow.document.getElementById('pdf-frame') + const pdframe = outerFrame.contentWindow.document.getElementById('pdf-frame'); if (pdframe) { pdframe.contentWindow.addEventListener('pdf-loaded', (event) => { - ipcRenderer.send('pdf-loaded', event.detail) - }) + ipcRenderer.send('pdf-loaded', event.detail); + }); } - } + }; } -}) +}); diff --git a/spec/fixtures/module/preload-pdf-loaded-in-subframe.js b/spec/fixtures/module/preload-pdf-loaded-in-subframe.js index 40ba24a4d958c..dd7a7aaa42d6d 100644 --- a/spec/fixtures/module/preload-pdf-loaded-in-subframe.js +++ b/spec/fixtures/module/preload-pdf-loaded-in-subframe.js @@ -1,10 +1,10 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); document.addEventListener('DOMContentLoaded', (event) => { - const subframe = document.querySelector('#pdf-frame') + const subframe = document.querySelector('#pdf-frame'); if (subframe) { subframe.contentWindow.addEventListener('pdf-loaded', (event) => { - ipcRenderer.send('pdf-loaded', event.detail) - }) + ipcRenderer.send('pdf-loaded', event.detail); + }); } -}) +}); diff --git a/spec/fixtures/module/preload-pdf-loaded.js b/spec/fixtures/module/preload-pdf-loaded.js index 9393898b50846..aa5c8fb4ffac6 100644 --- a/spec/fixtures/module/preload-pdf-loaded.js +++ b/spec/fixtures/module/preload-pdf-loaded.js @@ -1,5 +1,5 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.addEventListener('pdf-loaded', function (event) { - ipcRenderer.send('pdf-loaded', event.detail) -}) + ipcRenderer.send('pdf-loaded', event.detail); +}); diff --git a/spec/fixtures/module/preload-remote-function.js b/spec/fixtures/module/preload-remote-function.js index e9d5c3311c3ef..f33eff6ef611f 100644 --- a/spec/fixtures/module/preload-remote-function.js +++ b/spec/fixtures/module/preload-remote-function.js @@ -1,5 +1,5 @@ -const { remote, ipcRenderer } = require('electron') +const { remote, ipcRenderer } = require('electron'); remote.getCurrentWindow().rendererFunc = () => { - ipcRenderer.send('done') -} -remote.getCurrentWindow().rendererFunc() + ipcRenderer.send('done'); +}; +remote.getCurrentWindow().rendererFunc(); diff --git a/spec/fixtures/module/preload-remote.js b/spec/fixtures/module/preload-remote.js index a9014c726ff6c..035487be29ac6 100644 --- a/spec/fixtures/module/preload-remote.js +++ b/spec/fixtures/module/preload-remote.js @@ -1,5 +1,5 @@ -const { ipcRenderer, remote } = require('electron') +const { ipcRenderer, remote } = require('electron'); window.onload = function () { - ipcRenderer.send('remote', typeof remote) -} + ipcRenderer.send('remote', typeof remote); +}; diff --git a/spec/fixtures/module/preload-required-module.js b/spec/fixtures/module/preload-required-module.js index 75b2bf4add880..23ff1780021cd 100644 --- a/spec/fixtures/module/preload-required-module.js +++ b/spec/fixtures/module/preload-required-module.js @@ -5,8 +5,8 @@ try { global: typeof global, Buffer: typeof Buffer, 'global.Buffer': typeof global.Buffer - } - console.log(JSON.stringify(types)) + }; + console.log(JSON.stringify(types)); } catch (e) { - console.log(e.message) + console.log(e.message); } diff --git a/spec/fixtures/module/preload-sandbox.js b/spec/fixtures/module/preload-sandbox.js index d62e0e402abbc..524771046a0eb 100644 --- a/spec/fixtures/module/preload-sandbox.js +++ b/spec/fixtures/module/preload-sandbox.js @@ -1,25 +1,25 @@ (function () { - const { setImmediate } = require('timers') - const { ipcRenderer } = require('electron') - window.ipcRenderer = ipcRenderer - window.setImmediate = setImmediate - window.require = require + const { setImmediate } = require('timers'); + const { ipcRenderer } = require('electron'); + window.ipcRenderer = ipcRenderer; + window.setImmediate = setImmediate; + window.require = require; function invoke (code) { try { - return code() + return code(); } catch { - return null + return null; } } process.once('loaded', () => { - ipcRenderer.send('process-loaded') - }) + ipcRenderer.send('process-loaded'); + }); if (location.protocol === 'file:') { - window.test = 'preload' - window.process = process + window.test = 'preload'; + window.process = process; if (process.env.sandboxmain) { window.test = { osSandbox: !process.argv.includes('--no-sandbox'), @@ -42,20 +42,20 @@ type: process.type, version: process.version, versions: process.versions - } + }; } } else if (location.href !== 'about:blank') { addEventListener('DOMContentLoaded', () => { ipcRenderer.on('touch-the-opener', () => { - let errorMessage = null + let errorMessage = null; try { - const openerDoc = opener.document // eslint-disable-line no-unused-vars + const openerDoc = opener.document; // eslint-disable-line no-unused-vars } catch (error) { - errorMessage = error.message + errorMessage = error.message; } - ipcRenderer.send('answer', errorMessage) - }) - ipcRenderer.send('child-loaded', window.opener == null, document.body.innerHTML, location.href) - }) + ipcRenderer.send('answer', errorMessage); + }); + ipcRenderer.send('child-loaded', window.opener == null, document.body.innerHTML, location.href); + }); } -})() +})(); diff --git "a/spec/fixtures/module/preload-sandbox\303\246\303\270 \303\245\303\274.js" "b/spec/fixtures/module/preload-sandbox\303\246\303\270 \303\245\303\274.js" index 3799c8e69c7af..596e9ca4c12ea 100644 --- "a/spec/fixtures/module/preload-sandbox\303\246\303\270 \303\245\303\274.js" +++ "b/spec/fixtures/module/preload-sandbox\303\246\303\270 \303\245\303\274.js" @@ -1,6 +1,6 @@ (function () { - window.require = require + window.require = require; if (location.protocol === 'file:') { - window.test = 'preload' + window.test = 'preload'; } -})() +})(); diff --git a/spec/fixtures/module/preload-set-global.js b/spec/fixtures/module/preload-set-global.js index 6737b06982dc8..ff1dff5e793a6 100644 --- a/spec/fixtures/module/preload-set-global.js +++ b/spec/fixtures/module/preload-set-global.js @@ -1 +1 @@ -window.foo = 'bar' +window.foo = 'bar'; diff --git a/spec/fixtures/module/preload-webview.js b/spec/fixtures/module/preload-webview.js index b1386a95280c9..1789551556b57 100644 --- a/spec/fixtures/module/preload-webview.js +++ b/spec/fixtures/module/preload-webview.js @@ -1,5 +1,5 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.onload = function () { - ipcRenderer.send('webview', typeof WebView) -} + ipcRenderer.send('webview', typeof WebView); +}; diff --git a/spec/fixtures/module/preload.js b/spec/fixtures/module/preload.js index 6b77bde032940..aa7bba4cae8f0 100644 --- a/spec/fixtures/module/preload.js +++ b/spec/fixtures/module/preload.js @@ -3,5 +3,5 @@ const types = { module: typeof module, process: typeof process, Buffer: typeof Buffer -} -console.log(JSON.stringify(types)) +}; +console.log(JSON.stringify(types)); diff --git a/spec/fixtures/module/print_name.js b/spec/fixtures/module/print_name.js index 8583af00f4abc..047cab14d2d5f 100644 --- a/spec/fixtures/module/print_name.js +++ b/spec/fixtures/module/print_name.js @@ -1,10 +1,10 @@ exports.print = function (obj) { - return obj.constructor.name -} + return obj.constructor.name; +}; exports.echo = function (obj) { - return obj -} + return obj; +}; const typedArrays = { Int8Array, @@ -16,21 +16,21 @@ const typedArrays = { Uint32Array, Float32Array, Float64Array -} +}; exports.typedArray = function (type, values) { - const constructor = typedArrays[type] - const array = new constructor(values.length) + const constructor = typedArrays[type]; + const array = new constructor(values.length); for (let i = 0; i < values.length; ++i) { - array[i] = values[i] + array[i] = values[i]; } - return array -} + return array; +}; exports.getNaN = function () { - return NaN -} + return NaN; +}; exports.getInfinity = function () { - return Infinity -} + return Infinity; +}; diff --git a/spec/fixtures/module/process-stdout.js b/spec/fixtures/module/process-stdout.js index 953750a247f99..f45b5d60408d3 100644 --- a/spec/fixtures/module/process-stdout.js +++ b/spec/fixtures/module/process-stdout.js @@ -1 +1 @@ -process.stdout.write('pipes stdio') +process.stdout.write('pipes stdio'); diff --git a/spec/fixtures/module/process_args.js b/spec/fixtures/module/process_args.js index 56e3906c55345..88191450f1cb3 100644 --- a/spec/fixtures/module/process_args.js +++ b/spec/fixtures/module/process_args.js @@ -1,4 +1,4 @@ process.on('message', function () { - process.send(process.argv) - process.exit(0) -}) + process.send(process.argv); + process.exit(0); +}); diff --git a/spec/fixtures/module/promise.js b/spec/fixtures/module/promise.js index d34058cc80336..b9b568855e301 100644 --- a/spec/fixtures/module/promise.js +++ b/spec/fixtures/module/promise.js @@ -1,5 +1,5 @@ exports.twicePromise = function (promise) { return promise.then(function (value) { - return value * 2 - }) -} + return value * 2; + }); +}; diff --git a/spec/fixtures/module/property.js b/spec/fixtures/module/property.js index e570ca9351a90..6d15b3d3f89f9 100644 --- a/spec/fixtures/module/property.js +++ b/spec/fixtures/module/property.js @@ -1,11 +1,11 @@ -exports.property = 1127 +exports.property = 1127; function func () { } -func.property = 'foo' -exports.func = func +func.property = 'foo'; +exports.func = func; exports.getFunctionProperty = () => { - return `${func.property}-${process.type}` -} + return `${func.property}-${process.type}`; +}; diff --git a/spec/fixtures/module/rejected-promise.js b/spec/fixtures/module/rejected-promise.js index 93dd9accc0308..74c939b2d86c1 100644 --- a/spec/fixtures/module/rejected-promise.js +++ b/spec/fixtures/module/rejected-promise.js @@ -1,5 +1,5 @@ exports.reject = function (promise) { return promise.then(function () { - throw Error('rejected') - }) -} + throw Error('rejected'); + }); +}; diff --git a/spec/fixtures/module/remote-object-set.js b/spec/fixtures/module/remote-object-set.js index 74c574722037d..da4f030928e8c 100644 --- a/spec/fixtures/module/remote-object-set.js +++ b/spec/fixtures/module/remote-object-set.js @@ -1,11 +1,11 @@ -const { BrowserWindow } = require('electron') +const { BrowserWindow } = require('electron'); class Foo { set bar (value) { if (!(value instanceof BrowserWindow)) { - throw new Error('setting error') + throw new Error('setting error'); } } } -module.exports = new Foo() +module.exports = new Foo(); diff --git a/spec/fixtures/module/remote-static.js b/spec/fixtures/module/remote-static.js index ed35b1d4476a8..1e005ef5dded4 100644 --- a/spec/fixtures/module/remote-static.js +++ b/spec/fixtures/module/remote-static.js @@ -1,15 +1,15 @@ class Foo { static foo () { - return 3 + return 3; } baz () { - return 123 + return 123; } } -Foo.bar = 'baz' +Foo.bar = 'baz'; module.exports = { Foo: Foo -} +}; diff --git a/spec/fixtures/module/run-as-node.js b/spec/fixtures/module/run-as-node.js index 20812f0463eb5..4d033c7e8d196 100644 --- a/spec/fixtures/module/run-as-node.js +++ b/spec/fixtures/module/run-as-node.js @@ -2,4 +2,4 @@ console.log(JSON.stringify({ processLog: typeof process.log, processType: typeof process.type, window: typeof window -})) +})); diff --git a/spec/fixtures/module/send-later.js b/spec/fixtures/module/send-later.js index 34250aedce3fb..5b4a22097275c 100644 --- a/spec/fixtures/module/send-later.js +++ b/spec/fixtures/module/send-later.js @@ -1,4 +1,4 @@ -const { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron'); window.onload = function () { - ipcRenderer.send('answer', typeof window.process, typeof window.Buffer) -} + ipcRenderer.send('answer', typeof window.process, typeof window.Buffer); +}; diff --git a/spec/fixtures/module/set-global-preload-1.js b/spec/fixtures/module/set-global-preload-1.js index 22dfdf918506e..92e8741de1771 100644 --- a/spec/fixtures/module/set-global-preload-1.js +++ b/spec/fixtures/module/set-global-preload-1.js @@ -1 +1 @@ -window.preload1 = 'preload-1' +window.preload1 = 'preload-1'; diff --git a/spec/fixtures/module/set-global-preload-2.js b/spec/fixtures/module/set-global-preload-2.js index 7542009f7b09a..af100270d070c 100644 --- a/spec/fixtures/module/set-global-preload-2.js +++ b/spec/fixtures/module/set-global-preload-2.js @@ -1 +1 @@ -window.preload2 = window.preload1 + '-2' +window.preload2 = window.preload1 + '-2'; diff --git a/spec/fixtures/module/set-global-preload-3.js b/spec/fixtures/module/set-global-preload-3.js index 9cfef949277ed..8491be46727cf 100644 --- a/spec/fixtures/module/set-global-preload-3.js +++ b/spec/fixtures/module/set-global-preload-3.js @@ -1 +1 @@ -window.preload3 = window.preload2 + '-3' +window.preload3 = window.preload2 + '-3'; diff --git a/spec/fixtures/module/set-global.js b/spec/fixtures/module/set-global.js index 5ad98817e0bd5..c63ed6cf4486f 100644 --- a/spec/fixtures/module/set-global.js +++ b/spec/fixtures/module/set-global.js @@ -1 +1 @@ -if (!window.test) window.test = 'preload' +if (!window.test) window.test = 'preload'; diff --git a/spec/fixtures/module/set-immediate.js b/spec/fixtures/module/set-immediate.js index 69563fd0a832f..263801c3b9bc4 100644 --- a/spec/fixtures/module/set-immediate.js +++ b/spec/fixtures/module/set-immediate.js @@ -1,11 +1,11 @@ process.on('uncaughtException', function (error) { - process.send(error.message) - process.exit(1) -}) + process.send(error.message); + process.exit(1); +}); process.on('message', function () { setImmediate(function () { - process.send('ok') - process.exit(0) - }) -}) + process.send('ok'); + process.exit(0); + }); +}); diff --git a/spec/fixtures/module/to-string-non-function.js b/spec/fixtures/module/to-string-non-function.js index a898fc4892386..1c47cfe290355 100644 --- a/spec/fixtures/module/to-string-non-function.js +++ b/spec/fixtures/module/to-string-non-function.js @@ -1,4 +1,4 @@ function hello () { } -hello.toString = 'hello' -module.exports = { functionWithToStringProperty: hello } +hello.toString = 'hello'; +module.exports = { functionWithToStringProperty: hello }; diff --git a/spec/fixtures/module/unhandled-rejection.js b/spec/fixtures/module/unhandled-rejection.js index 6cb870ec88625..bd0a7b6538273 100644 --- a/spec/fixtures/module/unhandled-rejection.js +++ b/spec/fixtures/module/unhandled-rejection.js @@ -1,3 +1,3 @@ exports.reject = function () { - return Promise.reject(new Error('rejected')) -} + return Promise.reject(new Error('rejected')); +}; diff --git a/spec/fixtures/no-proprietary-codecs.js b/spec/fixtures/no-proprietary-codecs.js index 741927b44f27b..cc262391d333d 100644 --- a/spec/fixtures/no-proprietary-codecs.js +++ b/spec/fixtures/no-proprietary-codecs.js @@ -4,13 +4,13 @@ // proprietary codecs to ensure Electron uses it instead of the version // that does include proprietary codecs. -const { app, BrowserWindow, ipcMain } = require('electron') -const path = require('path') +const { app, BrowserWindow, ipcMain } = require('electron'); +const path = require('path'); -const MEDIA_ERR_SRC_NOT_SUPPORTED = 4 -const FIVE_MINUTES = 5 * 60 * 1000 +const MEDIA_ERR_SRC_NOT_SUPPORTED = 4; +const FIVE_MINUTES = 5 * 60 * 1000; -let window +let window; app.once('ready', () => { window = new BrowserWindow({ @@ -18,33 +18,33 @@ app.once('ready', () => { webPreferences: { nodeIntegration: true } - }) + }); window.webContents.on('crashed', (event, killed) => { - console.log(`WebContents crashed (killed=${killed})`) - app.exit(1) - }) + console.log(`WebContents crashed (killed=${killed})`); + app.exit(1); + }); - window.loadFile(path.resolve(__dirname, 'test.asar', 'video.asar', 'index.html')) + window.loadFile(path.resolve(__dirname, 'test.asar', 'video.asar', 'index.html')); ipcMain.on('asar-video', (event, message, error) => { if (message === 'ended') { - console.log('Video played, proprietary codecs are included') - app.exit(1) - return + console.log('Video played, proprietary codecs are included'); + app.exit(1); + return; } if (message === 'error' && error === MEDIA_ERR_SRC_NOT_SUPPORTED) { - app.exit(0) - return + app.exit(0); + return; } - console.log(`Unexpected response from page: ${message} ${error}`) - app.exit(1) - }) + console.log(`Unexpected response from page: ${message} ${error}`); + app.exit(1); + }); setTimeout(() => { - console.log('No IPC message after 5 minutes') - app.exit(1) - }, FIVE_MINUTES) -}) + console.log('No IPC message after 5 minutes'); + app.exit(1); + }, FIVE_MINUTES); +}); diff --git a/spec/fixtures/pages/form-with-data.html b/spec/fixtures/pages/form-with-data.html new file mode 100644 index 0000000000000..711f6196d525f --- /dev/null +++ b/spec/fixtures/pages/form-with-data.html @@ -0,0 +1,8 @@ + + +
+ + +
+ + diff --git a/spec/fixtures/pages/fullscreen-ipif.html b/spec/fixtures/pages/fullscreen-ipif.html new file mode 100644 index 0000000000000..dfca5396e1900 --- /dev/null +++ b/spec/fixtures/pages/fullscreen-ipif.html @@ -0,0 +1,15 @@ + + + + diff --git a/spec/fixtures/pages/fullscreen-oopif.html b/spec/fixtures/pages/fullscreen-oopif.html new file mode 100644 index 0000000000000..db9a24d77ff6c --- /dev/null +++ b/spec/fixtures/pages/fullscreen-oopif.html @@ -0,0 +1,19 @@ + + diff --git a/spec/fixtures/pages/service-worker/service-worker.js b/spec/fixtures/pages/service-worker/service-worker.js index a068d74046dc0..1de4a9e4740eb 100644 --- a/spec/fixtures/pages/service-worker/service-worker.js +++ b/spec/fixtures/pages/service-worker/service-worker.js @@ -1,9 +1,9 @@ self.addEventListener('fetch', function (event) { - const requestUrl = new URL(event.request.url) + const requestUrl = new URL(event.request.url); if (requestUrl.pathname === '/echo' && event.request.headers.has('X-Mock-Response')) { - const mockResponse = new Response('Hello from serviceWorker!') - event.respondWith(mockResponse) + const mockResponse = new Response('Hello from serviceWorker!'); + event.respondWith(mockResponse); } -}) +}); diff --git a/spec/fixtures/pages/storage/web_sql.html b/spec/fixtures/pages/storage/web_sql.html index 8ba4eec9252be..7d737a382ee51 100644 --- a/spec/fixtures/pages/storage/web_sql.html +++ b/spec/fixtures/pages/storage/web_sql.html @@ -1,8 +1,9 @@ diff --git a/spec/fixtures/pages/webview-devtools.html b/spec/fixtures/pages/webview-devtools.html index 8928c4ea79883..fb54b9b18aeb1 100644 --- a/spec/fixtures/pages/webview-devtools.html +++ b/spec/fixtures/pages/webview-devtools.html @@ -8,7 +8,8 @@ diff --git a/spec/fixtures/pages/window-open-postMessage.html b/spec/fixtures/pages/window-open-postMessage.html index ea97e593f5b76..e0a42b8b9c369 100644 --- a/spec/fixtures/pages/window-open-postMessage.html +++ b/spec/fixtures/pages/window-open-postMessage.html @@ -5,7 +5,7 @@ window.opener.postMessage(JSON.stringify({ origin: e.origin, data: e.data, - sourceEqualsOpener: e.source === window.opener + sourceEqualsOpener: e.source.location.href === window.opener.location.href }), '*'); }); window.opener.postMessage("ready", "*") diff --git a/spec/fixtures/snapshot-items-available/main.js b/spec/fixtures/snapshot-items-available/main.js index 601853a294776..58f027402198f 100644 --- a/spec/fixtures/snapshot-items-available/main.js +++ b/spec/fixtures/snapshot-items-available/main.js @@ -1,26 +1,26 @@ // Verifies that objects contained in custom snapshot are accessible in Electron. -const { app } = require('electron') +const { app } = require('electron'); app.on('ready', () => { - let returnCode = 0 + let returnCode = 0; try { - const testValue = f() // eslint-disable-line no-undef + const testValue = f(); // eslint-disable-line no-undef if (testValue === 86) { - console.log('ok test snapshot successfully loaded.') + console.log('ok test snapshot successfully loaded.'); } else { - console.log('not ok test snapshot could not be successfully loaded.') - returnCode = 1 + console.log('not ok test snapshot could not be successfully loaded.'); + returnCode = 1; } } catch (ex) { - console.log('Error running custom snapshot', ex) - returnCode = 1 + console.log('Error running custom snapshot', ex); + returnCode = 1; } setImmediate(function () { - app.exit(returnCode) - }) -}) + app.exit(returnCode); + }); +}); process.on('exit', function (code) { - console.log('test snapshot exited with code: ' + code) -}) + console.log('test snapshot exited with code: ' + code); +}); diff --git a/spec/fixtures/testsnap.js b/spec/fixtures/testsnap.js index 394aa9f4d8028..3ac89c522a8bb 100644 --- a/spec/fixtures/testsnap.js +++ b/spec/fixtures/testsnap.js @@ -1,3 +1,3 @@ // taken from https://chromium.googlesource.com/v8/v8.git/+/HEAD/test/cctest/test-serialize.cc#1127 -function f () { return g() * 2 } // eslint-disable-line no-unused-vars -function g () { return 43 } +function f () { return g() * 2; } // eslint-disable-line no-unused-vars +function g () { return 43; } diff --git a/spec/fixtures/workers/shared_worker.js b/spec/fixtures/workers/shared_worker.js index ad0776fc07f80..eea323af4d353 100644 --- a/spec/fixtures/workers/shared_worker.js +++ b/spec/fixtures/workers/shared_worker.js @@ -1,7 +1,7 @@ this.onconnect = function (event) { - const port = event.ports[0] - port.start() + const port = event.ports[0]; + port.start(); port.onmessage = function (event) { - port.postMessage(event.data) - } -} + port.postMessage(event.data); + }; +}; diff --git a/spec/fixtures/workers/shared_worker_node.js b/spec/fixtures/workers/shared_worker_node.js index 03f4eced53b21..d9229f7c74911 100644 --- a/spec/fixtures/workers/shared_worker_node.js +++ b/spec/fixtures/workers/shared_worker_node.js @@ -1,5 +1,5 @@ self.onconnect = function (event) { - const port = event.ports[0] - port.start() - port.postMessage([typeof process, typeof setImmediate, typeof global, typeof Buffer].join(' ')) -} + const port = event.ports[0]; + port.start(); + port.postMessage([typeof process, typeof setImmediate, typeof global, typeof Buffer].join(' ')); +}; diff --git a/spec/fixtures/workers/worker.js b/spec/fixtures/workers/worker.js index 884c17ac86ea6..488838ceb46c3 100644 --- a/spec/fixtures/workers/worker.js +++ b/spec/fixtures/workers/worker.js @@ -1,3 +1,3 @@ this.onmessage = function (msg) { - this.postMessage(msg.data) -} + this.postMessage(msg.data); +}; diff --git a/spec/fixtures/workers/worker_node.js b/spec/fixtures/workers/worker_node.js index 5d59d2d0c3404..61f387744ec89 100644 --- a/spec/fixtures/workers/worker_node.js +++ b/spec/fixtures/workers/worker_node.js @@ -1 +1 @@ -self.postMessage([typeof process, typeof setImmediate, typeof global, typeof Buffer].join(' ')) +self.postMessage([typeof process, typeof setImmediate, typeof global, typeof Buffer].join(' ')); diff --git a/spec/node-spec.js b/spec/node-spec.js index 682e8ad87e784..7352f56e68e84 100644 --- a/spec/node-spec.js +++ b/spec/node-spec.js @@ -1,150 +1,150 @@ -const ChildProcess = require('child_process') -const chai = require('chai') -const { expect } = chai -const dirtyChai = require('dirty-chai') -const fs = require('fs') -const path = require('path') -const os = require('os') -const { ipcRenderer } = require('electron') -const features = process.electronBinding('features') +const ChildProcess = require('child_process'); +const chai = require('chai'); +const { expect } = chai; +const dirtyChai = require('dirty-chai'); +const fs = require('fs'); +const path = require('path'); +const os = require('os'); +const { ipcRenderer } = require('electron'); +const features = process.electronBinding('features'); -const { emittedOnce } = require('./events-helpers') -const { ifit } = require('./spec-helpers') +const { emittedOnce } = require('./events-helpers'); +const { ifit } = require('./spec-helpers'); -chai.use(dirtyChai) +chai.use(dirtyChai); describe('node feature', () => { - const fixtures = path.join(__dirname, 'fixtures') + const fixtures = path.join(__dirname, 'fixtures'); describe('child_process', () => { beforeEach(function () { if (!features.isRunAsNodeEnabled()) { - this.skip() + this.skip(); } - }) + }); describe('child_process.fork', () => { it('works in current process', (done) => { - const child = ChildProcess.fork(path.join(fixtures, 'module', 'ping.js')) + const child = ChildProcess.fork(path.join(fixtures, 'module', 'ping.js')); child.on('message', msg => { - expect(msg).to.equal('message') - done() - }) - child.send('message') - }) + expect(msg).to.equal('message'); + done(); + }); + child.send('message'); + }); it('preserves args', (done) => { - const args = ['--expose_gc', '-test', '1'] - const child = ChildProcess.fork(path.join(fixtures, 'module', 'process_args.js'), args) + const args = ['--expose_gc', '-test', '1']; + const child = ChildProcess.fork(path.join(fixtures, 'module', 'process_args.js'), args); child.on('message', (msg) => { - expect(args).to.deep.equal(msg.slice(2)) - done() - }) - child.send('message') - }) + expect(args).to.deep.equal(msg.slice(2)); + done(); + }); + child.send('message'); + }); it('works in forked process', (done) => { - const child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js')) + const child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js')); child.on('message', (msg) => { - expect(msg).to.equal('message') - done() - }) - child.send('message') - }) + expect(msg).to.equal('message'); + done(); + }); + child.send('message'); + }); it('works in forked process when options.env is specifed', (done) => { const child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js'), [], { path: process.env['PATH'] - }) + }); child.on('message', (msg) => { - expect(msg).to.equal('message') - done() - }) - child.send('message') - }) + expect(msg).to.equal('message'); + done(); + }); + child.send('message'); + }); it('has String::localeCompare working in script', (done) => { - const child = ChildProcess.fork(path.join(fixtures, 'module', 'locale-compare.js')) + const child = ChildProcess.fork(path.join(fixtures, 'module', 'locale-compare.js')); child.on('message', (msg) => { - expect(msg).to.deep.equal([0, -1, 1]) - done() - }) - child.send('message') - }) + expect(msg).to.deep.equal([0, -1, 1]); + done(); + }); + child.send('message'); + }); it('has setImmediate working in script', (done) => { - const child = ChildProcess.fork(path.join(fixtures, 'module', 'set-immediate.js')) + const child = ChildProcess.fork(path.join(fixtures, 'module', 'set-immediate.js')); child.on('message', (msg) => { - expect(msg).to.equal('ok') - done() - }) - child.send('message') - }) + expect(msg).to.equal('ok'); + done(); + }); + child.send('message'); + }); it('pipes stdio', (done) => { - const child = ChildProcess.fork(path.join(fixtures, 'module', 'process-stdout.js'), { silent: true }) - let data = '' + const child = ChildProcess.fork(path.join(fixtures, 'module', 'process-stdout.js'), { silent: true }); + let data = ''; child.stdout.on('data', (chunk) => { - data += String(chunk) - }) + data += String(chunk); + }); child.on('close', (code) => { - expect(code).to.equal(0) - expect(data).to.equal('pipes stdio') - done() - }) - }) + expect(code).to.equal(0); + expect(data).to.equal('pipes stdio'); + done(); + }); + }); it('works when sending a message to a process forked with the --eval argument', (done) => { - const source = "process.on('message', (message) => { process.send(message) })" - const forked = ChildProcess.fork('--eval', [source]) + const source = "process.on('message', (message) => { process.send(message) })"; + const forked = ChildProcess.fork('--eval', [source]); forked.once('message', (message) => { - expect(message).to.equal('hello') - done() - }) - forked.send('hello') - }) + expect(message).to.equal('hello'); + done(); + }); + forked.send('hello'); + }); it('has the electron version in process.versions', (done) => { - const source = 'process.send(process.versions)' - const forked = ChildProcess.fork('--eval', [source]) + const source = 'process.send(process.versions)'; + const forked = ChildProcess.fork('--eval', [source]); forked.on('message', (message) => { expect(message) .to.have.own.property('electron') .that.is.a('string') - .and.matches(/^\d+\.\d+\.\d+(\S*)?$/) - done() - }) - }) - }) + .and.matches(/^\d+\.\d+\.\d+(\S*)?$/); + done(); + }); + }); + }); describe('child_process.spawn', () => { - let child + let child; afterEach(() => { - if (child != null) child.kill() - }) + if (child != null) child.kill(); + }); it('supports spawning Electron as a node process via the ELECTRON_RUN_AS_NODE env var', (done) => { child = ChildProcess.spawn(process.execPath, [path.join(__dirname, 'fixtures', 'module', 'run-as-node.js')], { env: { ELECTRON_RUN_AS_NODE: true } - }) + }); - let output = '' + let output = ''; child.stdout.on('data', data => { - output += data - }) + output += data; + }); child.stdout.on('close', () => { expect(JSON.parse(output)).to.deep.equal({ processLog: process.platform === 'win32' ? 'function' : 'undefined', processType: 'undefined', window: 'undefined' - }) - done() - }) - }) - }) + }); + done(); + }); + }); + }); describe('child_process.exec', () => { (process.platform === 'linux' ? it : it.skip)('allows executing a setuid binary from non-sandboxed renderer', () => { @@ -154,274 +154,287 @@ describe('node feature', () => { // are running in. If this test fails with an error like 'effective uid // is not 0', then it's likely that our patch to prevent the flag from // being set has become ineffective. - const stdout = ChildProcess.execSync('sudo --help') - expect(stdout).to.not.be.empty() - }) - }) - }) + const stdout = ChildProcess.execSync('sudo --help'); + expect(stdout).to.not.be.empty(); + }); + }); + }); describe('contexts', () => { describe('setTimeout in fs callback', () => { it('does not crash', (done) => { fs.readFile(__filename, () => { - setTimeout(done, 0) - }) - }) - }) + setTimeout(done, 0); + }); + }); + }); describe('error thrown in renderer process node context', () => { it('gets emitted as a process uncaughtException event', (done) => { - const error = new Error('boo!') - const listeners = process.listeners('uncaughtException') - process.removeAllListeners('uncaughtException') + const error = new Error('boo!'); + const listeners = process.listeners('uncaughtException'); + process.removeAllListeners('uncaughtException'); process.on('uncaughtException', (thrown) => { try { - expect(thrown).to.equal(error) - done() + expect(thrown).to.equal(error); + done(); } catch (e) { - done(e) + done(e); } finally { - process.removeAllListeners('uncaughtException') - listeners.forEach((listener) => process.on('uncaughtException', listener)) + process.removeAllListeners('uncaughtException'); + listeners.forEach((listener) => process.on('uncaughtException', listener)); } - }) + }); fs.readFile(__filename, () => { - throw error - }) - }) - }) + throw error; + }); + }); + }); describe('error thrown in main process node context', () => { it('gets emitted as a process uncaughtException event', () => { - const error = ipcRenderer.sendSync('handle-uncaught-exception', 'hello') - expect(error).to.equal('hello') - }) - }) + const error = ipcRenderer.sendSync('handle-uncaught-exception', 'hello'); + expect(error).to.equal('hello'); + }); + }); describe('promise rejection in main process node context', () => { it('gets emitted as a process unhandledRejection event', () => { - const error = ipcRenderer.sendSync('handle-unhandled-rejection', 'hello') - expect(error).to.equal('hello') - }) - }) + const error = ipcRenderer.sendSync('handle-unhandled-rejection', 'hello'); + expect(error).to.equal('hello'); + }); + }); describe('setTimeout called under blink env in renderer process', () => { it('can be scheduled in time', (done) => { - setTimeout(done, 10) - }) + setTimeout(done, 10); + }); it('works from the timers module', (done) => { - require('timers').setTimeout(done, 10) - }) - }) + require('timers').setTimeout(done, 10); + }); + }); describe('setInterval called under blink env in renderer process', () => { it('can be scheduled in time', (done) => { const id = setInterval(() => { - clearInterval(id) - done() - }, 10) - }) + clearInterval(id); + done(); + }, 10); + }); it('can be scheduled in time from timers module', (done) => { - const { setInterval, clearInterval } = require('timers') + const { setInterval, clearInterval } = require('timers'); const id = setInterval(() => { - clearInterval(id) - done() - }, 10) - }) - }) - }) + clearInterval(id); + done(); + }, 10); + }); + }); + }); describe('message loop', () => { describe('process.nextTick', () => { - it('emits the callback', (done) => process.nextTick(done)) + it('emits the callback', (done) => process.nextTick(done)); it('works in nested calls', (done) => { process.nextTick(() => { - process.nextTick(() => process.nextTick(done)) - }) - }) - }) + process.nextTick(() => process.nextTick(done)); + }); + }); + }); describe('setImmediate', () => { - it('emits the callback', (done) => setImmediate(done)) + it('emits the callback', (done) => setImmediate(done)); it('works in nested calls', (done) => { setImmediate(() => { - setImmediate(() => setImmediate(done)) - }) - }) - }) - }) + setImmediate(() => setImmediate(done)); + }); + }); + }); + }); describe('net.connect', () => { before(function () { if (!features.isRunAsNodeEnabled() || process.platform !== 'darwin') { - this.skip() + this.skip(); } - }) + }); it('emit error when connect to a socket path without listeners', (done) => { - const socketPath = path.join(os.tmpdir(), 'atom-shell-test.sock') - const script = path.join(fixtures, 'module', 'create_socket.js') - const child = ChildProcess.fork(script, [socketPath]) + const socketPath = path.join(os.tmpdir(), 'atom-shell-test.sock'); + const script = path.join(fixtures, 'module', 'create_socket.js'); + const child = ChildProcess.fork(script, [socketPath]); child.on('exit', (code) => { - expect(code).to.equal(0) - const client = require('net').connect(socketPath) + expect(code).to.equal(0); + const client = require('net').connect(socketPath); client.on('error', (error) => { - expect(error.code).to.equal('ECONNREFUSED') - done() - }) - }) - }) - }) + expect(error.code).to.equal('ECONNREFUSED'); + done(); + }); + }); + }); + }); describe('Buffer', () => { it('can be created from WebKit external string', () => { - const p = document.createElement('p') - p.innerText = '闲云潭影日悠悠,物换星移几度秋' - const b = Buffer.from(p.innerText) - expect(b.toString()).to.equal('闲云潭影日悠悠,物换星移几度秋') - expect(Buffer.byteLength(p.innerText)).to.equal(45) - }) + const p = document.createElement('p'); + p.innerText = '闲云潭影日悠悠,物换星移几度秋'; + const b = Buffer.from(p.innerText); + expect(b.toString()).to.equal('闲云潭影日悠悠,物换星移几度秋'); + expect(Buffer.byteLength(p.innerText)).to.equal(45); + }); it('correctly parses external one-byte UTF8 string', () => { - const p = document.createElement('p') - p.innerText = 'Jøhänñéß' - const b = Buffer.from(p.innerText) - expect(b.toString()).to.equal('Jøhänñéß') - expect(Buffer.byteLength(p.innerText)).to.equal(13) - }) + const p = document.createElement('p'); + p.innerText = 'Jøhänñéß'; + const b = Buffer.from(p.innerText); + expect(b.toString()).to.equal('Jøhänñéß'); + expect(Buffer.byteLength(p.innerText)).to.equal(13); + }); it('does not crash when creating large Buffers', () => { - let buffer = Buffer.from(new Array(4096).join(' ')) - expect(buffer.length).to.equal(4095) - buffer = Buffer.from(new Array(4097).join(' ')) - expect(buffer.length).to.equal(4096) - }) + let buffer = Buffer.from(new Array(4096).join(' ')); + expect(buffer.length).to.equal(4095); + buffer = Buffer.from(new Array(4097).join(' ')); + expect(buffer.length).to.equal(4096); + }); it('does not crash for crypto operations', () => { - const crypto = require('crypto') - const data = 'lG9E+/g4JmRmedDAnihtBD4Dfaha/GFOjd+xUOQI05UtfVX3DjUXvrS98p7kZQwY3LNhdiFo7MY5rGft8yBuDhKuNNag9vRx/44IuClDhdQ=' - const key = 'q90K9yBqhWZnAMCMTOJfPQ==' - const cipherText = '{"error_code":114,"error_message":"Tham số không hợp lệ","data":null}' + const crypto = require('crypto'); + const data = 'lG9E+/g4JmRmedDAnihtBD4Dfaha/GFOjd+xUOQI05UtfVX3DjUXvrS98p7kZQwY3LNhdiFo7MY5rGft8yBuDhKuNNag9vRx/44IuClDhdQ='; + const key = 'q90K9yBqhWZnAMCMTOJfPQ=='; + const cipherText = '{"error_code":114,"error_message":"Tham số không hợp lệ","data":null}'; for (let i = 0; i < 10000; ++i) { - const iv = Buffer.from('0'.repeat(32), 'hex') - const input = Buffer.from(data, 'base64') - const decipher = crypto.createDecipheriv('aes-128-cbc', Buffer.from(key, 'base64'), iv) - const result = Buffer.concat([decipher.update(input), decipher.final()]).toString('utf8') - expect(cipherText).to.equal(result) + const iv = Buffer.from('0'.repeat(32), 'hex'); + const input = Buffer.from(data, 'base64'); + const decipher = crypto.createDecipheriv('aes-128-cbc', Buffer.from(key, 'base64'), iv); + const result = Buffer.concat([decipher.update(input), decipher.final()]).toString('utf8'); + expect(cipherText).to.equal(result); } - }) - }) + }); + }); describe('process.stdout', () => { it('does not throw an exception when accessed', () => { - expect(() => process.stdout).to.not.throw() - }) + expect(() => process.stdout).to.not.throw(); + }); it('does not throw an exception when calling write()', () => { expect(() => { - process.stdout.write('test') - }).to.not.throw() - }) + process.stdout.write('test'); + }).to.not.throw(); + }); // TODO: figure out why process.stdout.isTTY is true on Darwin but not Linux/Win. ifit(process.platform !== 'darwin')('isTTY should be undefined in the renderer process', function () { - expect(process.stdout.isTTY).to.be.undefined() - }) - }) + expect(process.stdout.isTTY).to.be.undefined(); + }); + }); describe('process.stdin', () => { it('does not throw an exception when accessed', () => { - expect(() => process.stdin).to.not.throw() - }) + expect(() => process.stdin).to.not.throw(); + }); it('returns null when read from', () => { - expect(process.stdin.read()).to.be.null() - }) - }) + expect(process.stdin.read()).to.be.null(); + }); + }); describe('process.version', () => { it('should not have -pre', () => { - expect(process.version.endsWith('-pre')).to.be.false() - }) - }) + expect(process.version.endsWith('-pre')).to.be.false(); + }); + }); describe('vm.runInNewContext', () => { it('should not crash', () => { - require('vm').runInNewContext('') - }) - }) + require('vm').runInNewContext(''); + }); + }); describe('crypto', () => { it('should list the ripemd160 hash in getHashes', () => { - expect(require('crypto').getHashes()).to.include('ripemd160') - }) + expect(require('crypto').getHashes()).to.include('ripemd160'); + }); it('should be able to create a ripemd160 hash and use it', () => { - const hash = require('crypto').createHash('ripemd160') - hash.update('electron-ripemd160') - expect(hash.digest('hex')).to.equal('fa7fec13c624009ab126ebb99eda6525583395fe') - }) + const hash = require('crypto').createHash('ripemd160'); + hash.update('electron-ripemd160'); + expect(hash.digest('hex')).to.equal('fa7fec13c624009ab126ebb99eda6525583395fe'); + }); it('should list aes-{128,256}-cfb in getCiphers', () => { - expect(require('crypto').getCiphers()).to.include.members(['aes-128-cfb', 'aes-256-cfb']) - }) + expect(require('crypto').getCiphers()).to.include.members(['aes-128-cfb', 'aes-256-cfb']); + }); it('should be able to create an aes-128-cfb cipher', () => { - require('crypto').createCipheriv('aes-128-cfb', '0123456789abcdef', '0123456789abcdef') - }) + require('crypto').createCipheriv('aes-128-cfb', '0123456789abcdef', '0123456789abcdef'); + }); it('should be able to create an aes-256-cfb cipher', () => { - require('crypto').createCipheriv('aes-256-cfb', '0123456789abcdef0123456789abcdef', '0123456789abcdef') - }) + require('crypto').createCipheriv('aes-256-cfb', '0123456789abcdef0123456789abcdef', '0123456789abcdef'); + }); it('should list des-ede-cbc in getCiphers', () => { - expect(require('crypto').getCiphers()).to.include('des-ede-cbc') - }) + expect(require('crypto').getCiphers()).to.include('des-ede-cbc'); + }); it('should be able to create an des-ede-cbc cipher', () => { - const key = Buffer.from('0123456789abcdeff1e0d3c2b5a49786', 'hex') - const iv = Buffer.from('fedcba9876543210', 'hex') - require('crypto').createCipheriv('des-ede-cbc', key, iv) - }) + const key = Buffer.from('0123456789abcdeff1e0d3c2b5a49786', 'hex'); + const iv = Buffer.from('fedcba9876543210', 'hex'); + require('crypto').createCipheriv('des-ede-cbc', key, iv); + }); it('should not crash when getting an ECDH key', () => { - const ecdh = require('crypto').createECDH('prime256v1') - expect(ecdh.generateKeys()).to.be.an.instanceof(Buffer) - expect(ecdh.getPrivateKey()).to.be.an.instanceof(Buffer) - }) + const ecdh = require('crypto').createECDH('prime256v1'); + expect(ecdh.generateKeys()).to.be.an.instanceof(Buffer); + expect(ecdh.getPrivateKey()).to.be.an.instanceof(Buffer); + }); it('should not crash when generating DH keys or fetching DH fields', () => { - const dh = require('crypto').createDiffieHellman('modp15') - expect(dh.generateKeys()).to.be.an.instanceof(Buffer) - expect(dh.getPublicKey()).to.be.an.instanceof(Buffer) - expect(dh.getPrivateKey()).to.be.an.instanceof(Buffer) - expect(dh.getPrime()).to.be.an.instanceof(Buffer) - expect(dh.getGenerator()).to.be.an.instanceof(Buffer) - }) + const dh = require('crypto').createDiffieHellman('modp15'); + expect(dh.generateKeys()).to.be.an.instanceof(Buffer); + expect(dh.getPublicKey()).to.be.an.instanceof(Buffer); + expect(dh.getPrivateKey()).to.be.an.instanceof(Buffer); + expect(dh.getPrime()).to.be.an.instanceof(Buffer); + expect(dh.getGenerator()).to.be.an.instanceof(Buffer); + }); it('should not crash when creating an ECDH cipher', () => { - const crypto = require('crypto') - const dh = crypto.createECDH('prime256v1') - dh.generateKeys() - dh.setPrivateKey(dh.getPrivateKey()) - }) - }) + const crypto = require('crypto'); + const dh = crypto.createECDH('prime256v1'); + dh.generateKeys(); + dh.setPrivateKey(dh.getPrivateKey()); + }); + }); it('includes the electron version in process.versions', () => { expect(process.versions) .to.have.own.property('electron') .that.is.a('string') - .and.matches(/^\d+\.\d+\.\d+(\S*)?$/) - }) + .and.matches(/^\d+\.\d+\.\d+(\S*)?$/); + }); it('includes the chrome version in process.versions', () => { expect(process.versions) .to.have.own.property('chrome') .that.is.a('string') - .and.matches(/^\d+\.\d+\.\d+\.\d+$/) - }) -}) + .and.matches(/^\d+\.\d+\.\d+\.\d+$/); + }); + + it('handles Promise timeouts correctly', (done) => { + const scriptPath = path.join(fixtures, 'module', 'node-promise-timer.js'); + const child = ChildProcess.spawn(process.execPath, [scriptPath], { + env: { ELECTRON_RUN_AS_NODE: 'true' } + }); + emittedOnce(child, 'exit').then(([code, signal]) => { + expect(code).to.equal(0); + expect(signal).to.equal(null); + child.kill(); + done(); + }); + }); +}); diff --git a/spec/spec-helpers.js b/spec/spec-helpers.js index f54f043dccf69..ae7d955e28fdc 100644 --- a/spec/spec-helpers.js +++ b/spec/spec-helpers.js @@ -1,2 +1,2 @@ -exports.ifit = (condition) => (condition ? it : it.skip) -exports.ifdescribe = (condition) => (condition ? describe : describe.skip) +exports.ifit = (condition) => (condition ? it : it.skip); +exports.ifdescribe = (condition) => (condition ? describe : describe.skip); diff --git a/spec/static/main.js b/spec/static/main.js index d4458e78f3274..9d53aeb0c1aa6 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -1,32 +1,32 @@ // Deprecated APIs are still supported and should be tested. -process.throwDeprecation = false +process.throwDeprecation = false; -const electron = require('electron') -const { app, BrowserWindow, crashReporter, dialog, ipcMain, protocol, webContents } = electron +const electron = require('electron'); +const { app, BrowserWindow, crashReporter, dialog, ipcMain, protocol, webContents } = electron; -const fs = require('fs') -const path = require('path') -const util = require('util') -const v8 = require('v8') +const fs = require('fs'); +const path = require('path'); +const util = require('util'); +const v8 = require('v8'); const argv = require('yargs') .boolean('ci') .string('g').alias('g', 'grep') .boolean('i').alias('i', 'invert') - .argv + .argv; -let window = null +let window = null; // will be used by crash-reporter spec. -process.port = 0 +process.port = 0; -v8.setFlagsFromString('--expose_gc') -app.commandLine.appendSwitch('js-flags', '--expose_gc') -app.commandLine.appendSwitch('ignore-certificate-errors') -app.commandLine.appendSwitch('disable-renderer-backgrounding') +v8.setFlagsFromString('--expose_gc'); +app.commandLine.appendSwitch('js-flags', '--expose_gc'); +app.commandLine.appendSwitch('ignore-certificate-errors'); +app.commandLine.appendSwitch('disable-renderer-backgrounding'); // Disable security warnings (the security warnings test will enable them) -process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true +process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true; // Accessing stdout in the main process will result in the process.stdout // throwing UnknownSystemError in renderer process sometimes. This line makes @@ -39,57 +39,59 @@ process.stdout console ipcMain.on('message', function (event, ...args) { - event.sender.send('message', ...args) -}) + event.sender.send('message', ...args); +}); -ipcMain.handle('get-temp-dir', () => app.getPath('temp')) -ipcMain.handle('ping', () => null) +ipcMain.handle('get-modules', () => Object.keys(electron)); +ipcMain.handle('get-temp-dir', () => app.getPath('temp')); +ipcMain.handle('ping', () => null); // Set productName so getUploadedReports() uses the right directory in specs if (process.platform !== 'darwin') { - crashReporter.productName = 'Zombies' + crashReporter.productName = 'Zombies'; } // Write output to file if OUTPUT_TO_FILE is defined. -const outputToFile = process.env.OUTPUT_TO_FILE +const outputToFile = process.env.OUTPUT_TO_FILE; const print = function (_, method, args) { - const output = util.format.apply(null, args) + const output = util.format.apply(null, args); if (outputToFile) { - fs.appendFileSync(outputToFile, output + '\n') + fs.appendFileSync(outputToFile, output + '\n'); } else { - console[method](output) + console[method](output); } -} -ipcMain.on('console-call', print) +}; +ipcMain.on('console-call', print); ipcMain.on('process.exit', function (event, code) { - process.exit(code) -}) + process.exit(code); +}); ipcMain.on('eval', function (event, script) { event.returnValue = eval(script) // eslint-disable-line -}) +}); ipcMain.on('echo', function (event, msg) { - event.returnValue = msg -}) + event.returnValue = msg; +}); -global.setTimeoutPromisified = util.promisify(setTimeout) +global.setTimeoutPromisified = util.promisify(setTimeout); +global.returnAPromise = (value) => new Promise((resolve) => setTimeout(() => resolve(value), 100)); -global.isCi = !!argv.ci +global.isCi = !!argv.ci; if (global.isCi) { - process.removeAllListeners('uncaughtException') + process.removeAllListeners('uncaughtException'); process.on('uncaughtException', function (error) { - console.error(error, error.stack) - process.exit(1) - }) + console.error(error, error.stack); + process.exit(1); + }); } -global.nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS +global.nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS; // Register app as standard scheme. -global.standardScheme = 'app' -global.zoomScheme = 'zoom' +global.standardScheme = 'app'; +global.zoomScheme = 'zoom'; protocol.registerSchemesAsPrivileged([ { scheme: global.standardScheme, privileges: { standard: true, secure: true } }, { scheme: global.zoomScheme, privileges: { standard: true, secure: true } }, @@ -97,28 +99,28 @@ protocol.registerSchemesAsPrivileged([ { scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } }, { scheme: 'no-cors', privileges: { supportFetchAPI: true } }, { scheme: 'no-fetch', privileges: { corsEnabled: true } } -]) +]); app.on('window-all-closed', function () { - app.quit() -}) + app.quit(); +}); app.on('gpu-process-crashed', (event, killed) => { - console.log(`GPU process crashed (killed=${killed})`) -}) + console.log(`GPU process crashed (killed=${killed})`); +}); app.on('renderer-process-crashed', (event, contents, killed) => { - console.log(`webContents ${contents.id} crashed: ${contents.getURL()} (killed=${killed})`) -}) + console.log(`webContents ${contents.id} crashed: ${contents.getURL()} (killed=${killed})`); +}); app.on('ready', function () { // Test if using protocol module would crash. - electron.protocol.registerStringProtocol('test-if-crashes', function () {}) + electron.protocol.registerStringProtocol('test-if-crashes', function () {}); // Send auto updater errors to window to be verified in specs electron.autoUpdater.on('error', function (error) { - window.send('auto-updater-error', error.message) - }) + window.send('auto-updater-error', error.message); + }); window = new BrowserWindow({ title: 'Electron Tests', @@ -130,27 +132,27 @@ app.on('ready', function () { nodeIntegration: true, webviewTag: true } - }) + }); window.loadFile('static/index.html', { query: { grep: argv.grep, invert: argv.invert ? 'true' : '' } - }) + }); window.on('unresponsive', function () { const chosen = dialog.showMessageBox(window, { type: 'warning', buttons: ['Close', 'Keep Waiting'], message: 'Window is not responsing', detail: 'The window is not responding. Would you like to force close it or just keep waiting?' - }) - if (chosen === 0) window.destroy() - }) + }); + if (chosen === 0) window.destroy(); + }); window.webContents.on('crashed', function () { - console.error('Renderer process crashed') - process.exit(1) - }) -}) + console.error('Renderer process crashed'); + process.exit(1); + }); +}); for (const eventName of [ 'remote-get-guest-web-contents' @@ -158,96 +160,96 @@ for (const eventName of [ ipcMain.on(`handle-next-${eventName}`, function (event, returnValue) { event.sender.once(eventName, (event) => { if (returnValue) { - event.returnValue = returnValue + event.returnValue = returnValue; } else { - event.preventDefault() + event.preventDefault(); } - }) - }) + }); + }); } ipcMain.on('set-client-certificate-option', function (event, skip) { app.once('select-client-certificate', function (event, webContents, url, list, callback) { - event.preventDefault() + event.preventDefault(); if (skip) { - callback() + callback(); } else { ipcMain.on('client-certificate-response', function (event, certificate) { - callback(certificate) - }) - window.webContents.send('select-client-certificate', webContents.id, list) + callback(certificate); + }); + window.webContents.send('select-client-certificate', webContents.id, list); } - }) - event.returnValue = 'done' -}) + }); + event.returnValue = 'done'; +}); ipcMain.on('prevent-next-will-attach-webview', (event) => { - event.sender.once('will-attach-webview', event => event.preventDefault()) -}) + event.sender.once('will-attach-webview', event => event.preventDefault()); +}); ipcMain.on('disable-node-on-next-will-attach-webview', (event, id) => { event.sender.once('will-attach-webview', (event, webPreferences, params) => { - params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'c.html')}` - webPreferences.nodeIntegration = false - }) -}) + params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'c.html')}`; + webPreferences.nodeIntegration = false; + }); +}); ipcMain.on('disable-preload-on-next-will-attach-webview', (event, id) => { event.sender.once('will-attach-webview', (event, webPreferences, params) => { - params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'webview-stripped-preload.html')}` - delete webPreferences.preload - delete webPreferences.preloadURL - }) -}) + params.src = `file://${path.join(__dirname, '..', 'fixtures', 'pages', 'webview-stripped-preload.html')}`; + delete webPreferences.preload; + delete webPreferences.preloadURL; + }); +}); ipcMain.on('try-emit-web-contents-event', (event, id, eventName) => { - const consoleWarn = console.warn - const contents = webContents.fromId(id) - const listenerCountBefore = contents.listenerCount(eventName) + const consoleWarn = console.warn; + const contents = webContents.fromId(id); + const listenerCountBefore = contents.listenerCount(eventName); console.warn = (warningMessage) => { - console.warn = consoleWarn + console.warn = consoleWarn; - const listenerCountAfter = contents.listenerCount(eventName) + const listenerCountAfter = contents.listenerCount(eventName); event.returnValue = { warningMessage, listenerCountBefore, listenerCountAfter - } - } + }; + }; - contents.emit(eventName, { sender: contents }) -}) + contents.emit(eventName, { sender: contents }); +}); ipcMain.on('handle-uncaught-exception', (event, message) => { suspendListeners(process, 'uncaughtException', (error) => { - event.returnValue = error.message - }) + event.returnValue = error.message; + }); fs.readFile(__filename, () => { - throw new Error(message) - }) -}) + throw new Error(message); + }); +}); ipcMain.on('handle-unhandled-rejection', (event, message) => { suspendListeners(process, 'unhandledRejection', (error) => { - event.returnValue = error.message - }) + event.returnValue = error.message; + }); fs.readFile(__filename, () => { - Promise.reject(new Error(message)) - }) -}) + Promise.reject(new Error(message)); + }); +}); // Suspend listeners until the next event and then restore them const suspendListeners = (emitter, eventName, callback) => { - const listeners = emitter.listeners(eventName) - emitter.removeAllListeners(eventName) + const listeners = emitter.listeners(eventName); + emitter.removeAllListeners(eventName); emitter.once(eventName, (...args) => { - emitter.removeAllListeners(eventName) + emitter.removeAllListeners(eventName); listeners.forEach((listener) => { - emitter.on(eventName, listener) - }) + emitter.on(eventName, listener); + }); // eslint-disable-next-line standard/no-callback-literal - callback(...args) - }) -} + callback(...args); + }); +}; diff --git a/spec/ts-smoke/electron/main.ts b/spec/ts-smoke/electron/main.ts index d9f256d2adfa8..177fdba20b205 100644 --- a/spec/ts-smoke/electron/main.ts +++ b/spec/ts-smoke/electron/main.ts @@ -81,7 +81,6 @@ app.on('ready', () => { }) mainWindow.webContents.setVisualZoomLevelLimits(50, 200) - mainWindow.webContents.setLayoutZoomLevelLimits(50, 200) mainWindow.webContents.print({ silent: true, printBackground: false }) mainWindow.webContents.print() @@ -318,17 +317,6 @@ app.setAboutPanelOptions({ version: '1.2.3' }) -let window = new BrowserWindow() -window.setProgressBar(0.5) -window.setRepresentedFilename('/etc/passwd') -window.setDocumentEdited(true) -window.previewFile('/path/to/file') -window.previewFile('/path/to/file', 'Displayed Name') -window.setVibrancy('menu') -window.setVibrancy('titlebar') -window.setVibrancy('selection') -window.setVibrancy('popover') - // Online/Offline Event Detection // https://github.com/atom/electron/blob/master/docs/tutorial/online-offline-events.md @@ -448,7 +436,7 @@ autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName, releaseDa console.log('update-downloaded', releaseNotes, releaseName, releaseDate, updateURL) }) -// browser-window +// BrowserWindow // https://github.com/atom/electron/blob/master/docs/api/browser-window.md let win3 = new BrowserWindow({ width: 800, height: 600, show: false }) @@ -462,6 +450,18 @@ win3.show() const toolbarRect = document.getElementById('toolbar').getBoundingClientRect() win3.setSheetOffset(toolbarRect.height) +let window = new BrowserWindow() +window.setProgressBar(0.5) +window.setRepresentedFilename('/etc/passwd') +window.setDocumentEdited(true) +window.previewFile('/path/to/file') +window.previewFile('/path/to/file', 'Displayed Name') +window.setVibrancy('menu') +window.setVibrancy('titlebar') +window.setVibrancy('selection') +window.setVibrancy('popover') +window.setIcon('/path/to/icon') + const installed = BrowserWindow.getDevToolsExtensions().hasOwnProperty('devtron') // content-tracing diff --git a/spec/ts-smoke/electron/renderer.ts b/spec/ts-smoke/electron/renderer.ts index b8d68d2d5109d..51cb8328874c0 100644 --- a/spec/ts-smoke/electron/renderer.ts +++ b/spec/ts-smoke/electron/renderer.ts @@ -57,7 +57,6 @@ webFrame.setZoomLevel(200) console.log(webFrame.getZoomLevel()) webFrame.setVisualZoomLevelLimits(50, 200) -webFrame.setLayoutZoomLevelLimits(50, 200) webFrame.setSpellCheckProvider('en-US', { spellCheck (words, callback) { diff --git a/spec/webview-spec.js b/spec/webview-spec.js index 57969fd821368..dff5b6607029d 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -1,150 +1,151 @@ -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const path = require('path') -const http = require('http') -const url = require('url') -const { ipcRenderer } = require('electron') -const { emittedOnce, waitForEvent } = require('./events-helpers') +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const path = require('path'); +const http = require('http'); +const url = require('url'); +const { ipcRenderer } = require('electron'); +const { emittedOnce, waitForEvent } = require('./events-helpers'); +const { ifdescribe } = require('./spec-helpers'); -const { expect } = chai -chai.use(dirtyChai) +const { expect } = chai; +chai.use(dirtyChai); -const features = process.electronBinding('features') -const nativeModulesEnabled = process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS +const features = process.electronBinding('features'); +const nativeModulesEnabled = process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS; /* Most of the APIs here don't use standard callbacks */ /* eslint-disable standard/no-callback-literal */ describe(' tag', function () { - this.timeout(3 * 60 * 1000) + this.timeout(3 * 60 * 1000); - const fixtures = path.join(__dirname, 'fixtures') - let webview = null + const fixtures = path.join(__dirname, 'fixtures'); + let webview = null; const loadWebView = async (webview, attributes = {}) => { for (const [name, value] of Object.entries(attributes)) { - webview.setAttribute(name, value) + webview.setAttribute(name, value); } - document.body.appendChild(webview) - await waitForEvent(webview, 'did-finish-load') - return webview - } + document.body.appendChild(webview); + await waitForEvent(webview, 'did-finish-load'); + return webview; + }; const startLoadingWebViewAndWaitForMessage = async (webview, attributes = {}) => { - loadWebView(webview, attributes) // Don't wait for load to be finished. - const event = await waitForEvent(webview, 'console-message') - return event.message - } + loadWebView(webview, attributes); // Don't wait for load to be finished. + const event = await waitForEvent(webview, 'console-message'); + return event.message; + }; beforeEach(() => { - webview = new WebView() - }) + webview = new WebView(); + }); afterEach(() => { if (!document.body.contains(webview)) { - document.body.appendChild(webview) + document.body.appendChild(webview); } - webview.remove() - }) + webview.remove(); + }); describe('src attribute', () => { it('specifies the page to load', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `file://${fixtures}/pages/a.html` - }) - expect(message).to.equal('a') - }) + }); + expect(message).to.equal('a'); + }); it('navigates to new page when changed', async () => { await loadWebView(webview, { src: `file://${fixtures}/pages/a.html` - }) + }); - webview.src = `file://${fixtures}/pages/b.html` + webview.src = `file://${fixtures}/pages/b.html`; - const { message } = await waitForEvent(webview, 'console-message') - expect(message).to.equal('b') - }) + const { message } = await waitForEvent(webview, 'console-message'); + expect(message).to.equal('b'); + }); it('resolves relative URLs', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { src: '../fixtures/pages/e.html' - }) - expect(message).to.equal('Window script is loaded before preload script') - }) + }); + expect(message).to.equal('Window script is loaded before preload script'); + }); it('ignores empty values', () => { - expect(webview.src).to.equal('') + expect(webview.src).to.equal(''); for (const emptyValue of ['', null, undefined]) { - webview.src = emptyValue - expect(webview.src).to.equal('') + webview.src = emptyValue; + expect(webview.src).to.equal(''); } - }) + }); it('does not wait until loadURL is resolved', async () => { - await loadWebView(webview, { src: 'about:blank' }) + await loadWebView(webview, { src: 'about:blank' }); - const before = Date.now() - webview.src = 'https://github.com' - const now = Date.now() + const before = Date.now(); + webview.src = 'https://github.com'; + const now = Date.now(); // Setting src is essentially sending a sync IPC message, which should // not exceed more than a few ms. // // This is for testing #18638. - expect(now - before).to.be.below(100) - }) - }) + expect(now - before).to.be.below(100); + }); + }); describe('nodeintegration attribute', () => { it('inserts no node symbols when not set', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `file://${fixtures}/pages/c.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'undefined', module: 'undefined', process: 'undefined', global: 'undefined' - }) - }) + }); + }); it('inserts node symbols when set', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/d.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object' - }) - }) + }); + }); it('loads node symbols after POST navigation when set', async function () { // FIXME Figure out why this is timing out on AppVeyor if (process.env.APPVEYOR === 'True') { - this.skip() - return + this.skip(); + return; } const message = await startLoadingWebViewAndWaitForMessage(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/post.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object' - }) - }) + }); + }); it('disables node integration on child windows when it is disabled on the webview', async () => { const src = url.format({ @@ -154,44 +155,44 @@ describe(' tag', function () { p: `${fixtures}/pages/window-opener-node.html` }, slashes: true - }) + }); loadWebView(webview, { allowpopups: 'on', src - }) - const { message } = await waitForEvent(webview, 'console-message') - expect(JSON.parse(message).isProcessGlobalUndefined).to.be.true() + }); + const { message } = await waitForEvent(webview, 'console-message'); + expect(JSON.parse(message).isProcessGlobalUndefined).to.be.true(); }); (nativeModulesEnabled ? it : it.skip)('loads native modules when navigation happens', async function () { await loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/native-module.html` - }) + }); - webview.reload() + webview.reload(); - const { message } = await waitForEvent(webview, 'console-message') - expect(message).to.equal('function') - }) - }) + const { message } = await waitForEvent(webview, 'console-message'); + expect(message).to.equal('function'); + }); + }); describe('enableremotemodule attribute', () => { const generateSpecs = (description, sandbox) => { describe(description, () => { - const preload = `${fixtures}/module/preload-disable-remote.js` - const src = `file://${fixtures}/api/blank.html` + const preload = `${fixtures}/module/preload-disable-remote.js`; + const src = `file://${fixtures}/api/blank.html`; it('enables the remote module by default', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload, src, sandbox - }) + }); - const typeOfRemote = JSON.parse(message) - expect(typeOfRemote).to.equal('object') - }) + const typeOfRemote = JSON.parse(message); + expect(typeOfRemote).to.equal('object'); + }); it('disables the remote module when false', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { @@ -199,272 +200,272 @@ describe(' tag', function () { src, sandbox, enableremotemodule: false - }) + }); - const typeOfRemote = JSON.parse(message) - expect(typeOfRemote).to.equal('undefined') - }) - }) - } + const typeOfRemote = JSON.parse(message); + expect(typeOfRemote).to.equal('undefined'); + }); + }); + }; - generateSpecs('without sandbox', false) - generateSpecs('with sandbox', true) - }) + generateSpecs('without sandbox', false); + generateSpecs('with sandbox', true); + }); describe('preload attribute', () => { it('loads the script before other scripts in window', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload.js`, src: `file://${fixtures}/pages/e.html` - }) + }); - expect(message).to.be.a('string') - expect(message).to.be.not.equal('Window script is loaded before preload script') - }) + expect(message).to.be.a('string'); + expect(message).to.be.not.equal('Window script is loaded before preload script'); + }); it('preload script can still use "process" and "Buffer" when nodeintegration is off', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload-node-off.js`, src: `file://${fixtures}/api/blank.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ process: 'object', Buffer: 'function' - }) - }) + }); + }); it('runs in the correct scope when sandboxed', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload-context.js`, src: `file://${fixtures}/api/blank.html`, webpreferences: 'sandbox=yes' - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', // arguments passed to it should be availale electron: 'undefined', // objects from the scope it is called from should not be available window: 'object', // the window object should be available localVar: 'undefined' // but local variables should not be exposed to the window - }) - }) + }); + }); it('preload script can require modules that still use "process" and "Buffer" when nodeintegration is off', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload-node-off-wrapper.js`, src: `file://${fixtures}/api/blank.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ process: 'object', Buffer: 'function' - }) - }) + }); + }); it('receives ipc message in preload script', async () => { await loadWebView(webview, { preload: `${fixtures}/module/preload-ipc.js`, src: `file://${fixtures}/pages/e.html` - }) + }); - const message = 'boom!' - webview.send('ping', message) + const message = 'boom!'; + webview.send('ping', message); - const { channel, args } = await waitForEvent(webview, 'ipc-message') - expect(channel).to.equal('pong') - expect(args).to.deep.equal([message]) - }) + const { channel, args } = await waitForEvent(webview, 'ipc-message'); + expect(channel).to.equal('pong'); + expect(args).to.deep.equal([message]); + }); it('works without script tag in page', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload.js`, src: `file://${fixtures}pages/base-page.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object', Buffer: 'function' - }) - }) + }); + }); it('resolves relative URLs', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: '../fixtures/module/preload.js', src: `file://${fixtures}/pages/e.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object', Buffer: 'function' - }) - }) + }); + }); it('ignores empty values', () => { - expect(webview.preload).to.equal('') + expect(webview.preload).to.equal(''); for (const emptyValue of ['', null, undefined]) { - webview.preload = emptyValue - expect(webview.preload).to.equal('') + webview.preload = emptyValue; + expect(webview.preload).to.equal(''); } - }) - }) + }); + }); describe('httpreferrer attribute', () => { it('sets the referrer url', (done) => { - const referrer = 'http://github.com/' + const referrer = 'http://github.com/'; const server = http.createServer((req, res) => { - res.end() - server.close() - expect(req.headers.referer).to.equal(referrer) - done() + res.end(); + server.close(); + expect(req.headers.referer).to.equal(referrer); + done(); }).listen(0, '127.0.0.1', () => { - const port = server.address().port + const port = server.address().port; loadWebView(webview, { httpreferrer: referrer, src: `http://127.0.0.1:${port}` - }) - }) - }) - }) + }); + }); + }); + }); describe('useragent attribute', () => { it('sets the user agent', async () => { - const referrer = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko' + const referrer = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'; const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `file://${fixtures}/pages/useragent.html`, useragent: referrer - }) - expect(message).to.equal(referrer) - }) - }) + }); + expect(message).to.equal(referrer); + }); + }); 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 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') - }) + }); + expect(message).to.be.a('string'); + expect(message).to.contain('Not allowed to load local resource'); + }); 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 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') - }) + }); + expect(message).to.equal('ok'); + }); it('does not break node integration', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { disablewebsecurity: '', nodeintegration: 'on', src: `file://${fixtures}/pages/d.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object' - }) - }) + }); + }); it('does not break preload script', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { disablewebsecurity: '', preload: `${fixtures}/module/preload.js`, src: `file://${fixtures}/pages/e.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object', Buffer: 'function' - }) - }) - }) + }); + }); + }); describe('partition attribute', () => { it('inserts no node symbols when not set', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { partition: 'test1', src: `file://${fixtures}/pages/c.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'undefined', module: 'undefined', process: 'undefined', global: 'undefined' - }) - }) + }); + }); it('inserts node symbols when set', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { nodeintegration: 'on', partition: 'test2', src: `file://${fixtures}/pages/d.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object' - }) - }) + }); + }); it('isolates storage for different id', async () => { - window.localStorage.setItem('test', 'one') + window.localStorage.setItem('test', 'one'); const message = await startLoadingWebViewAndWaitForMessage(webview, { partition: 'test3', src: `file://${fixtures}/pages/partition/one.html` - }) + }); - const parsedMessage = JSON.parse(message) + const parsedMessage = JSON.parse(message); expect(parsedMessage).to.include({ numberOfEntries: 0, testValue: null - }) - }) + }); + }); it('uses current session storage when no id is provided', async () => { - const testValue = 'one' - window.localStorage.setItem('test', testValue) + const testValue = 'one'; + window.localStorage.setItem('test', testValue); const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `file://${fixtures}/pages/partition/one.html` - }) + }); - const parsedMessage = JSON.parse(message) + const parsedMessage = JSON.parse(message); expect(parsedMessage).to.include({ numberOfEntries: 1, testValue - }) - }) - }) + }); + }); + }); describe('allowpopups attribute', () => { const generateSpecs = (description, webpreferences = '') => { @@ -473,494 +474,494 @@ describe(' tag', function () { const message = await startLoadingWebViewAndWaitForMessage(webview, { webpreferences, src: `file://${fixtures}/pages/window-open-hide.html` - }) - expect(message).to.equal('null') - }) + }); + expect(message).to.equal('null'); + }); it('can open new window when set', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { webpreferences, allowpopups: 'on', src: `file://${fixtures}/pages/window-open-hide.html` - }) - expect(message).to.equal('window') - }) - }) - } + }); + expect(message).to.equal('window'); + }); + }); + }; - generateSpecs('without sandbox') - generateSpecs('with sandbox', 'sandbox=yes') - generateSpecs('with nativeWindowOpen', 'nativeWindowOpen=yes') - }) + generateSpecs('without sandbox'); + generateSpecs('with sandbox', 'sandbox=yes'); + generateSpecs('with nativeWindowOpen', 'nativeWindowOpen=yes'); + }); describe('webpreferences attribute', () => { it('can enable nodeintegration', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { src: `file://${fixtures}/pages/d.html`, webpreferences: 'nodeIntegration' - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'function', module: 'object', process: 'object' - }) - }) + }); + }); it('can disable the remote module', async () => { const message = await startLoadingWebViewAndWaitForMessage(webview, { preload: `${fixtures}/module/preload-disable-remote.js`, src: `file://${fixtures}/api/blank.html`, webpreferences: 'enableRemoteModule=no' - }) + }); - const typeOfRemote = JSON.parse(message) - expect(typeOfRemote).to.equal('undefined') - }) + const typeOfRemote = JSON.parse(message); + expect(typeOfRemote).to.equal('undefined'); + }); 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 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') - }) - }) + expect(message).to.equal('function'); + }); + }); describe('new-window event', () => { it('emits when window.open is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/window-open.html` - }) - const { url, frameName } = await waitForEvent(webview, 'new-window') + }); + const { url, frameName } = await waitForEvent(webview, 'new-window'); - expect(url).to.equal('http://host/') - expect(frameName).to.equal('host') - }) + expect(url).to.equal('http://host/'); + expect(frameName).to.equal('host'); + }); it('emits when link with target is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/target-name.html` - }) - const { url, frameName } = await waitForEvent(webview, 'new-window') + }); + const { url, frameName } = await waitForEvent(webview, 'new-window'); - expect(url).to.equal('http://host/') - expect(frameName).to.equal('target') - }) - }) + expect(url).to.equal('http://host/'); + expect(frameName).to.equal('target'); + }); + }); describe('ipc-message event', () => { it('emits when guest sends an ipc message to browser', async () => { loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/ipc-message.html` - }) - const { channel, args } = await waitForEvent(webview, 'ipc-message') + }); + const { channel, args } = await waitForEvent(webview, 'ipc-message'); - expect(channel).to.equal('channel') - expect(args).to.deep.equal(['arg1', 'arg2']) - }) - }) + expect(channel).to.equal('channel'); + expect(args).to.deep.equal(['arg1', 'arg2']); + }); + }); describe('page-title-set event', () => { it('emits when title is set', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/a.html` - }) - const { title, explicitSet } = await waitForEvent(webview, 'page-title-set') + }); + const { title, explicitSet } = await waitForEvent(webview, 'page-title-set'); - expect(title).to.equal('test') - expect(explicitSet).to.be.true() - }) - }) + expect(title).to.equal('test'); + expect(explicitSet).to.be.true(); + }); + }); describe('page-favicon-updated event', () => { it('emits when favicon urls are received', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/a.html` - }) - const { favicons } = await waitForEvent(webview, 'page-favicon-updated') + }); + const { favicons } = await waitForEvent(webview, 'page-favicon-updated'); - expect(favicons).to.be.an('array').of.length(2) + expect(favicons).to.be.an('array').of.length(2); if (process.platform === 'win32') { - expect(favicons[0]).to.match(/^file:\/\/\/[A-Z]:\/favicon.png$/i) + expect(favicons[0]).to.match(/^file:\/\/\/[A-Z]:\/favicon.png$/i); } else { - expect(favicons[0]).to.equal('file:///favicon.png') + expect(favicons[0]).to.equal('file:///favicon.png'); } - }) - }) + }); + }); describe('will-navigate event', () => { it('emits when a url that leads to oustide of the page is clicked', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/webview-will-navigate.html` - }) - const { url } = await waitForEvent(webview, 'will-navigate') + }); + const { url } = await waitForEvent(webview, 'will-navigate'); - expect(url).to.equal('http://host/') - }) - }) + expect(url).to.equal('http://host/'); + }); + }); describe('did-navigate event', () => { - let p = path.join(fixtures, 'pages', 'webview-will-navigate.html') - p = p.replace(/\\/g, '/') + let p = path.join(fixtures, 'pages', 'webview-will-navigate.html'); + p = p.replace(/\\/g, '/'); const pageUrl = url.format({ protocol: 'file', slashes: true, pathname: p - }) + }); it('emits when a url that leads to outside of the page is clicked', async () => { - loadWebView(webview, { src: pageUrl }) - const { url } = await waitForEvent(webview, 'did-navigate') + loadWebView(webview, { src: pageUrl }); + const { url } = await waitForEvent(webview, 'did-navigate'); - expect(url).to.equal(pageUrl) - }) - }) + expect(url).to.equal(pageUrl); + }); + }); describe('did-navigate-in-page event', () => { it('emits when an anchor link is clicked', async () => { - let p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page.html') - p = p.replace(/\\/g, '/') + let p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page.html'); + p = p.replace(/\\/g, '/'); const pageUrl = url.format({ protocol: 'file', slashes: true, pathname: p - }) - loadWebView(webview, { src: pageUrl }) - const event = await waitForEvent(webview, 'did-navigate-in-page') - expect(event.url).to.equal(`${pageUrl}#test_content`) - }) + }); + loadWebView(webview, { src: pageUrl }); + const event = await waitForEvent(webview, 'did-navigate-in-page'); + expect(event.url).to.equal(`${pageUrl}#test_content`); + }); it('emits when window.history.replaceState is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/webview-did-navigate-in-page-with-history.html` - }) - const { url } = await waitForEvent(webview, 'did-navigate-in-page') - expect(url).to.equal('http://host/') - }) + }); + const { url } = await waitForEvent(webview, 'did-navigate-in-page'); + expect(url).to.equal('http://host/'); + }); it('emits when window.location.hash is changed', async () => { - let p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page-with-hash.html') - p = p.replace(/\\/g, '/') + let p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page-with-hash.html'); + p = p.replace(/\\/g, '/'); const pageUrl = url.format({ protocol: 'file', slashes: true, pathname: p - }) - loadWebView(webview, { src: pageUrl }) - const event = await waitForEvent(webview, 'did-navigate-in-page') - expect(event.url).to.equal(`${pageUrl}#test`) - }) - }) + }); + loadWebView(webview, { src: pageUrl }); + const event = await waitForEvent(webview, 'did-navigate-in-page'); + expect(event.url).to.equal(`${pageUrl}#test`); + }); + }); describe('close event', () => { it('should fire when interior page calls window.close', async () => { - loadWebView(webview, { src: `file://${fixtures}/pages/close.html` }) - await waitForEvent(webview, 'close') - }) - }) + loadWebView(webview, { src: `file://${fixtures}/pages/close.html` }); + await waitForEvent(webview, 'close'); + }); + }); // FIXME(zcbenz): Disabled because of moving to OOPIF webview. xdescribe('setDevToolsWebContents() API', () => { it('sets webContents of webview as devtools', async () => { - const webview2 = new WebView() - loadWebView(webview2) + const webview2 = new WebView(); + loadWebView(webview2); // Setup an event handler for further usage. - const waitForDomReady = waitForEvent(webview2, 'dom-ready') + const waitForDomReady = waitForEvent(webview2, 'dom-ready'); - loadWebView(webview, { src: 'about:blank' }) - await waitForEvent(webview, 'dom-ready') - webview.getWebContents().setDevToolsWebContents(webview2.getWebContents()) - webview.getWebContents().openDevTools() + loadWebView(webview, { src: 'about:blank' }); + await waitForEvent(webview, 'dom-ready'); + webview.getWebContents().setDevToolsWebContents(webview2.getWebContents()); + webview.getWebContents().openDevTools(); - await waitForDomReady + await waitForDomReady; // Its WebContents should be a DevTools. - const devtools = webview2.getWebContents() - expect(devtools.getURL().startsWith('devtools://devtools')).to.be.true() + const devtools = webview2.getWebContents(); + expect(devtools.getURL().startsWith('devtools://devtools')).to.be.true(); - const name = await devtools.executeJavaScript('InspectorFrontendHost.constructor.name') - document.body.removeChild(webview2) + const name = await devtools.executeJavaScript('InspectorFrontendHost.constructor.name'); + document.body.removeChild(webview2); - expect(name).to.be.equal('InspectorFrontendHostImpl') - }) - }) + expect(name).to.be.equal('InspectorFrontendHostImpl'); + }); + }); describe('devtools-opened event', () => { it('should fire when webview.openDevTools() is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` - }) - await waitForEvent(webview, 'dom-ready') + }); + await waitForEvent(webview, 'dom-ready'); - webview.openDevTools() - await waitForEvent(webview, 'devtools-opened') + webview.openDevTools(); + await waitForEvent(webview, 'devtools-opened'); - webview.closeDevTools() - }) - }) + webview.closeDevTools(); + }); + }); describe('devtools-closed event', () => { it('should fire when webview.closeDevTools() is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` - }) - await waitForEvent(webview, 'dom-ready') + }); + await waitForEvent(webview, 'dom-ready'); - webview.openDevTools() - await waitForEvent(webview, 'devtools-opened') + webview.openDevTools(); + await waitForEvent(webview, 'devtools-opened'); - webview.closeDevTools() - await waitForEvent(webview, 'devtools-closed') - }) - }) + webview.closeDevTools(); + await waitForEvent(webview, 'devtools-closed'); + }); + }); describe('devtools-focused event', () => { it('should fire when webview.openDevTools() is called', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` - }) + }); - const waitForDevToolsFocused = waitForEvent(webview, 'devtools-focused') + const waitForDevToolsFocused = waitForEvent(webview, 'devtools-focused'); - await waitForEvent(webview, 'dom-ready') - webview.openDevTools() + await waitForEvent(webview, 'dom-ready'); + webview.openDevTools(); - await waitForDevToolsFocused - webview.closeDevTools() - }) - }) + await waitForDevToolsFocused; + webview.closeDevTools(); + }); + }); describe('.reload()', () => { it('should emit beforeunload handler', async () => { await loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/beforeunload-false.html` - }) + }); // Event handler has to be added before reload. - const waitForOnbeforeunload = waitForEvent(webview, 'ipc-message') + const waitForOnbeforeunload = waitForEvent(webview, 'ipc-message'); - webview.reload() + webview.reload(); - const { channel } = await waitForOnbeforeunload - expect(channel).to.equal('onbeforeunload') - }) - }) + const { channel } = await waitForOnbeforeunload; + expect(channel).to.equal('onbeforeunload'); + }); + }); describe('.goForward()', () => { it('should work after a replaced history entry', (done) => { - let loadCount = 1 + let loadCount = 1; const listener = (e) => { if (loadCount === 1) { - expect(e.channel).to.equal('history') - expect(e.args[0]).to.equal(1) - expect(webview.canGoBack()).to.be.false() - expect(webview.canGoForward()).to.be.false() + expect(e.channel).to.equal('history'); + expect(e.args[0]).to.equal(1); + expect(webview.canGoBack()).to.be.false(); + expect(webview.canGoForward()).to.be.false(); } else if (loadCount === 2) { - expect(e.channel).to.equal('history') - expect(e.args[0]).to.equal(2) - expect(webview.canGoBack()).to.be.false() - expect(webview.canGoForward()).to.be.true() - webview.removeEventListener('ipc-message', listener) + expect(e.channel).to.equal('history'); + expect(e.args[0]).to.equal(2); + expect(webview.canGoBack()).to.be.false(); + expect(webview.canGoForward()).to.be.true(); + webview.removeEventListener('ipc-message', listener); } - } + }; const loadListener = () => { if (loadCount === 1) { - webview.src = `file://${fixtures}/pages/base-page.html` + webview.src = `file://${fixtures}/pages/base-page.html`; } else if (loadCount === 2) { - expect(webview.canGoBack()).to.be.true() - expect(webview.canGoForward()).to.be.false() + expect(webview.canGoBack()).to.be.true(); + expect(webview.canGoForward()).to.be.false(); - webview.goBack() + webview.goBack(); } else if (loadCount === 3) { - webview.goForward() + webview.goForward(); } else if (loadCount === 4) { - expect(webview.canGoBack()).to.be.true() - expect(webview.canGoForward()).to.be.false() + expect(webview.canGoBack()).to.be.true(); + expect(webview.canGoForward()).to.be.false(); - webview.removeEventListener('did-finish-load', loadListener) - done() + webview.removeEventListener('did-finish-load', loadListener); + done(); } - loadCount += 1 - } + loadCount += 1; + }; - webview.addEventListener('ipc-message', listener) - webview.addEventListener('did-finish-load', loadListener) + webview.addEventListener('ipc-message', listener); + webview.addEventListener('did-finish-load', loadListener); loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/history-replace.html` - }) - }) - }) + }); + }); + }); // FIXME: https://github.com/electron/electron/issues/19397 xdescribe('.clearHistory()', () => { it('should clear the navigation history', async () => { - const message = waitForEvent(webview, 'ipc-message') + const message = waitForEvent(webview, 'ipc-message'); await loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/history.html` - }) - const event = await message + }); + const event = await message; - expect(event.channel).to.equal('history') - expect(event.args[0]).to.equal(2) - expect(webview.canGoBack()).to.be.true() + expect(event.channel).to.equal('history'); + expect(event.args[0]).to.equal(2); + expect(webview.canGoBack()).to.be.true(); - webview.clearHistory() - expect(webview.canGoBack()).to.be.false() - }) - }) + webview.clearHistory(); + expect(webview.canGoBack()).to.be.false(); + }); + }); describe('basic auth', () => { - const auth = require('basic-auth') + const auth = require('basic-auth'); it('should authenticate with correct credentials', (done) => { - const message = 'Authenticated' + const message = 'Authenticated'; const server = http.createServer((req, res) => { - const credentials = auth(req) + const credentials = auth(req); if (credentials.name === 'test' && credentials.pass === 'test') { - res.end(message) + res.end(message); } else { - res.end('failed') + res.end('failed'); } - server.close() - }) + server.close(); + }); server.listen(0, '127.0.0.1', () => { - const port = server.address().port + const port = server.address().port; webview.addEventListener('ipc-message', (e) => { - expect(e.channel).to.equal(message) - done() - }) + expect(e.channel).to.equal(message); + done(); + }); loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/basic-auth.html?port=${port}` - }) - }) - }) - }) + }); + }); + }); + }); describe('dom-ready event', () => { it('emits when document is loaded', (done) => { - const server = http.createServer(() => {}) + const server = http.createServer(() => {}); server.listen(0, '127.0.0.1', () => { - const port = server.address().port + const port = server.address().port; webview.addEventListener('dom-ready', () => { - done() - }) + done(); + }); loadWebView(webview, { src: `file://${fixtures}/pages/dom-ready.html?port=${port}` - }) - }) - }) + }); + }); + }); it('throws a custom error when an API method is called before the event is emitted', () => { const expectedErrorMessage = 'The WebView must be attached to the DOM ' + - 'and the dom-ready event emitted before this method can be called.' - expect(() => { webview.stop() }).to.throw(expectedErrorMessage) - }) - }) + 'and the dom-ready event emitted before this method can be called.'; + expect(() => { webview.stop(); }).to.throw(expectedErrorMessage); + }); + }); describe('executeJavaScript', () => { it('should support user gesture', async () => { await loadWebView(webview, { src: `file://${fixtures}/pages/fullscreen.html` - }) + }); // Event handler has to be added before js execution. - const waitForEnterHtmlFullScreen = waitForEvent(webview, 'enter-html-full-screen') + const waitForEnterHtmlFullScreen = waitForEvent(webview, 'enter-html-full-screen'); - const jsScript = "document.querySelector('video').webkitRequestFullscreen()" - webview.executeJavaScript(jsScript, true) + const jsScript = "document.querySelector('video').webkitRequestFullscreen()"; + webview.executeJavaScript(jsScript, true); - return waitForEnterHtmlFullScreen - }) + return waitForEnterHtmlFullScreen; + }); it('can return the result of the executed script', async () => { await loadWebView(webview, { src: 'about:blank' - }) + }); - const jsScript = "'4'+2" - const expectedResult = '42' + const jsScript = "'4'+2"; + const expectedResult = '42'; - const result = await webview.executeJavaScript(jsScript) - expect(result).to.equal(expectedResult) - }) - }) + const result = await webview.executeJavaScript(jsScript); + expect(result).to.equal(expectedResult); + }); + }); it('supports inserting CSS', async () => { - await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }) - await webview.insertCSS('body { background-repeat: round; }') - const result = await webview.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")') - expect(result).to.equal('round') - }) + await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }); + await webview.insertCSS('body { background-repeat: round; }'); + const result = await webview.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")'); + expect(result).to.equal('round'); + }); it('supports removing inserted CSS', async () => { - await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }) - const key = await webview.insertCSS('body { background-repeat: round; }') - await webview.removeInsertedCSS(key) - const result = await webview.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")') - expect(result).to.equal('repeat') - }) + await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }); + const key = await webview.insertCSS('body { background-repeat: round; }'); + await webview.removeInsertedCSS(key); + const result = await webview.executeJavaScript('window.getComputedStyle(document.body).getPropertyValue("background-repeat")'); + expect(result).to.equal('repeat'); + }); describe('sendInputEvent', () => { it('can send keyboard event', async () => { loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/onkeyup.html` - }) - await waitForEvent(webview, 'dom-ready') + }); + await waitForEvent(webview, 'dom-ready'); - const waitForIpcMessage = waitForEvent(webview, 'ipc-message') + const waitForIpcMessage = waitForEvent(webview, 'ipc-message'); webview.sendInputEvent({ type: 'keyup', keyCode: 'c', modifiers: ['shift'] - }) + }); - const { channel, args } = await waitForIpcMessage - expect(channel).to.equal('keyup') - expect(args).to.deep.equal(['C', 'KeyC', 67, true, false]) - }) + const { channel, args } = await waitForIpcMessage; + expect(channel).to.equal('keyup'); + expect(args).to.deep.equal(['C', 'KeyC', 67, true, false]); + }); it('can send mouse event', async () => { loadWebView(webview, { nodeintegration: 'on', src: `file://${fixtures}/pages/onmouseup.html` - }) - await waitForEvent(webview, 'dom-ready') + }); + await waitForEvent(webview, 'dom-ready'); - const waitForIpcMessage = waitForEvent(webview, 'ipc-message') + const waitForIpcMessage = waitForEvent(webview, 'ipc-message'); webview.sendInputEvent({ type: 'mouseup', modifiers: ['ctrl'], x: 10, y: 20 - }) + }); - const { channel, args } = await waitForIpcMessage - expect(channel).to.equal('mouseup') - expect(args).to.deep.equal([10, 20, false, true]) - }) - }) + const { channel, args } = await waitForIpcMessage; + expect(channel).to.equal('mouseup'); + expect(args).to.deep.equal([10, 20, false, true]); + }); + }); describe('media-started-playing media-paused events', () => { it('emits when audio starts and stops playing', async () => { - await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }) + await loadWebView(webview, { src: `file://${fixtures}/pages/base-page.html` }); // With the new autoplay policy, audio elements must be unmuted // see https://goo.gl/xX8pDD. @@ -969,225 +970,241 @@ describe(' tag', function () { audio.src = "../assets/tone.wav" document.body.appendChild(audio); audio.play() - ` - webview.executeJavaScript(source, true) - await waitForEvent(webview, 'media-started-playing') + `; + webview.executeJavaScript(source, true); + await waitForEvent(webview, 'media-started-playing'); - webview.executeJavaScript('document.querySelector("audio").pause()', true) - await waitForEvent(webview, 'media-paused') - }) - }) + webview.executeJavaScript('document.querySelector("audio").pause()', true); + await waitForEvent(webview, 'media-paused'); + }); + }); describe('found-in-page event', () => { it('emits when a request is made', (done) => { - let requestId = null - const activeMatchOrdinal = [] + let requestId = null; + const activeMatchOrdinal = []; const listener = (e) => { - expect(e.result.requestId).to.equal(requestId) - expect(e.result.matches).to.equal(3) - activeMatchOrdinal.push(e.result.activeMatchOrdinal) + expect(e.result.requestId).to.equal(requestId); + expect(e.result.matches).to.equal(3); + activeMatchOrdinal.push(e.result.activeMatchOrdinal); if (e.result.activeMatchOrdinal === e.result.matches) { - expect(activeMatchOrdinal).to.deep.equal([1, 2, 3]) - webview.stopFindInPage('clearSelection') - done() + expect(activeMatchOrdinal).to.deep.equal([1, 2, 3]); + webview.stopFindInPage('clearSelection'); + done(); } else { - listener2() + listener2(); } - } + }; const listener2 = () => { - requestId = webview.findInPage('virtual') - } - webview.addEventListener('found-in-page', listener) - webview.addEventListener('did-finish-load', listener2) - loadWebView(webview, { src: `file://${fixtures}/pages/content.html` }) + requestId = webview.findInPage('virtual'); + }; + webview.addEventListener('found-in-page', listener); + webview.addEventListener('did-finish-load', listener2); + loadWebView(webview, { src: `file://${fixtures}/pages/content.html` }); // TODO(deepak1556): With https://codereview.chromium.org/2836973002 // focus of the webContents is required when triggering the api. // Remove this workaround after determining the cause for // incorrect focus. - webview.focus() - }) - }) + webview.focus(); + }); + }); describe('did-change-theme-color event', () => { it('emits when theme color changes', async () => { loadWebView(webview, { src: `file://${fixtures}/pages/theme-color.html` - }) - await waitForEvent(webview, 'did-change-theme-color') - }) - }) + }); + await waitForEvent(webview, 'did-change-theme-color'); + }); + }); describe('.getWebContentsId', () => { it('can return the WebContents ID', async () => { - const src = 'about:blank' - await loadWebView(webview, { src }) + const src = 'about:blank'; + await loadWebView(webview, { src }); - expect(webview.getWebContentsId()).to.be.equal(webview.getWebContents().id) - }) - }) + expect(webview.getWebContentsId()).to.be.equal(webview.getWebContents().id); + }); + }); describe('.getWebContents', () => { it('can return the webcontents associated', async () => { - const src = 'about:blank' - await loadWebView(webview, { src }) + const src = 'about:blank'; + await loadWebView(webview, { src }); - const webviewContents = webview.getWebContents() - expect(webviewContents).to.be.an('object') - expect(webviewContents.getURL()).to.equal(src) - }) - }) + const webviewContents = webview.getWebContents(); + expect(webviewContents).to.be.an('object'); + expect(webviewContents.getURL()).to.equal(src); + }); + }); describe('.getWebContents filtering', () => { it('can return custom value', async () => { - const src = 'about:blank' - await loadWebView(webview, { src }) + const src = 'about:blank'; + await loadWebView(webview, { src }); - ipcRenderer.send('handle-next-remote-get-guest-web-contents', 'Hello World!') - expect(webview.getWebContents()).to.be.equal('Hello World!') - }) + ipcRenderer.send('handle-next-remote-get-guest-web-contents', 'Hello World!'); + expect(webview.getWebContents()).to.be.equal('Hello World!'); + }); it('throws when no returnValue set', async () => { - const src = 'about:blank' - await loadWebView(webview, { src }) + const src = 'about:blank'; + await loadWebView(webview, { src }); - ipcRenderer.send('handle-next-remote-get-guest-web-contents') - expect(() => webview.getWebContents()).to.throw('Blocked remote.getGuestWebContents()') - }) - }) + ipcRenderer.send('handle-next-remote-get-guest-web-contents'); + expect(() => webview.getWebContents()).to.throw('Blocked remote.getGuestWebContents()'); + }); + }); - describe('.printToPDF()', () => { + describe('.capturePage()', () => { before(function () { - if (!features.isPrintingEnabled()) { - this.skip() + // TODO(miniak): figure out why this is failing on windows + if (process.platform === 'win32') { + this.skip(); } - }) + }); + + it('returns a Promise with a NativeImage', async () => { + const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'; + await loadWebView(webview, { src }); - it('can print to PDF', async () => { - const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E' - await loadWebView(webview, { src }) + const image = await webview.capturePage(); + const imgBuffer = image.toPNG(); - const data = await webview.printToPDF({}) - expect(data).to.be.an.instanceof(Uint8Array).that.is.not.empty() - }) - }) + // Check the 25th byte in the PNG. + // Values can be 0,2,3,4, or 6. We want 6, which is RGB + Alpha + expect(imgBuffer[25]).to.equal(6); + }); + }); + + ifdescribe(features.isPrintingEnabled())('.printToPDF()', () => { + // TODO(deepak1556): Fix and enable after upgrade. + it.skip('can print to PDF', async () => { + const src = 'data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E'; + await loadWebView(webview, { src }); + + const data = await webview.printToPDF({}); + expect(data).to.be.an.instanceof(Uint8Array).that.is.not.empty(); + }); + }); describe('will-attach-webview event', () => { it('does not emit when src is not changed', (done) => { - loadWebView(webview) + loadWebView(webview); setTimeout(() => { const expectedErrorMessage = 'The WebView must be attached to the DOM ' + - 'and the dom-ready event emitted before this method can be called.' - expect(() => { webview.stop() }).to.throw(expectedErrorMessage) - done() - }) - }) + 'and the dom-ready event emitted before this method can be called.'; + expect(() => { webview.stop(); }).to.throw(expectedErrorMessage); + done(); + }); + }); it('supports changing the web preferences', async () => { - ipcRenderer.send('disable-node-on-next-will-attach-webview') + ipcRenderer.send('disable-node-on-next-will-attach-webview'); const message = await startLoadingWebViewAndWaitForMessage(webview, { nodeintegration: 'yes', src: `file://${fixtures}/pages/a.html` - }) + }); - const types = JSON.parse(message) + const types = JSON.parse(message); expect(types).to.include({ require: 'undefined', module: 'undefined', process: 'undefined', global: 'undefined' - }) - }) + }); + }); it('supports preventing a webview from being created', async () => { - ipcRenderer.send('prevent-next-will-attach-webview') + ipcRenderer.send('prevent-next-will-attach-webview'); loadWebView(webview, { src: `file://${fixtures}/pages/c.html` - }) - await waitForEvent(webview, 'destroyed') - }) + }); + await waitForEvent(webview, 'destroyed'); + }); it('supports removing the preload script', async () => { - ipcRenderer.send('disable-preload-on-next-will-attach-webview') + ipcRenderer.send('disable-preload-on-next-will-attach-webview'); const message = await startLoadingWebViewAndWaitForMessage(webview, { nodeintegration: 'yes', preload: path.join(fixtures, 'module', 'preload-set-global.js'), src: `file://${fixtures}/pages/a.html` - }) + }); - expect(message).to.equal('undefined') - }) - }) + expect(message).to.equal('undefined'); + }); + }); describe('DOM events', () => { - let div + let div; beforeEach(() => { - div = document.createElement('div') - div.style.width = '100px' - div.style.height = '10px' - div.style.overflow = 'hidden' - webview.style.height = '100%' - webview.style.width = '100%' - }) + div = document.createElement('div'); + div.style.width = '100px'; + div.style.height = '10px'; + div.style.overflow = 'hidden'; + webview.style.height = '100%'; + webview.style.width = '100%'; + }); afterEach(() => { - if (div != null) div.remove() - }) + if (div != null) div.remove(); + }); const generateSpecs = (description, sandbox) => { describe(description, () => { // TODO(nornagon): disabled during chromium roll 2019-06-11 due to a // 'ResizeObserver loop limit exceeded' error on Windows xit('emits resize events', async () => { - const firstResizeSignal = waitForEvent(webview, 'resize') - const domReadySignal = waitForEvent(webview, 'dom-ready') + const firstResizeSignal = waitForEvent(webview, 'resize'); + const domReadySignal = waitForEvent(webview, 'dom-ready'); - webview.src = `file://${fixtures}/pages/a.html` - webview.webpreferences = `sandbox=${sandbox ? 'yes' : 'no'}` - div.appendChild(webview) - document.body.appendChild(div) + webview.src = `file://${fixtures}/pages/a.html`; + webview.webpreferences = `sandbox=${sandbox ? 'yes' : 'no'}`; + div.appendChild(webview); + document.body.appendChild(div); - const firstResizeEvent = await firstResizeSignal - expect(firstResizeEvent.target).to.equal(webview) - expect(firstResizeEvent.newWidth).to.equal(100) - expect(firstResizeEvent.newHeight).to.equal(10) + const firstResizeEvent = await firstResizeSignal; + expect(firstResizeEvent.target).to.equal(webview); + expect(firstResizeEvent.newWidth).to.equal(100); + expect(firstResizeEvent.newHeight).to.equal(10); - await domReadySignal + await domReadySignal; - const secondResizeSignal = waitForEvent(webview, 'resize') + const secondResizeSignal = waitForEvent(webview, 'resize'); - const newWidth = 1234 - const newHeight = 789 - div.style.width = `${newWidth}px` - div.style.height = `${newHeight}px` + const newWidth = 1234; + const newHeight = 789; + div.style.width = `${newWidth}px`; + div.style.height = `${newHeight}px`; - const secondResizeEvent = await secondResizeSignal - expect(secondResizeEvent.target).to.equal(webview) - expect(secondResizeEvent.newWidth).to.equal(newWidth) - expect(secondResizeEvent.newHeight).to.equal(newHeight) - }) + const secondResizeEvent = await secondResizeSignal; + expect(secondResizeEvent.target).to.equal(webview); + expect(secondResizeEvent.newWidth).to.equal(newWidth); + expect(secondResizeEvent.newHeight).to.equal(newHeight); + }); it('emits focus event', async () => { - const domReadySignal = waitForEvent(webview, 'dom-ready') - webview.src = `file://${fixtures}/pages/a.html` - webview.webpreferences = `sandbox=${sandbox ? 'yes' : 'no'}` - document.body.appendChild(webview) + const domReadySignal = waitForEvent(webview, 'dom-ready'); + webview.src = `file://${fixtures}/pages/a.html`; + webview.webpreferences = `sandbox=${sandbox ? 'yes' : 'no'}`; + document.body.appendChild(webview); - await domReadySignal + await domReadySignal; // If this test fails, check if webview.focus() still works. - const focusSignal = waitForEvent(webview, 'focus') - webview.focus() - - await focusSignal - }) - }) - } - - generateSpecs('without sandbox', false) - generateSpecs('with sandbox', true) - }) -}) + const focusSignal = waitForEvent(webview, 'focus'); + webview.focus(); + + await focusSignal; + }); + }); + }; + + generateSpecs('without sandbox', false); + generateSpecs('with sandbox', true); + }); +}); diff --git a/spec/window-helpers.js b/spec/window-helpers.js index 8786762a71afc..973fa7a04e8a3 100644 --- a/spec/window-helpers.js +++ b/spec/window-helpers.js @@ -1,8 +1,8 @@ -const { expect } = require('chai') -const { remote } = require('electron') -const { BrowserWindow } = remote +const { expect } = require('chai'); +const { remote } = require('electron'); +const { BrowserWindow } = remote; -const { emittedOnce } = require('./events-helpers') +const { emittedOnce } = require('./events-helpers'); async function ensureWindowIsClosed (window) { if (window && !window.isDestroyed()) { @@ -12,33 +12,33 @@ async function ensureWindowIsClosed (window) { // children which need to be destroyed first. In that case, we // await the 'closed' event which signals the complete shutdown of the // window. - const isClosed = emittedOnce(window, 'closed') - window.destroy() - await isClosed + const isClosed = emittedOnce(window, 'closed'); + window.destroy(); + await isClosed; } else { // If there's no WebContents or if the WebContents is already destroyed, // then the 'closed' event has already been emitted so there's nothing to // wait for. - window.destroy() + window.destroy(); } } } exports.closeWindow = async (window = null, { assertSingleWindow } = { assertSingleWindow: true }) => { - await ensureWindowIsClosed(window) + await ensureWindowIsClosed(window); if (assertSingleWindow) { // Although we want to assert that no windows were left handing around // we also want to clean up the left over windows so that no future // tests fail as a side effect - const currentId = remote.getCurrentWindow().id - const windows = BrowserWindow.getAllWindows() + const currentId = remote.getCurrentWindow().id; + const windows = BrowserWindow.getAllWindows(); for (const win of windows) { if (win.id !== currentId) { - await ensureWindowIsClosed(win) + await ensureWindowIsClosed(win); } } - expect(windows).to.have.lengthOf(1) + expect(windows).to.have.lengthOf(1); } -} +}; diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index 01c2abbc4677b..d06b3526083b1 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -30,7 +30,10 @@ declare namespace NodeJS { deleteHiddenValue(obj: any, key: string): void; requestGarbageCollectionForTesting(): void; createDoubleIDWeakMap(): any; - setRemoteCallbackFreer(fn: Function, contextId: String, id: number, sender: any): void + setRemoteCallbackFreer(fn: Function, frameId: number, contextId: String, id: number, sender: any): void + weaklyTrackValue(value: any): void; + clearWeaklyTrackedValues(): void; + getWeaklyTrackedValues(): any[]; } interface Process { diff --git a/typings/internal-electron.d.ts b/typings/internal-electron.d.ts index 1dfdcc34b4b6b..8f23a11289c66 100644 --- a/typings/internal-electron.d.ts +++ b/typings/internal-electron.d.ts @@ -167,6 +167,7 @@ declare namespace ElectronInternal { // Created in web-view-impl public getWebContents(): Electron.WebContents; public getWebContentsId(): number; + public capturePage(rect?: Electron.Rectangle): Promise; } } diff --git a/yarn.lock b/yarn.lock index a0b8b87aa2f96..9cf7762cea1a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40,10 +40,10 @@ ora "^3.4.0" pretty-ms "^5.0.0" -"@electron/typescript-definitions@^8.6.1": - version "8.6.3" - resolved "https://registry.yarnpkg.com/@electron/typescript-definitions/-/typescript-definitions-8.6.3.tgz#6868d71bd56b6219150123969d1a6634629ef566" - integrity sha512-/PLUskJu2+W8BKkQQBQq2C0WFUyAeBaGEutt2sJkzzhkTWoVcKG4M36Me45+D0CiRqgAxTO9qR68O96+8vtIfg== +"@electron/typescript-definitions@^8.6.4": + version "8.6.4" + resolved "https://registry.yarnpkg.com/@electron/typescript-definitions/-/typescript-definitions-8.6.4.tgz#d4727c825afcebf6ff00a7d18416878288cac632" + integrity sha512-Fb4o56fikSE3id61U0MbPVBPcweVw8YwzO+JtT9aOorJR4baJZGmpsGxuaHeLSGv+D0Zd5XiWk13cEM/nv9fFw== dependencies: "@electron/docs-parser" "^0.4.1" "@types/node" "^11.13.7"