diff --git a/.github/actions/build-electron/action.yml b/.github/actions/build-electron/action.yml index 1a4a94169c..5397a8fb4b 100644 --- a/.github/actions/build-electron/action.yml +++ b/.github/actions/build-electron/action.yml @@ -63,7 +63,7 @@ runs: run: | cd src electron/script/copy-debug-symbols.py --target-cpu="${{ inputs.target-arch }}" --out-dir=out/Default/debug --compress - electron/script/strip-binaries.py --target-cpu="${{ inputs.target-arch }}" + electron/script/strip-binaries.py --target-cpu="${{ inputs.target-arch }}" --verbose electron/script/add-debug-link.py --target-cpu="${{ inputs.target-arch }}" --debug-dir=out/Default/debug - name: Build Electron dist.zip ${{ inputs.step-suffix }} shell: bash diff --git a/.github/actions/checkout/action.yml b/.github/actions/checkout/action.yml index dbd81e6a9e..aabce459dc 100644 --- a/.github/actions/checkout/action.yml +++ b/.github/actions/checkout/action.yml @@ -69,7 +69,7 @@ runs: cache_path=/mnt/cross-instance-cache/$DEPSHASH.tar echo "Using cache key: $DEPSHASH" echo "Checking for cache in: $cache_path" - if [ ! -f "$cache_path" ]; then + if [ ! -f "$cache_path" ] || [ `du $cache_path | cut -f1` = "0" ]; then echo "cache_exists=false" >> $GITHUB_OUTPUT echo "Cache Does Not Exist for $DEPSHASH" else @@ -77,6 +77,19 @@ runs: echo "Cache Already Exists for $DEPSHASH, Skipping.." fi fi + - name: Check cross instance cache disk space + if: steps.check-cache.outputs.cache_exists == 'false' + shell: bash + run: | + # if there is less than 20 GB free space then creating the cache might fail so exit early + freespace=`df -m /mnt/cross-instance-cache | grep -w /mnt/cross-instance-cache | awk '{print $4}'` + freespace_human=`df -h /mnt/cross-instance-cache | grep -w /mnt/cross-instance-cache | awk '{print $4}'` + if [ $freespace -le 20000 ]; then + echo "The cross mount cache has $freespace_human free space which is not enough - exiting" + exit 1 + else + echo "The cross mount cache has $freespace_human free space - continuing" + fi - name: Gclient Sync if: steps.check-cache.outputs.cache_exists == 'false' shell: bash diff --git a/.github/actions/restore-cache-aks/action.yml b/.github/actions/restore-cache-aks/action.yml index eccbb5ebfc..70c67f1d1c 100644 --- a/.github/actions/restore-cache-aks/action.yml +++ b/.github/actions/restore-cache-aks/action.yml @@ -17,6 +17,11 @@ runs: fi echo "Persisted cache is $(du -sh $cache_path | cut -f1)" + if [ `du $cache_path | cut -f1` = "0" ]; then + echo "Cache is empty - exiting" + exit 1 + fi + mkdir temp-cache tar -xf $cache_path -C temp-cache echo "Unzipped cache is $(du -sh temp-cache/src | cut -f1)" diff --git a/.github/actions/restore-cache-azcopy/action.yml b/.github/actions/restore-cache-azcopy/action.yml index d41014b755..562fbc6f43 100644 --- a/.github/actions/restore-cache-azcopy/action.yml +++ b/.github/actions/restore-cache-azcopy/action.yml @@ -44,6 +44,11 @@ runs: shell: bash run: | echo "Downloaded cache is $(du -sh $DEPSHASH.tar | cut -f1)" + if [ `du $DEPSHASH.tar | cut -f1` = "0" ]; then + echo "Cache is empty - exiting" + exit 1 + fi + mkdir temp-cache tar -xf $DEPSHASH.tar -C temp-cache echo "Unzipped cache is $(du -sh temp-cache/src | cut -f1)" diff --git a/.github/workflows/archaeologist-dig.yml b/.github/workflows/archaeologist-dig.yml index 0feb3d3e08..7f8686ab8d 100644 --- a/.github/workflows/archaeologist-dig.yml +++ b/.github/workflows/archaeologist-dig.yml @@ -9,11 +9,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 #v4.0.2 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.0.2 with: fetch-depth: 0 - name: Setup Node.js/npm - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af with: node-version: 20.11.x - name: Setting Up Dig Site diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 22e896ce89..d096be785d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,7 +49,7 @@ jobs: build-image-sha: ${{ steps.set-output.outputs.build-image-sha }} docs-only: ${{ steps.set-output.outputs.docs-only }} steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 #v4.0.2 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.0.2 - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 id: filter with: @@ -103,7 +103,7 @@ jobs: build-image-sha: ${{ needs.setup.outputs.build-image-sha }} steps: - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 @@ -129,7 +129,7 @@ jobs: build-image-sha: ${{ needs.setup.outputs.build-image-sha}} steps: - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 diff --git a/.github/workflows/clean-src-cache.yml b/.github/workflows/clean-src-cache.yml new file mode 100644 index 0000000000..73af458ba7 --- /dev/null +++ b/.github/workflows/clean-src-cache.yml @@ -0,0 +1,21 @@ +name: Clean Source Cache + +on: + schedule: + - cron: "0 0 * * SUN" # Run at midnight every Sunday + +jobs: + clean-src-cache: + runs-on: electron-arc-linux-amd64-32core + container: + image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1 + options: --user root + volumes: + - /mnt/cross-instance-cache:/mnt/cross-instance-cache + steps: + - name: Cleanup Source Cache + shell: bash + run: | + df -h /mnt/cross-instance-cache + find /mnt/cross-instance-cache -type f -mtime +30 -delete + df -h /mnt/cross-instance-cache diff --git a/.github/workflows/linux-publish.yml b/.github/workflows/linux-publish.yml index ebccdf9bb6..cfba906322 100644 --- a/.github/workflows/linux-publish.yml +++ b/.github/workflows/linux-publish.yml @@ -30,7 +30,7 @@ jobs: GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' steps: - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 diff --git a/.github/workflows/macos-publish.yml b/.github/workflows/macos-publish.yml index 278a40e494..34baead94a 100644 --- a/.github/workflows/macos-publish.yml +++ b/.github/workflows/macos-publish.yml @@ -31,7 +31,7 @@ jobs: GCLIENT_EXTRA_ARGS: '--custom-var=checkout_mac=True --custom-var=host_os=mac' steps: - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 diff --git a/.github/workflows/pipeline-electron-docs-only.yml b/.github/workflows/pipeline-electron-docs-only.yml index ad5f80a540..a5626731ee 100644 --- a/.github/workflows/pipeline-electron-docs-only.yml +++ b/.github/workflows/pipeline-electron-docs-only.yml @@ -20,7 +20,7 @@ jobs: container: ${{ fromJSON(inputs.container) }} steps: - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 diff --git a/.github/workflows/pipeline-electron-lint.yml b/.github/workflows/pipeline-electron-lint.yml index 3fcab9b91f..79b11b7c6f 100644 --- a/.github/workflows/pipeline-electron-lint.yml +++ b/.github/workflows/pipeline-electron-lint.yml @@ -20,7 +20,7 @@ jobs: container: ${{ fromJSON(inputs.container) }} steps: - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 diff --git a/.github/workflows/pipeline-segment-electron-build.yml b/.github/workflows/pipeline-segment-electron-build.yml index f5743a8f5b..60eb57198c 100644 --- a/.github/workflows/pipeline-segment-electron-build.yml +++ b/.github/workflows/pipeline-segment-electron-build.yml @@ -87,7 +87,7 @@ jobs: run: | mkdir src - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 @@ -99,7 +99,7 @@ jobs: run: df -h - name: Setup Node.js/npm if: ${{ inputs.target-platform == 'macos' }} - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af with: node-version: 20.11.x cache: yarn @@ -165,7 +165,7 @@ jobs: with: use-cache: 'false' - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 diff --git a/.github/workflows/pipeline-segment-electron-gn-check.yml b/.github/workflows/pipeline-segment-electron-gn-check.yml index 75abed8b4b..9ac2d020af 100644 --- a/.github/workflows/pipeline-segment-electron-gn-check.yml +++ b/.github/workflows/pipeline-segment-electron-gn-check.yml @@ -44,7 +44,7 @@ jobs: container: ${{ fromJSON(inputs.check-container) }} steps: - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 @@ -104,10 +104,14 @@ jobs: - name: Add CHROMIUM_BUILDTOOLS_PATH to env run: echo "CHROMIUM_BUILDTOOLS_PATH=$(pwd)/src/buildtools" >> $GITHUB_ENV - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 + - name: Install Dependencies + run: | + cd src/electron + node script/yarn install --frozen-lockfile - name: Default GN gen run: | cd src/electron diff --git a/.github/workflows/pipeline-segment-electron-test.yml b/.github/workflows/pipeline-segment-electron-test.yml index 51a3b46cc8..f1303b6c34 100644 --- a/.github/workflows/pipeline-segment-electron-test.yml +++ b/.github/workflows/pipeline-segment-electron-test.yml @@ -113,7 +113,7 @@ jobs: fi done - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 diff --git a/.github/workflows/pipeline-segment-node-nan-test.yml b/.github/workflows/pipeline-segment-node-nan-test.yml index 67d58e5586..3dd391e278 100644 --- a/.github/workflows/pipeline-segment-node-nan-test.yml +++ b/.github/workflows/pipeline-segment-node-nan-test.yml @@ -45,7 +45,7 @@ jobs: container: ${{ fromJSON(inputs.test-container) }} steps: - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 @@ -109,7 +109,7 @@ jobs: container: ${{ fromJSON(inputs.test-container) }} steps: - name: Checkout Electron - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: src/electron fetch-depth: 0 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 2e782be282..6e9ece6073 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -22,7 +22,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false @@ -50,6 +50,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 + uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 with: sarif_file: results.sarif diff --git a/.github/workflows/update_appveyor_image.yml b/.github/workflows/update_appveyor_image.yml index e6f3bf8381..7d3a8ef1fc 100644 --- a/.github/workflows/update_appveyor_image.yml +++ b/.github/workflows/update_appveyor_image.yml @@ -19,12 +19,12 @@ jobs: with: creds: ${{ secrets.APPVEYOR_UPDATER_GH_APP_CREDS }} - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 token: ${{ steps.generate-token.outputs.token }} - name: Setup Node.js - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 with: node-version: 20.11.x - name: Yarn install diff --git a/BUILD.gn b/BUILD.gn index 0b1f112e9e..b0c54bf6bc 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -865,6 +865,7 @@ if (is_mac) { ":electron_framework_resources", ":electron_swiftshader_library", ":electron_xibs", + "//third_party/electron_node:node_lib", ] if (!is_mas_build) { deps += [ ":electron_crashpad_helper" ] @@ -920,7 +921,6 @@ if (is_mac) { "shell/app/electron_main_mac.cc", "shell/app/uv_stdio_fix.cc", "shell/app/uv_stdio_fix.h", - "shell/common/electron_constants.cc", ] include_dirs = [ "." ] info_plist = "shell/renderer/resources/mac/Info.plist" @@ -1189,6 +1189,7 @@ if (is_mac) { "//components/crash/core/app", "//content:sandbox_helper_win", "//electron/buildflags", + "//third_party/electron_node:node_lib", "//ui/strings", ] @@ -1535,7 +1536,7 @@ copy("node_gypi_headers") { action("node_version_header") { inputs = [ "$node_dir/src/node_version.h" ] outputs = [ "$node_headers_dir/include/node/node_version.h" ] - script = "script/generate_node_version_header.py" + script = "script/node/generate_node_version_header.py" args = rebase_path(inputs) + rebase_path(outputs) if (node_module_version != "") { args += [ "$node_module_version" ] diff --git a/build/args/all.gn b/build/args/all.gn index d982726bc3..f8ed5769a9 100644 --- a/build/args/all.gn +++ b/build/args/all.gn @@ -77,3 +77,6 @@ enterprise_cloud_content_analysis = false # TODO: remove dependency on legacy ipc # https://issues.chromium.org/issues/40943039 content_enable_legacy_ipc = true + +# Electron has its own unsafe-buffers enforcement directories. +clang_unsafe_buffers_paths = "//electron/electron_unsafe_buffers_paths.txt" diff --git a/docs/api/base-window.md b/docs/api/base-window.md index 30f2db76fa..c6cc7c878b 100644 --- a/docs/api/base-window.md +++ b/docs/api/base-window.md @@ -512,7 +512,7 @@ Sets the content view of the window. #### `win.getContentView()` -Returns [View](view.md) - The content view of the window. +Returns [`View`](view.md) - The content view of the window. #### `win.destroy()` diff --git a/docs/api/power-monitor.md b/docs/api/power-monitor.md index 6f53114b12..0801f81a07 100644 --- a/docs/api/power-monitor.md +++ b/docs/api/power-monitor.md @@ -42,6 +42,8 @@ See https://developer.apple.com/library/archive/documentation/Performance/Concep ### Event: 'speed-limit-change' _macOS_ _Windows_ +Returns: + * `limit` number - The operating system's advertised speed limit for CPUs, in percent. Notification of a change in the operating system's advertised speed limit for diff --git a/electron_unsafe_buffers_paths.txt b/electron_unsafe_buffers_paths.txt new file mode 100644 index 0000000000..1438934591 --- /dev/null +++ b/electron_unsafe_buffers_paths.txt @@ -0,0 +1,34 @@ +# Copyright 2024 The Electron Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# The set of path prefixes that should be checked for unsafe buffer usage (see +# -Wunsafe-buffer-usage in Clang). +# +# *** +# Paths should be written as relative to the root of the source tree with +# unix-style path separators. Directory prefixes should end with `/`, such +# as `base/`. +# *** +# +# Files in this set are known to not use pointer arithmetic/subscripting, and +# make use of constructs like base::span or containers like std::vector instead. +# +# See `docs/unsafe_buffers.md`. + +# These directories are excluded because they come from outside Electron and +# we don't have control over their contents. +-base/ +-chrome/ +-components/ +-device/ +-extensions/ +-google_apis/ +-net/ +-services/ +-skia/ +-third_party/ +-tools/ +-ui/ +-url/ +-v8/ diff --git a/filenames.gni b/filenames.gni index e5b7d73dd9..039f60c9ac 100644 --- a/filenames.gni +++ b/filenames.gni @@ -582,7 +582,6 @@ filenames = { "shell/common/crash_keys.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/electron_paths.h", "shell/common/gin_converters/accelerator_converter.cc", diff --git a/lib/node/init.ts b/lib/node/init.ts index 8857eabb6b..341666db09 100644 --- a/lib/node/init.ts +++ b/lib/node/init.ts @@ -6,7 +6,7 @@ wrapFsWithAsar(require('fs')); // See ElectronRendererClient::DidCreateScriptContext. if ((globalThis as any).blinkfetch) { - const keys = ['fetch', 'Response', 'FormData', 'Request', 'Headers']; + const keys = ['fetch', 'Response', 'FormData', 'Request', 'Headers', 'EventSource']; for (const key of keys) { (globalThis as any)[key] = (globalThis as any)[`blink${key}`]; } diff --git a/lib/worker/init.ts b/lib/worker/init.ts index d23a4f942b..ccbd3596fc 100644 --- a/lib/worker/init.ts +++ b/lib/worker/init.ts @@ -19,7 +19,7 @@ global.require = makeRequireFunction(global.module); // See WebWorkerObserver::WorkerScriptReadyForEvaluation. if ((globalThis as any).blinkfetch) { - const keys = ['fetch', 'Response', 'FormData', 'Request', 'Headers']; + const keys = ['fetch', 'Response', 'FormData', 'Request', 'Headers', 'EventSource']; for (const key of keys) { (globalThis as any)[key] = (globalThis as any)[`blink${key}`]; } diff --git a/patches/node/build_add_gn_build_files.patch b/patches/node/build_add_gn_build_files.patch index 6197fab911..1cb0332f0c 100644 --- a/patches/node/build_add_gn_build_files.patch +++ b/patches/node/build_add_gn_build_files.patch @@ -11,7 +11,7 @@ really in 20/21. We have to wait until 22 is released to be able to build with upstream GN files. diff --git a/BUILD.gn b/BUILD.gn -index 1ed186b597eece7c34cb69c8e1e20870555a040d..2ce1e8a7dcca2ba153d387d11970c72b5f43c167 100644 +index 1ed186b597eece7c34cb69c8e1e20870555a040d..e36168f0a051ca2fa2fc024aadcf5375b860105e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1,14 +1,406 @@ @@ -113,7 +113,7 @@ index 1ed186b597eece7c34cb69c8e1e20870555a040d..2ce1e8a7dcca2ba153d387d11970c72b +} + +action("node_js2c_original_fs") { -+ script = "tools/generate_original_fs.py" ++ script = "//electron/script/node/generate_original_fs.py" + inputs = fs_files + outputs = [] + foreach(file, fs_files + original_fs_files) { @@ -2384,30 +2384,6 @@ index 0000000000000000000000000000000000000000..37c16859003e61636fe2f1a4040b1e90 + f.write(FILENAMES_JSON_HEADER) + f.write(json.dumps(out, sort_keys=True, indent=2, separators=(',', ': '))) + f.write('\n') -diff --git a/tools/generate_original_fs.py b/tools/generate_original_fs.py -new file mode 100644 -index 0000000000000000000000000000000000000000..5259e6a7a1fd6b21df69dc461dee67d95800c2c8 ---- /dev/null -+++ b/tools/generate_original_fs.py -@@ -0,0 +1,18 @@ -+import os -+import sys -+ -+node_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) -+out_dir = sys.argv[1] -+fs_files = sys.argv[2:] -+ -+for fs_file in fs_files: -+ with open(os.path.join(node_root_dir, fs_file), 'r') as f: -+ contents = f.read() -+ original_fs_file = fs_file.replace('internal/fs/', 'internal/original-fs/').replace('lib/fs.js', 'lib/original-fs.js').replace('lib/fs/', 'lib/original-fs/') -+ -+ with open(os.path.join(out_dir, fs_file), 'w') as original_f: -+ original_f.write(contents) -+ -+ with open(os.path.join(out_dir, original_fs_file), 'w') as transformed_f: -+ transformed_contents = contents.replace('internal/fs/', 'internal/original-fs/').replace('require(\'fs', 'require(\'original-fs') -+ transformed_f.write(transformed_contents) diff --git a/tools/install.py b/tools/install.py index b132c7bf26c02886a7ab341a1973bf449744ba0f..757e3e60a7be01fac55c5fbb010dbbae00b1bfca 100755 --- a/tools/install.py diff --git a/script/gen-libc++-filenames.js b/script/gen-libc++-filenames.js index 86776338d8..84cb2b2e3b 100644 --- a/script/gen-libc++-filenames.js +++ b/script/gen-libc++-filenames.js @@ -1,3 +1,5 @@ +const chalk = require('chalk'); + const fs = require('node:fs'); const path = require('node:path'); @@ -17,6 +19,29 @@ function findAllHeaders (basePath) { return toReturn; } +const diff = (array1, array2) => { + const set1 = new Set(array1); + const set2 = new Set(array2); + + const added = array1.filter(item => !set2.has(item)); + const removed = array2.filter(item => !set1.has(item)); + + console.log(chalk.white.bgGreen.bold('Files Added:')); + added.forEach(item => console.log(chalk.green.bold(`+ ${item}`))); + + console.log(chalk.white.bgRed.bold('Files Removed:')); + removed.forEach(item => console.log(chalk.red.bold(`- ${item}`))); +}; + +const parseHeaders = (name, content) => { + const pattern = new RegExp(`${name}_headers\\s*=\\s*\\[(.*?)\\]`, 's'); + const headers = content.match(pattern); + if (!headers) return []; + return headers[1].split(',') + .map(item => item.trim().replace(/"/g, '')) + .filter(item => item.length > 0); +}; + for (const folder of ['libc++', 'libc++abi']) { const prettyName = folder.replace(/\+/g, 'x'); @@ -25,21 +50,26 @@ for (const folder of ['libc++', 'libc++abi']) { const headers = findAllHeaders(libcxxIncludeDir).map(absPath => path.relative(path.resolve(__dirname, '../..', gclientPath), absPath)); + const newHeaders = headers.map(f => `//${path.posix.join(gclientPath, f)}`); const content = `${prettyName}_headers = [ - ${headers.map(f => `"//${path.posix.join(gclientPath, f)}"`).join(',\n ')}, + ${newHeaders.map(h => `"${h}"`).join(',\n ')}, ] ${prettyName}_licenses = [ "//third_party/${folder}/src/LICENSE.TXT" ] `; - const filenamesPath = path.resolve(__dirname, '..', `filenames.${prettyName}.gni`); + const file = `filenames.${prettyName}.gni`; + const filenamesPath = path.resolve(__dirname, '..', file); if (check) { const currentContent = fs.readFileSync(filenamesPath, 'utf8'); if (currentContent !== content) { - console.log('currentContent: ', currentContent); - console.log('content: ', content); - throw new Error(`${prettyName} filenames need to be regenerated, latest generation does not match current file. Please run node gen-libc++-filenames.js`); + const currentHeaders = parseHeaders(prettyName, currentContent); + + console.error(chalk.bold(`${file} contents are not up to date:\n`)); + diff(currentHeaders, newHeaders); + console.error(chalk.bold(`\nRun node gen-libc++-filenames.js to regenerate ${file}`)); + process.exit(1); } } else { console.log(filenamesPath); diff --git a/script/generate_node_version_header.py b/script/generate_node_version_header.py deleted file mode 100755 index ab9bba7cd3..0000000000 --- a/script/generate_node_version_header.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python3 -import re -import sys - -node_version_file = sys.argv[1] -out_file = sys.argv[2] -NMV = None -if len(sys.argv) > 3: - NMV = sys.argv[3] - -with open(node_version_file, 'r', encoding='utf-8') as in_file, \ - open(out_file, 'w', encoding='utf-8') as out_file: - changed = False - contents = in_file.read() - new_contents = re.sub( - r'^#define NODE_MODULE_VERSION [0-9]+$', - '#define NODE_MODULE_VERSION ' + NMV, - contents, - flags=re.MULTILINE) - - changed = contents != new_contents - - if not changed and NMV is not None: - raise Exception("Did not modify the NMV from nodes value, this value MUST " - "differ from node") - - out_file.writelines(new_contents) diff --git a/script/lib/config.py b/script/lib/config.py index 131cd9b24c..bca8b616fe 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -28,16 +28,17 @@ def get_target_arch(): return 'x64' return arch - -def enable_verbose_mode(): +def set_verbose_mode(mode): print('Running in verbose mode') global verbose_mode - verbose_mode = True - + verbose_mode = mode def is_verbose_mode(): return verbose_mode +def verbose_mode_print(output): + if verbose_mode: + print(output) def get_zip_name(name, version, suffix=''): arch = get_target_arch() diff --git a/script/lib/util.py b/script/lib/util.py index eb03a856fd..bc70cb2ed1 100644 --- a/script/lib/util.py +++ b/script/lib/util.py @@ -11,9 +11,7 @@ import sys from urllib.request import urlopen import zipfile -# from lib.config import is_verbose_mode -def is_verbose_mode(): - return False +from lib.config import verbose_mode_print ELECTRON_DIR = os.path.abspath( os.path.dirname(os.path.dirname(os.path.dirname(__file__))) @@ -112,13 +110,11 @@ def safe_mkdir(path): def execute(argv, env=None, cwd=None): if env is None: env = os.environ - if is_verbose_mode(): - print(' '.join(argv)) + verbose_mode_print(' '.join(argv)) try: output = subprocess.check_output(argv, stderr=subprocess.STDOUT, env=env, cwd=cwd) - if is_verbose_mode(): - print(output) + verbose_mode_print(output.decode('utf-8').strip()) return output except subprocess.CalledProcessError as e: print(e.output) diff --git a/script/node/generate_node_version_header.py b/script/node/generate_node_version_header.py new file mode 100755 index 0000000000..112d059eda --- /dev/null +++ b/script/node/generate_node_version_header.py @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import re +import sys + +node_version_file = sys.argv[1] +out_file = sys.argv[2] +NMV = None +if len(sys.argv) > 3: + NMV = sys.argv[3] + +with open(node_version_file, 'r', encoding='utf-8') as in_file: + with open(out_file, 'w', encoding='utf-8') as out_file: + changed = False + contents = in_file.read() + new_contents = re.sub(r'^#define NODE_MODULE_VERSION [0-9]+$', + '#define NODE_MODULE_VERSION ' + NMV, + contents, flags=re.MULTILINE) + + changed = contents != new_contents + + if not changed and NMV is not None: + raise Exception('NMV must differ from current value in Node.js') + + out_file.writelines(new_contents) diff --git a/script/node/generate_original_fs.py b/script/node/generate_original_fs.py new file mode 100644 index 0000000000..c2a54ac7ab --- /dev/null +++ b/script/node/generate_original_fs.py @@ -0,0 +1,28 @@ +#!/usr/bin/python3 + +import os +import sys + +NODE_ROOT_DIR = "../../third_party/electron_node" +out_dir = sys.argv[1] +fs_files = sys.argv[2:] + +for fs_file in fs_files: + with open(os.path.join(NODE_ROOT_DIR, fs_file), 'r', + encoding='utf-8') as f: + contents = f.read() + original_fs_file = fs_file.replace('internal/fs/', + 'internal/original-fs/').replace('lib/fs.js', + 'lib/original-fs.js').replace('lib/fs/', + 'lib/original-fs/') + + with open(os.path.join(out_dir, fs_file), 'w', encoding='utf-8' + ) as original_f: + original_f.write(contents) + + with open(os.path.join(out_dir, original_fs_file), 'w', + encoding='utf-8') as transformed_f: + transformed_contents = contents.replace('internal/fs/', + 'internal/original-fs/').replace('require(\'fs', + 'require(\'original-fs') + transformed_f.write(transformed_contents) diff --git a/script/release/uploaders/upload.py b/script/release/uploaders/upload.py index c65d01ff0b..c5fa934b01 100755 --- a/script/release/uploaders/upload.py +++ b/script/release/uploaders/upload.py @@ -16,8 +16,9 @@ sys.path.append( from zipfile import ZipFile from lib.config import PLATFORM, get_target_arch, \ - get_zip_name, enable_verbose_mode, \ - is_verbose_mode, get_platform_key + get_zip_name, set_verbose_mode, \ + is_verbose_mode, get_platform_key, \ + verbose_mode_print from lib.util import get_electron_branding, execute, get_electron_version, \ store_artifact, get_electron_exec, get_out_dir, \ SRC_DIR, ELECTRON_DIR, TS_NODE @@ -45,8 +46,7 @@ CXX_OBJECTS_NAME = get_zip_name(PROJECT_NAME, ELECTRON_VERSION, def main(): args = parse_args() - if args.verbose: - enable_verbose_mode() + set_verbose_mode(args.verbose) if args.upload_to_storage: utcnow = datetime.datetime.utcnow() args.upload_timestamp = utcnow.strftime('%Y%m%d') @@ -391,9 +391,8 @@ def get_release(version): release_env = os.environ.copy() release_env['NODE_NO_WARNINGS'] = '1' release_info = execute([TS_NODE, script_path, version], release_env) - if is_verbose_mode(): - print(f'Release info for version: {version}:\n') - print(release_info) + verbose_mode_print(f'Release info for version: {version}:\n') + verbose_mode_print(release_info) release = json.loads(release_info) return release diff --git a/script/strip-binaries.py b/script/strip-binaries.py index e21fdeb6a6..61fd543862 100755 --- a/script/strip-binaries.py +++ b/script/strip-binaries.py @@ -4,11 +4,26 @@ import argparse import os import sys -from lib.config import enable_verbose_mode +from lib.config import set_verbose_mode, is_verbose_mode, verbose_mode_print from lib.util import execute, get_linux_binaries, get_out_dir +def get_size(path): + size = os.path.getsize(path) + units = ["bytes", "KB", "MB", "GB"] + for unit in units: + if size < 1024: + return f"{size:.2f} {unit}" + size /= 1024 + raise ValueError("File size is too large to be processed") + def strip_binaries(directory, target_cpu): + if not os.path.isdir(directory): + verbose_mode_print('Directory ' + directory + ' does not exist.') + return + + verbose_mode_print('Stripping binaries in ' + directory) for binary in get_linux_binaries(): + verbose_mode_print('\nStripping ' + binary) binary_path = os.path.join(directory, binary) if os.path.isfile(binary_path): strip_binary(binary_path, target_cpu) @@ -20,14 +35,23 @@ def strip_binary(binary_path, target_cpu): strip = 'aarch64-linux-gnu-strip' else: strip = 'strip' - execute([ - strip, '--discard-all', '--strip-debug', '--preserve-dates', - binary_path]) + + strip_args = [strip, + '--discard-all', + '--strip-debug', + '--preserve-dates', + binary_path] + if (is_verbose_mode()): + strip_args.insert(1, '--verbose') + verbose_mode_print('Binary size before stripping: ' + + str(get_size(binary_path))) + execute(strip_args) + verbose_mode_print('Binary size after stripping: ' + + str(get_size(binary_path))) def main(): args = parse_args() - if args.verbose: - enable_verbose_mode() + set_verbose_mode(args.verbose) if args.file: strip_binary(args.file, args.target_cpu) else: @@ -43,6 +67,7 @@ def parse_args(): help='Path to a specific file to strip.', required=False) parser.add_argument('-v', '--verbose', + default=False, action='store_true', help='Prints the output of the subprocesses') parser.add_argument('--target-cpu', diff --git a/shell/app/electron_content_client.cc b/shell/app/electron_content_client.cc index 8e8b18c17c..136b15e848 100644 --- a/shell/app/electron_content_client.cc +++ b/shell/app/electron_content_client.cc @@ -187,7 +187,8 @@ void ElectronContentClient::AddPlugins( pdf_info.name = kPDFInternalPluginName; pdf_info.description = kPDFPluginDescription; // This isn't a real file path; it's just used as a unique identifier. - pdf_info.path = base::FilePath(kPdfPluginPath); + static constexpr std::string_view kPdfPluginPath = "internal-pdf-viewer"; + pdf_info.path = base::FilePath::FromASCII(kPdfPluginPath); content::WebPluginMimeType pdf_mime_type( pdf::kInternalPluginMimeType, kPDFPluginExtension, kPDFPluginDescription); pdf_info.mime_types.push_back(pdf_mime_type); diff --git a/shell/app/electron_library_main.mm b/shell/app/electron_library_main.mm index 114b8e22e8..3945d97db1 100644 --- a/shell/app/electron_library_main.mm +++ b/shell/app/electron_library_main.mm @@ -9,6 +9,7 @@ #include "base/apple/bundle_locations.h" #include "base/apple/scoped_nsautorelease_pool.h" #include "base/at_exit.h" +#include "base/command_line.h" #include "base/i18n/icu_util.h" #include "content/public/app/content_main.h" #include "electron/fuses.h" @@ -16,21 +17,22 @@ #include "shell/app/node_main.h" #include "shell/common/electron_command_line.h" #include "shell/common/mac/main_application_bundle.h" +#include "uv.h" int ElectronMain(int argc, char* argv[]) { - electron::ElectronMainDelegate delegate; - content::ContentMainParams params(&delegate); - params.argc = argc; - params.argv = const_cast(argv); + argv = uv_setup_args(argc, argv); + base::CommandLine::Init(argc, argv); electron::ElectronCommandLine::Init(argc, argv); + electron::ElectronMainDelegate delegate; + // Ensure that Bundle Id is set before ContentMain. // Refs https://chromium-review.googlesource.com/c/chromium/src/+/5581006 delegate.OverrideChildProcessPath(); delegate.OverrideFrameworkBundlePath(); delegate.SetUpBundleOverrides(); - return content::ContentMain(std::move(params)); + return content::ContentMain(content::ContentMainParams{&delegate}); } int ElectronInitializeICUandStartNode(int argc, char* argv[]) { @@ -39,6 +41,10 @@ int ElectronInitializeICUandStartNode(int argc, char* argv[]) { return 1; } + argv = uv_setup_args(argc, argv); + base::CommandLine::Init(argc, argv); + electron::ElectronCommandLine::Init(argc, argv); + base::AtExitManager atexit_manager; base::apple::ScopedNSAutoreleasePool pool; base::apple::SetOverrideFrameworkBundlePath( @@ -47,5 +53,5 @@ int ElectronInitializeICUandStartNode(int argc, char* argv[]) { .Append("Frameworks") .Append(ELECTRON_PRODUCT_NAME " Framework.framework")); base::i18n::InitializeICU(); - return electron::NodeMain(argc, argv); + return electron::NodeMain(); } diff --git a/shell/app/electron_main_linux.cc b/shell/app/electron_main_linux.cc index 14347d4880..d12cd55f7e 100644 --- a/shell/app/electron_main_linux.cc +++ b/shell/app/electron_main_linux.cc @@ -8,6 +8,7 @@ #include "base/at_exit.h" #include "base/command_line.h" #include "base/i18n/icu_util.h" +#include "base/strings/cstring_view.h" #include "content/public/app/content_main.h" #include "electron/fuses.h" #include "shell/app/electron_main_delegate.h" // NOLINT @@ -15,12 +16,13 @@ #include "shell/app/uv_stdio_fix.h" #include "shell/common/electron_command_line.h" #include "shell/common/electron_constants.h" +#include "uv.h" namespace { -bool IsEnvSet(const char* name) { - char* indicator = getenv(name); - return indicator && indicator[0] != '\0'; +[[nodiscard]] bool IsEnvSet(const base::cstring_view name) { + const char* const indicator = getenv(name.c_str()); + return indicator && *indicator; } } // namespace @@ -28,17 +30,16 @@ bool IsEnvSet(const char* name) { int main(int argc, char* argv[]) { FixStdioStreams(); + argv = uv_setup_args(argc, argv); + base::CommandLine::Init(argc, argv); + electron::ElectronCommandLine::Init(argc, argv); + if (electron::fuses::IsRunAsNodeEnabled() && IsEnvSet(electron::kRunAsNode)) { base::i18n::InitializeICU(); base::AtExitManager atexit_manager; - return electron::NodeMain(argc, argv); + return electron::NodeMain(); } electron::ElectronMainDelegate delegate; - content::ContentMainParams params(&delegate); - electron::ElectronCommandLine::Init(argc, argv); - params.argc = argc; - params.argv = const_cast(argv); - base::CommandLine::Init(params.argc, params.argv); - return content::ContentMain(std::move(params)); + return content::ContentMain(content::ContentMainParams{&delegate}); } diff --git a/shell/app/electron_main_mac.cc b/shell/app/electron_main_mac.cc index 030bac3b86..8fa5e132fd 100644 --- a/shell/app/electron_main_mac.cc +++ b/shell/app/electron_main_mac.cc @@ -5,10 +5,12 @@ #include #include +#include "base/strings/cstring_view.h" #include "electron/fuses.h" #include "electron/mas.h" #include "shell/app/electron_library_main.h" #include "shell/app/uv_stdio_fix.h" +#include "shell/common/electron_constants.h" #if defined(HELPER_EXECUTABLE) && !IS_MAS_BUILD() #include @@ -27,9 +29,9 @@ void abort_report_np(const char* fmt, ...); namespace { -bool IsEnvSet(const char* name) { - char* indicator = getenv(name); - return indicator && indicator[0] != '\0'; +[[nodiscard]] bool IsEnvSet(const base::cstring_view name) { + const char* const indicator = getenv(name.c_str()); + return indicator && *indicator; } #if defined(HELPER_EXECUTABLE) && !IS_MAS_BUILD() @@ -51,8 +53,7 @@ bool IsEnvSet(const char* name) { int main(int argc, char* argv[]) { FixStdioStreams(); - if (electron::fuses::IsRunAsNodeEnabled() && - IsEnvSet("ELECTRON_RUN_AS_NODE")) { + if (electron::fuses::IsRunAsNodeEnabled() && IsEnvSet(electron::kRunAsNode)) { return ElectronInitializeICUandStartNode(argc, argv); } diff --git a/shell/app/electron_main_win.cc b/shell/app/electron_main_win.cc index 6187f98765..4f89cb5a4c 100644 --- a/shell/app/electron_main_win.cc +++ b/shell/app/electron_main_win.cc @@ -21,6 +21,7 @@ #include "base/i18n/icu_util.h" #include "base/memory/raw_ptr_exclusion.h" #include "base/process/launch.h" +#include "base/strings/cstring_view.h" #include "base/strings/utf_string_conversions.h" #include "base/win/dark_mode_support.h" #include "chrome/app/exit_code_watcher_win.h" @@ -45,9 +46,9 @@ namespace { const char kUserDataDir[] = "user-data-dir"; const char kProcessType[] = "type"; -bool IsEnvSet(const char* name) { - size_t required_size; - getenv_s(&required_size, nullptr, 0, name); +[[nodiscard]] bool IsEnvSet(const base::cstring_view name) { + size_t required_size = 0; + getenv_s(&required_size, nullptr, 0, name.c_str()); return required_size != 0; } @@ -126,20 +127,19 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) { // If we are already a fiber then continue normal execution. #endif // defined(ARCH_CPU_32_BITS) - struct Arguments { + { int argc = 0; - RAW_PTR_EXCLUSION wchar_t** argv = - ::CommandLineToArgvW(::GetCommandLineW(), &argc); - - ~Arguments() { LocalFree(argv); } - } arguments; - - if (!arguments.argv) - return -1; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (!argv) + return -1; + base::CommandLine::Init(0, nullptr); // args ignored on Windows + electron::ElectronCommandLine::Init(argc, argv); + LocalFree(argv); + } #ifdef _DEBUG // Don't display assert dialog boxes in CI test runs - static const char kCI[] = "CI"; + static constexpr base::cstring_view kCI = "CI"; if (IsEnvSet(kCI)) { _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); @@ -158,18 +158,12 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) { if (run_as_node || !IsEnvSet("ELECTRON_NO_ATTACH_CONSOLE")) base::RouteStdioToConsole(false); - std::vector argv(arguments.argc); - std::transform(arguments.argv, arguments.argv + arguments.argc, argv.begin(), - [](auto& a) { return _strdup(base::WideToUTF8(a).c_str()); }); if (run_as_node) { base::AtExitManager atexit_manager; base::i18n::InitializeICU(); - auto ret = electron::NodeMain(argv.size(), argv.data()); - std::ranges::for_each(argv, free); - return ret; + return electron::NodeMain(); } - base::CommandLine::Init(argv.size(), argv.data()); const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); @@ -234,6 +228,5 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) { content::ContentMainParams params(&delegate); params.instance = instance; params.sandbox_info = &sandbox_info; - electron::ElectronCommandLine::Init(arguments.argc, arguments.argv); return content::ContentMain(std::move(params)); } diff --git a/shell/app/node_main.cc b/shell/app/node_main.cc index 88e2a8d6e5..8db8d80c14 100644 --- a/shell/app/node_main.cc +++ b/shell/app/node_main.cc @@ -27,6 +27,7 @@ #include "shell/app/uv_task_runner.h" #include "shell/browser/javascript_environment.h" #include "shell/common/api/electron_bindings.h" +#include "shell/common/electron_command_line.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/node_bindings.h" #include "shell/common/node_includes.h" @@ -115,12 +116,8 @@ v8::Local GetParameters(v8::Isolate* isolate) { namespace electron { -int NodeMain(int argc, char* argv[]) { - bool initialized = base::CommandLine::Init(argc, argv); - if (!initialized) { - LOG(ERROR) << "Failed to initialize CommandLine"; - exit(1); - } +int NodeMain() { + DCHECK(base::CommandLine::InitializedForCurrentProcess()); auto os_env = base::Environment::Create(); bool node_options_enabled = electron::fuses::IsNodeOptionsEnabled(); @@ -182,11 +179,8 @@ int NodeMain(int argc, char* argv[]) { // Explicitly register electron's builtin bindings. NodeBindings::RegisterBuiltinBindings(); - // Hack around with the argv pointer. Used for process.title = "blah". - argv = uv_setup_args(argc, argv); - // Parse Node.js cli flags and strip out disallowed options. - std::vector args(argv, argv + argc); + const std::vector args = ElectronCommandLine::AsUtf8(); ExitIfContainsDisallowedFlags(args); std::unique_ptr result = diff --git a/shell/app/node_main.h b/shell/app/node_main.h index 9aaa91b8dc..90cbb8b434 100644 --- a/shell/app/node_main.h +++ b/shell/app/node_main.h @@ -7,7 +7,7 @@ namespace electron { -int NodeMain(int argc, char* argv[]); +int NodeMain(); } // namespace electron diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index f67923ef1a..7bbb66f761 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -285,7 +285,7 @@ void BaseWindow::OnWindowAlwaysOnTopChanged() { Emit("always-on-top-changed", IsAlwaysOnTop()); } -void BaseWindow::OnExecuteAppCommand(const std::string& command_name) { +void BaseWindow::OnExecuteAppCommand(const std::string_view command_name) { Emit("app-command", command_name); } diff --git a/shell/browser/api/electron_api_base_window.h b/shell/browser/api/electron_api_base_window.h index c488588d9c..f852154365 100644 --- a/shell/browser/api/electron_api_base_window.h +++ b/shell/browser/api/electron_api_base_window.h @@ -84,7 +84,7 @@ class BaseWindow : public gin_helper::TrackableObject, void OnWindowEnterHtmlFullScreen() override; void OnWindowLeaveHtmlFullScreen() override; void OnWindowAlwaysOnTopChanged() override; - void OnExecuteAppCommand(const std::string& command_name) override; + void OnExecuteAppCommand(std::string_view command_name) override; void OnTouchBarItemResult(const std::string& item_id, const base::Value::Dict& details) override; void OnNewWindowForTab() override; diff --git a/shell/browser/api/electron_api_net_log.cc b/shell/browser/api/electron_api_net_log.cc index 2ea08604da..394d45b548 100644 --- a/shell/browser/api/electron_api_net_log.cc +++ b/shell/browser/api/electron_api_net_log.cc @@ -166,6 +166,7 @@ void NetLog::StartNetLogAfterCreateFile(net::NetLogCaptureMode capture_mode, std::move(*pending_start_promise_) .RejectWithErrorMessage( base::File::ErrorToString(output_file.error_details())); + pending_start_promise_.reset(); net_log_exporter_.reset(); return; } @@ -178,6 +179,7 @@ void NetLog::StartNetLogAfterCreateFile(net::NetLogCaptureMode capture_mode, void NetLog::NetLogStarted(int32_t error) { DCHECK(pending_start_promise_); ResolvePromiseWithNetError(std::move(*pending_start_promise_), error); + pending_start_promise_.reset(); } void NetLog::OnConnectionError() { @@ -185,6 +187,7 @@ void NetLog::OnConnectionError() { if (pending_start_promise_) { std::move(*pending_start_promise_) .RejectWithErrorMessage("Failed to start net log exporter"); + pending_start_promise_.reset(); } } diff --git a/shell/browser/microtasks_runner.cc b/shell/browser/microtasks_runner.cc index ac8774e710..e50ef5f610 100755 --- a/shell/browser/microtasks_runner.cc +++ b/shell/browser/microtasks_runner.cc @@ -17,7 +17,6 @@ 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_); // In the browser process we follow Node.js microtask policy of kExplicit // and let the MicrotaskRunner which is a task observer for chromium UI thread // scheduler run the microtask checkpoint. This worked fine because Node.js diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index 418783b4cf..625bfb8314 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -665,9 +665,10 @@ void NativeWindow::NotifyWindowAlwaysOnTopChanged() { observer.OnWindowAlwaysOnTopChanged(); } -void NativeWindow::NotifyWindowExecuteAppCommand(const std::string& command) { +void NativeWindow::NotifyWindowExecuteAppCommand( + const std::string_view command_name) { for (NativeWindowObserver& observer : observers_) - observer.OnExecuteAppCommand(command); + observer.OnExecuteAppCommand(command_name); } void NativeWindow::NotifyTouchBarItemInteraction(const std::string& item_id, diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 3fdd983aad..6f521722e5 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -326,7 +326,7 @@ class NativeWindow : public base::SupportsUserData, void NotifyWindowEnterHtmlFullScreen(); void NotifyWindowLeaveHtmlFullScreen(); void NotifyWindowAlwaysOnTopChanged(); - void NotifyWindowExecuteAppCommand(const std::string& command); + void NotifyWindowExecuteAppCommand(std::string_view command_name); void NotifyTouchBarItemInteraction(const std::string& item_id, base::Value::Dict details); void NotifyNewWindowForTab(); diff --git a/shell/browser/native_window_observer.h b/shell/browser/native_window_observer.h index 2c24406375..ba7d3fb14a 100644 --- a/shell/browser/native_window_observer.h +++ b/shell/browser/native_window_observer.h @@ -6,6 +6,7 @@ #define ELECTRON_SHELL_BROWSER_NATIVE_WINDOW_OBSERVER_H_ #include +#include #include "base/observer_list_types.h" #include "base/values.h" @@ -102,7 +103,7 @@ class NativeWindowObserver : public base::CheckedObserver { // Called on Windows when App Commands arrive (WM_APPCOMMAND) // Some commands are implemented on on other platforms as well - virtual void OnExecuteAppCommand(const std::string& command_name) {} + virtual void OnExecuteAppCommand(std::string_view command_name) {} virtual void UpdateWindowControlsOverlay(const gfx::Rect& bounding_rect) {} }; diff --git a/shell/browser/native_window_views_win.cc b/shell/browser/native_window_views_win.cc index 8c4d2c1c75..ce3b50a043 100644 --- a/shell/browser/native_window_views_win.cc +++ b/shell/browser/native_window_views_win.cc @@ -28,7 +28,7 @@ namespace electron { namespace { // Convert Win32 WM_APPCOMMANDS to strings. -const char* AppCommandToString(int command_id) { +constexpr std::string_view AppCommandToString(int command_id) { switch (command_id) { case APPCOMMAND_BROWSER_BACKWARD: return kBrowserBackward; @@ -227,8 +227,8 @@ void NativeWindowViews::Maximize() { } bool NativeWindowViews::ExecuteWindowsCommand(int command_id) { - std::string command = AppCommandToString(command_id); - NotifyWindowExecuteAppCommand(command); + const auto command_name = AppCommandToString(command_id); + NotifyWindowExecuteAppCommand(command_name); return false; } diff --git a/shell/browser/ui/electron_gdk_pixbuf.sigs b/shell/browser/ui/electron_gdk_pixbuf.sigs index c19eb46792..9fcce430fd 100644 --- a/shell/browser/ui/electron_gdk_pixbuf.sigs +++ b/shell/browser/ui/electron_gdk_pixbuf.sigs @@ -1,3 +1,4 @@ -GdkPixbuf* gdk_pixbuf_new(GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height) +GdkPixbuf* gdk_pixbuf_new_from_bytes(GBytes* data, GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height, int rowstride) GdkPixbuf* gdk_pixbuf_scale_simple(const GdkPixbuf* src, int dest_width, int dest_height, GdkInterpType interp_type) -guchar* gdk_pixbuf_get_pixels(const GdkPixbuf* pixbuf) +GdkPixbuf* gdk_pixbuf_new_from_file_at_size(const char* filename, int width, int height, GError** error); +gint gdk_pixbuf_calculate_rowstride(GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height) diff --git a/shell/common/api/electron_api_asar.cc b/shell/common/api/electron_api_asar.cc index 212d7dec31..2661292360 100644 --- a/shell/common/api/electron_api_asar.cc +++ b/shell/common/api/electron_api_asar.cc @@ -18,8 +18,7 @@ class Archive : public node::ObjectWrap { static v8::Local CreateFunctionTemplate( v8::Isolate* isolate) { auto tpl = v8::FunctionTemplate::New(isolate, Archive::New); - tpl->SetClassName( - v8::String::NewFromUtf8(isolate, "Archive").ToLocalChecked()); + tpl->SetClassName(v8::String::NewFromUtf8Literal(isolate, "Archive")); tpl->InstanceTemplate()->SetInternalFieldCount(1); NODE_SET_PROTOTYPE_METHOD(tpl, "getFileInfo", &Archive::GetFileInfo); diff --git a/shell/common/electron_command_line.cc b/shell/common/electron_command_line.cc index bc50114cdb..0f474e91eb 100644 --- a/shell/common/electron_command_line.cc +++ b/shell/common/electron_command_line.cc @@ -5,7 +5,8 @@ #include "shell/common/electron_command_line.h" #include "base/command_line.h" -#include "uv.h" // NOLINT(build/include_directory) +#include "base/containers/to_vector.h" +#include "base/strings/utf_string_conversions.h" namespace electron { @@ -13,17 +14,25 @@ namespace electron { base::CommandLine::StringVector ElectronCommandLine::argv_; // static -void ElectronCommandLine::Init(int argc, base::CommandLine::CharType** argv) { +void ElectronCommandLine::Init(int argc, + base::CommandLine::CharType const* const* argv) { DCHECK(argv_.empty()); - // NOTE: uv_setup_args does nothing on Windows, so we don't need to call it. - // Otherwise we'd have to convert the arguments from UTF16. -#if !BUILDFLAG(IS_WIN) - // Hack around with the argv pointer. Used for process.title = "blah" - argv = uv_setup_args(argc, argv); -#endif + // Safety: as is normal in command lines, argc and argv must correspond + // to one another. Otherwise there will be out-of-bounds accesses. + argv_.assign(argv, UNSAFE_BUFFERS(argv + argc)); +} - argv_.assign(argv, argv + argc); +// static +std::vector ElectronCommandLine::AsUtf8() { + DCHECK(!argv_.empty()); + +#if BUILDFLAG(IS_WIN) + return base::ToVector( + argv_, [](const auto& wstr) { return base::WideToUTF8(wstr); }); +#else + return argv_; +#endif } #if BUILDFLAG(IS_LINUX) diff --git a/shell/common/electron_command_line.h b/shell/common/electron_command_line.h index b6c910016b..f9e713c6c3 100644 --- a/shell/common/electron_command_line.h +++ b/shell/common/electron_command_line.h @@ -20,7 +20,9 @@ class ElectronCommandLine { static const base::CommandLine::StringVector& argv() { return argv_; } - static void Init(int argc, base::CommandLine::CharType** argv); + static std::vector AsUtf8(); + + static void Init(int argc, base::CommandLine::CharType const* const* argv); #if BUILDFLAG(IS_LINUX) // On Linux the command line has to be read from base::CommandLine since diff --git a/shell/common/electron_constants.cc b/shell/common/electron_constants.cc deleted file mode 100644 index a47d40a9fa..0000000000 --- a/shell/common/electron_constants.cc +++ /dev/null @@ -1,25 +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/common/electron_constants.h" - -namespace electron { - -const char kBrowserForward[] = "browser-forward"; -const char kBrowserBackward[] = "browser-backward"; - -const char kDeviceVendorIdKey[] = "vendorId"; -const char kDeviceProductIdKey[] = "productId"; -const char kDeviceSerialNumberKey[] = "serialNumber"; - -const char kRunAsNode[] = "ELECTRON_RUN_AS_NODE"; - -#if BUILDFLAG(ENABLE_PDF_VIEWER) -const char kPDFExtensionPluginName[] = "Chromium PDF Viewer"; -const char kPDFInternalPluginName[] = "Chromium PDF Plugin"; -const base::FilePath::CharType kPdfPluginPath[] = - FILE_PATH_LITERAL("internal-pdf-viewer"); -#endif // BUILDFLAG(ENABLE_PDF_VIEWER) - -} // namespace electron diff --git a/shell/common/electron_constants.h b/shell/common/electron_constants.h index ad2b77aa0d..1050533c72 100644 --- a/shell/common/electron_constants.h +++ b/shell/common/electron_constants.h @@ -5,26 +5,29 @@ #ifndef ELECTRON_SHELL_COMMON_ELECTRON_CONSTANTS_H_ #define ELECTRON_SHELL_COMMON_ELECTRON_CONSTANTS_H_ -#include "base/files/file_path.h" +#include + +#include "base/strings/cstring_view.h" #include "electron/buildflags/buildflags.h" namespace electron { // The app-command in NativeWindow. -extern const char kBrowserForward[]; -extern const char kBrowserBackward[]; +inline constexpr std::string_view kBrowserForward = "browser-forward"; +inline constexpr std::string_view kBrowserBackward = "browser-backward"; // Keys for Device APIs -extern const char kDeviceVendorIdKey[]; -extern const char kDeviceProductIdKey[]; -extern const char kDeviceSerialNumberKey[]; +inline constexpr std::string_view kDeviceVendorIdKey = "vendorId"; +inline constexpr std::string_view kDeviceProductIdKey = "productId"; +inline constexpr std::string_view kDeviceSerialNumberKey = "serialNumber"; -extern const char kRunAsNode[]; +inline constexpr base::cstring_view kRunAsNode = "ELECTRON_RUN_AS_NODE"; #if BUILDFLAG(ENABLE_PDF_VIEWER) -extern const char kPDFExtensionPluginName[]; -extern const char kPDFInternalPluginName[]; -extern const base::FilePath::CharType kPdfPluginPath[]; +inline constexpr std::string_view kPDFExtensionPluginName = + "Chromium PDF Viewer"; +inline constexpr std::string_view kPDFInternalPluginName = + "Chromium PDF Plugin"; #endif // BUILDFLAG(ENABLE_PDF_VIEWER) } // namespace electron diff --git a/shell/common/node_bindings.cc b/shell/common/node_bindings.cc index ef97b468ff..b2b94d89e0 100644 --- a/shell/common/node_bindings.cc +++ b/shell/common/node_bindings.cc @@ -799,15 +799,8 @@ std::shared_ptr NodeBindings::CreateEnvironment( v8::Local context, node::MultiIsolatePlatform* platform, std::optional> on_app_code_ready) { -#if BUILDFLAG(IS_WIN) - auto& electron_args = ElectronCommandLine::argv(); - std::vector args(electron_args.size()); - std::ranges::transform(electron_args, args.begin(), - [](auto& a) { return base::WideToUTF8(a); }); -#else - auto args = ElectronCommandLine::argv(); -#endif - return CreateEnvironment(context, platform, args, {}, on_app_code_ready); + return CreateEnvironment(context, platform, ElectronCommandLine::AsUtf8(), {}, + on_app_code_ready); } void NodeBindings::LoadEnvironment(node::Environment* env) { diff --git a/shell/common/platform_util_mac.mm b/shell/common/platform_util_mac.mm index 3df0ce366f..6e99e336e3 100644 --- a/shell/common/platform_util_mac.mm +++ b/shell/common/platform_util_mac.mm @@ -19,6 +19,7 @@ #include "base/logging.h" #include "base/mac/scoped_aedesc.h" #include "base/strings/sys_string_conversions.h" +#include "base/task/sequenced_task_runner.h" #include "base/task/thread_pool.h" #include "content/public/browser/browser_task_traits.h" #include "electron/mas.h" @@ -147,11 +148,27 @@ void OpenExternal(const GURL& url, return; } - bool success = [[NSWorkspace sharedWorkspace] openURL:ns_url]; - if (success && options.activate) - [NSApp activateIgnoringOtherApps:YES]; + NSWorkspaceOpenConfiguration* configuration = + [NSWorkspaceOpenConfiguration configuration]; + configuration.activates = options.activate; - std::move(callback).Run(success ? "" : "Failed to open URL"); + __block OpenCallback copied_callback = std::move(callback); + scoped_refptr runner = + base::SequencedTaskRunner::GetCurrentDefault(); + + [[NSWorkspace sharedWorkspace] + openURL:ns_url + configuration:configuration + completionHandler:^(NSRunningApplication* _Nullable app, + NSError* _Nullable error) { + if (error) { + runner->PostTask(FROM_HERE, base::BindOnce(std::move(copied_callback), + "Failed to open URL")); + } else { + runner->PostTask(FROM_HERE, + base::BindOnce(std::move(copied_callback), "")); + } + }]; } bool MoveItemToTrashWithError(const base::FilePath& full_path, diff --git a/shell/renderer/api/electron_api_web_frame.cc b/shell/renderer/api/electron_api_web_frame.cc index 5424994f51..35861aa941 100644 --- a/shell/renderer/api/electron_api_web_frame.cc +++ b/shell/renderer/api/electron_api_web_frame.cc @@ -146,7 +146,7 @@ class ScriptExecutionCallback { const v8::Local& result) { v8::MaybeLocal maybe_result; bool success = true; - std::string error_message = + std::string errmsg = "An unknown exception occurred while getting the result of the script"; { v8::TryCatch try_catch(isolate); @@ -164,7 +164,7 @@ class ScriptExecutionCallback { auto message = try_catch.Message(); if (!message.IsEmpty()) { - gin::ConvertFromV8(isolate, message->Get(), &error_message); + gin::ConvertFromV8(isolate, message->Get(), &errmsg); } } } @@ -173,10 +173,11 @@ class ScriptExecutionCallback { if (callback_) std::move(callback_).Run( v8::Undefined(isolate), - v8::Exception::Error( - v8::String::NewFromUtf8(isolate, error_message.c_str()) - .ToLocalChecked())); - promise_.RejectWithErrorMessage(error_message); + v8::Exception::Error(v8::String::NewFromUtf8( + isolate, errmsg.data(), + v8::NewStringType::kNormal, errmsg.size()) + .ToLocalChecked())); + promise_.RejectWithErrorMessage(errmsg); } else { v8::Local context = promise_.GetContext(); v8::Context::Scope context_scope(context); @@ -210,7 +211,7 @@ class ScriptExecutionCallback { promise_.Resolve(value); } } else { - const char error_message[] = + const char errmsg[] = "Script failed to execute, this normally means an error " "was thrown. Check the renderer console for the error."; if (!callback_.is_null()) { @@ -219,13 +220,12 @@ class ScriptExecutionCallback { std::move(callback_).Run( v8::Undefined(isolate), v8::Exception::Error( - v8::String::NewFromUtf8(isolate, error_message) - .ToLocalChecked())); + v8::String::NewFromUtf8Literal(isolate, errmsg))); } - promise_.RejectWithErrorMessage(error_message); + promise_.RejectWithErrorMessage(errmsg); } } else { - const char error_message[] = + const char errmsg[] = "WebFrame was removed before script could run. This normally means " "the underlying frame was destroyed"; if (!callback_.is_null()) { @@ -233,10 +233,10 @@ class ScriptExecutionCallback { v8::Context::Scope context_scope(context); std::move(callback_).Run( v8::Undefined(isolate), - v8::Exception::Error(v8::String::NewFromUtf8(isolate, error_message) - .ToLocalChecked())); + v8::Exception::Error( + v8::String::NewFromUtf8Literal(isolate, errmsg))); } - promise_.RejectWithErrorMessage(error_message); + promise_.RejectWithErrorMessage(errmsg); } delete this; } @@ -716,15 +716,14 @@ class WebFrameRenderer final : public gin::Wrappable, script.Get("url", &url); if (!script.Get("code", &code)) { - const char error_message[] = "Invalid 'code'"; + const char errmsg[] = "Invalid 'code'"; if (!completion_callback.is_null()) { std::move(completion_callback) .Run(v8::Undefined(isolate), v8::Exception::Error( - v8::String::NewFromUtf8(isolate, error_message) - .ToLocalChecked())); + v8::String::NewFromUtf8Literal(isolate, errmsg))); } - promise.RejectWithErrorMessage(error_message); + promise.RejectWithErrorMessage(errmsg); return handle; } diff --git a/shell/renderer/electron_renderer_client.cc b/shell/renderer/electron_renderer_client.cc index bc05a31ce8..6e69d39c6d 100644 --- a/shell/renderer/electron_renderer_client.cc +++ b/shell/renderer/electron_renderer_client.cc @@ -118,15 +118,15 @@ void ElectronRendererClient::DidCreateScriptContext( v8::Isolate* isolate = env->isolate(); v8::Local global = renderer_context->Global(); - std::vector keys = {"fetch", "Response", "FormData", "Request", - "Headers"}; + std::vector keys = {"fetch", "Response", "FormData", + "Request", "Headers", "EventSource"}; for (const auto& key : keys) { v8::MaybeLocal value = - global->Get(renderer_context, gin::StringToV8(isolate, key.c_str())); + global->Get(renderer_context, gin::StringToV8(isolate, key)); if (!value.IsEmpty()) { std::string blink_key = "blink" + key; global - ->Set(renderer_context, gin::StringToV8(isolate, blink_key.c_str()), + ->Set(renderer_context, gin::StringToV8(isolate, blink_key), value.ToLocalChecked()) .Check(); } diff --git a/shell/renderer/electron_sandboxed_renderer_client.cc b/shell/renderer/electron_sandboxed_renderer_client.cc index 8568ec59b8..4d578f51ed 100644 --- a/shell/renderer/electron_sandboxed_renderer_client.cc +++ b/shell/renderer/electron_sandboxed_renderer_client.cc @@ -57,7 +57,7 @@ v8::Local GetBinding(v8::Isolate* isolate, std::string binding_key = gin::V8ToString(isolate, key); gin_helper::Dictionary cache(isolate, GetBindingCache(isolate)); - if (cache.Get(binding_key.c_str(), &exports)) { + if (cache.Get(binding_key, &exports)) { return exports; } @@ -76,7 +76,7 @@ v8::Local GetBinding(v8::Isolate* isolate, DCHECK_NE(mod->nm_context_register_func, nullptr); mod->nm_context_register_func(exports, v8::Null(isolate), isolate->GetCurrentContext(), mod->nm_priv); - cache.Set(binding_key.c_str(), exports); + cache.Set(binding_key, exports); return exports; } diff --git a/shell/renderer/web_worker_observer.cc b/shell/renderer/web_worker_observer.cc index 6d71e1b3c1..295ec163a2 100644 --- a/shell/renderer/web_worker_observer.cc +++ b/shell/renderer/web_worker_observer.cc @@ -71,8 +71,8 @@ void WebWorkerObserver::WorkerScriptReadyForEvaluation( // is loaded. See corresponding change in node/init.ts. v8::Local global = worker_context->Global(); - std::vector keys = {"fetch", "Response", "FormData", "Request", - "Headers"}; + std::vector keys = {"fetch", "Response", "FormData", + "Request", "Headers", "EventSource"}; for (const auto& key : keys) { v8::MaybeLocal value = global->Get(worker_context, gin::StringToV8(isolate, key.c_str())); diff --git a/spec/api-browser-view-spec.ts b/spec/api-browser-view-spec.ts index 93a0f830a0..6cf339dd36 100644 --- a/spec/api-browser-view-spec.ts +++ b/spec/api-browser-view-spec.ts @@ -1,4 +1,4 @@ -import { BrowserView, BrowserWindow, screen, webContents } from 'electron/main'; +import { BrowserView, BrowserWindow, screen, session, webContents } from 'electron/main'; import { expect } from 'chai'; @@ -11,24 +11,29 @@ import { closeWindow } from './lib/window-helpers'; describe('BrowserView module', () => { const fixtures = path.resolve(__dirname, 'fixtures'); + const ses = session.fromPartition(crypto.randomUUID()); let w: BrowserWindow; let view: BrowserView; + const getSessionWebContents = () => + webContents.getAllWebContents().filter(wc => wc.session === ses); + beforeEach(() => { - expect(webContents.getAllWebContents().length).to.equal(0, 'expected no webContents to exist'); + expect(getSessionWebContents().length).to.equal(0, 'expected no webContents to exist'); w = new BrowserWindow({ show: false, width: 400, height: 400, webPreferences: { - backgroundThrottling: false + backgroundThrottling: false, + session: ses } }); }); afterEach(async () => { - if (!w.isDestroyed()) { + if (w && !w.isDestroyed()) { const p = once(w.webContents, 'destroyed'); await closeWindow(w); w = null as any; @@ -42,7 +47,7 @@ describe('BrowserView module', () => { await p; } - expect(webContents.getAllWebContents().length).to.equal(0, 'expected no webContents to exist'); + expect(getSessionWebContents().length).to.equal(0, 'expected no webContents to exist'); }); it('sets the correct class name on the prototype', () => { @@ -50,7 +55,7 @@ describe('BrowserView module', () => { }); it('can be created with an existing webContents', async () => { - const wc = (webContents as typeof ElectronInternal.WebContents).create({ sandbox: true }); + const wc = (webContents as typeof ElectronInternal.WebContents).create({ session: ses, sandbox: true }); await wc.loadURL('about:blank'); view = new BrowserView({ webContents: wc } as any); diff --git a/spec/api-shell-spec.ts b/spec/api-shell-spec.ts index 3883bf79dd..4000ee106c 100644 --- a/spec/api-shell-spec.ts +++ b/spec/api-shell-spec.ts @@ -76,6 +76,21 @@ describe('shell module', () => { requestReceived ]); }); + + ifit(process.platform === 'darwin')('removes focus from the electron window after opening an external link', async () => { + const url = 'http://127.0.0.1'; + const w = new BrowserWindow({ show: true }); + + await once(w, 'focus'); + expect(w.isFocused()).to.be.true(); + + await Promise.all([ + shell.openExternal(url), + once(w, 'blur') as Promise + ]); + + expect(w.isFocused()).to.be.false(); + }); }); describe('shell.trashItem()', () => { diff --git a/spec/api-utility-process-spec.ts b/spec/api-utility-process-spec.ts index e27eae06f8..b17a6f70a8 100644 --- a/spec/api-utility-process-spec.ts +++ b/spec/api-utility-process-spec.ts @@ -144,21 +144,36 @@ describe('utilityProcess module', () => { }); describe('app \'child-process-gone\' event', () => { + const waitForCrash = (name: string) => { + return new Promise((resolve) => { + app.on('child-process-gone', function onCrash (_event, details) { + if (details.name === name) { + app.off('child-process-gone', onCrash); + resolve(details); + } + }); + }); + }; + ifit(!isWindows32Bit)('with default serviceName', async () => { + const name = 'Node Utility Process'; + const crashPromise = waitForCrash(name); utilityProcess.fork(path.join(fixturesPath, 'crash.js')); - const [, details] = await once(app, 'child-process-gone') as [any, Electron.Details]; + const details = await crashPromise; expect(details.type).to.equal('Utility'); expect(details.serviceName).to.equal('node.mojom.NodeService'); - expect(details.name).to.equal('Node Utility Process'); + expect(details.name).to.equal(name); expect(details.reason).to.be.oneOf(['crashed', 'abnormal-exit']); }); ifit(!isWindows32Bit)('with custom serviceName', async () => { - utilityProcess.fork(path.join(fixturesPath, 'crash.js'), [], { serviceName: 'Hello World!' }); - const [, details] = await once(app, 'child-process-gone') as [any, Electron.Details]; + const name = crypto.randomUUID(); + const crashPromise = waitForCrash(name); + utilityProcess.fork(path.join(fixturesPath, 'crash.js'), [], { serviceName: name }); + const details = await crashPromise; expect(details.type).to.equal('Utility'); expect(details.serviceName).to.equal('node.mojom.NodeService'); - expect(details.name).to.equal('Hello World!'); + expect(details.name).to.equal(name); expect(details.reason).to.be.oneOf(['crashed', 'abnormal-exit']); }); }); diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index 1293a4a058..345e9c2d7c 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -1030,6 +1030,43 @@ describe('chromium features', () => { expect(code).to.equal(0); }); + itremote('Worker with nodeIntegrationInWorker has access to EventSource', () => { + const es = new EventSource('https://example.com'); + expect(es).to.have.property('url').that.is.a('string'); + expect(es).to.have.property('readyState').that.is.a('number'); + expect(es).to.have.property('withCredentials').that.is.a('boolean'); + }); + + itremote('Worker with nodeIntegrationInWorker has access to fetch-dependent interfaces', async (fixtures: string) => { + const file = require('node:path').join(fixtures, 'hello.txt'); + expect(() => { + fetch('file://' + file); + }).to.not.throw(); + + expect(() => { + const formData = new FormData(); + formData.append('username', 'Groucho'); + }).not.to.throw(); + + expect(() => { + const request = new Request('https://example.com', { + method: 'POST', + body: JSON.stringify({ foo: 'bar' }) + }); + expect(request.method).to.equal('POST'); + }).not.to.throw(); + + expect(() => { + const response = new Response('Hello, world!'); + expect(response.status).to.equal(200); + }).not.to.throw(); + + expect(() => { + const headers = new Headers(); + headers.append('Content-Type', 'text/xml'); + }).not.to.throw(); + }, [path.join(__dirname, 'fixtures')]); + it('Worker can work', async () => { const w = new BrowserWindow({ show: false }); await w.loadURL(`file://${fixturesPath}/pages/blank.html`); diff --git a/spec/node-spec.ts b/spec/node-spec.ts index d3f68030a2..0d22f3fbfd 100644 --- a/spec/node-spec.ts +++ b/spec/node-spec.ts @@ -162,6 +162,15 @@ describe('node feature', () => { }); }); + describe('EventSource', () => { + itremote('works correctly when nodeIntegration is enabled in the renderer', () => { + const es = new EventSource('https://example.com'); + expect(es).to.have.property('url').that.is.a('string'); + expect(es).to.have.property('readyState').that.is.a('number'); + expect(es).to.have.property('withCredentials').that.is.a('boolean'); + }); + }); + describe('fetch', () => { itremote('works correctly when nodeIntegration is enabled in the renderer', async (fixtures: string) => { const file = require('node:path').join(fixtures, 'hello.txt');