mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1079f3bbfa | ||
|
|
d91adea56f | ||
|
|
b2b584a320 | ||
|
|
1778a26c46 | ||
|
|
9fed98cee5 | ||
|
|
d4d1596d2f | ||
|
|
356bba8060 | ||
|
|
ecbe8ee08a | ||
|
|
8fb777dab0 | ||
|
|
c8af46e054 | ||
|
|
e342216d9e | ||
|
|
90b4003ad5 | ||
|
|
ab3292cf81 | ||
|
|
75ee26902b | ||
|
|
a586dd3045 | ||
|
|
bd1561a5b5 | ||
|
|
bc8ecdf96f | ||
|
|
2515880814 | ||
|
|
4c06de632e | ||
|
|
6b2861d063 | ||
|
|
9692c9ea58 | ||
|
|
ecb6b6c1c1 | ||
|
|
269a5393c0 | ||
|
|
822fb2cd4d | ||
|
|
32fcfe4505 | ||
|
|
933f0d50d1 | ||
|
|
4bd6182e83 | ||
|
|
3e6dd7f771 | ||
|
|
9b89d19b1b |
@@ -2,7 +2,7 @@ version: '3'
|
||||
|
||||
services:
|
||||
buildtools:
|
||||
image: ghcr.io/electron/devcontainer:933c7d6ff6802706875270bec2e3c891cf8add3f
|
||||
image: ghcr.io/electron/devcontainer:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
|
||||
volumes:
|
||||
- ..:/workspaces/gclient/src/electron:cached
|
||||
|
||||
8
.github/actions/build-electron/action.yml
vendored
8
.github/actions/build-electron/action.yml
vendored
@@ -176,10 +176,11 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn create-typescript-definitions
|
||||
node script/yarn.js create-typescript-definitions
|
||||
- name: Publish Electron Dist ${{ inputs.step-suffix }}
|
||||
if: ${{ inputs.is-release == 'true' }}
|
||||
shell: bash
|
||||
id: github-upload
|
||||
run: |
|
||||
rm -rf src/out/Default/obj
|
||||
cd src/electron
|
||||
@@ -190,6 +191,11 @@ runs:
|
||||
echo 'Uploading Electron release distribution to GitHub releases'
|
||||
script/release/uploaders/upload.py --verbose
|
||||
fi
|
||||
- name: Generate artifact attestation
|
||||
if: ${{ inputs.is-release == 'true' }}
|
||||
uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0
|
||||
with:
|
||||
subject-path: ${{ steps.github-upload.outputs.UPLOADED_PATHS }}
|
||||
- name: Generate siso report
|
||||
if: ${{ inputs.target-platform != 'win' && !cancelled() }}
|
||||
shell: bash
|
||||
|
||||
5
.github/actions/checkout/action.yml
vendored
5
.github/actions/checkout/action.yml
vendored
@@ -143,16 +143,17 @@ runs:
|
||||
echo "No changes to patches detected"
|
||||
fi
|
||||
fi
|
||||
- name: Remove patch conflict problem matcher
|
||||
- name: Remove patch conflict problem matchers
|
||||
shell: bash
|
||||
run: |
|
||||
echo "::remove-matcher owner=merge-conflict::"
|
||||
echo "::remove-matcher owner=patch-conflict::"
|
||||
echo "::remove-matcher owner=patch-needs-update::"
|
||||
- name: Upload patches stats
|
||||
if: ${{ inputs.target-platform == 'linux' && github.ref == 'refs/heads/main' }}
|
||||
shell: bash
|
||||
run: |
|
||||
npx node src/electron/script/patches-stats.mjs --upload-stats || true
|
||||
node src/electron/script/patches-stats.mjs --upload-stats || true
|
||||
# delete all .git directories under src/ except for
|
||||
# third_party/angle/ and third_party/dawn/ because of build time generation of files
|
||||
# gen/angle/commit.h depends on third_party/angle/.git/HEAD
|
||||
|
||||
14
.github/actions/generate-types/action.yml
vendored
14
.github/actions/generate-types/action.yml
vendored
@@ -13,12 +13,16 @@ runs:
|
||||
- name: Generating Types for SHA in ${{ inputs.sha-file }}
|
||||
shell: bash
|
||||
run: |
|
||||
git checkout $(cat ${{ inputs.sha-file }})
|
||||
rm -rf node_modules
|
||||
yarn install --frozen-lockfile --ignore-scripts
|
||||
export ELECTRON_DIR=$(pwd)
|
||||
if [ "${{ inputs.sha-file }}" == ".dig-old" ]; then
|
||||
cd /tmp
|
||||
git clone https://github.com/electron/electron.git
|
||||
cd electron
|
||||
fi
|
||||
git checkout $(cat $ELECTRON_DIR/${{ inputs.sha-file }})
|
||||
node script/yarn.js install --immutable
|
||||
echo "#!/usr/bin/env node\nglobal.x=1" > node_modules/typescript/bin/tsc
|
||||
node node_modules/.bin/electron-docs-parser --dir=./ --outDir=./ --moduleVersion=0.0.0-development
|
||||
node node_modules/.bin/electron-typescript-definitions --api=electron-api.json --outDir=artifacts
|
||||
mv artifacts/electron.d.ts artifacts/${{ inputs.filename }}
|
||||
git checkout .
|
||||
mv artifacts/electron.d.ts $ELECTRON_DIR/artifacts/${{ inputs.filename }}
|
||||
working-directory: ./electron
|
||||
|
||||
@@ -15,7 +15,7 @@ runs:
|
||||
git config --global core.preloadindex true
|
||||
git config --global core.longpaths true
|
||||
fi
|
||||
export BUILD_TOOLS_SHA=706147b2376f55078f718576b28129a0457f1795
|
||||
export BUILD_TOOLS_SHA=a0cc95a1884a631559bcca0c948465b725d9295a
|
||||
npm i -g @electron/build-tools
|
||||
# Update depot_tools to ensure python
|
||||
e d update_depot_tools
|
||||
|
||||
14
.github/actions/install-dependencies/action.yml
vendored
14
.github/actions/install-dependencies/action.yml
vendored
@@ -6,7 +6,7 @@ runs:
|
||||
- name: Get yarn cache directory path
|
||||
shell: bash
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(node src/electron/script/yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
run: echo "dir=$(node src/electron/script/yarn.js config get cacheFolder)" >> $GITHUB_OUTPUT
|
||||
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
id: yarn-cache
|
||||
with:
|
||||
@@ -18,4 +18,14 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile --prefer-offline
|
||||
if [ "$TARGET_ARCH" = "x86" ]; then
|
||||
export npm_config_arch="ia32"
|
||||
fi
|
||||
# if running on linux arm skip yarn Builds
|
||||
ARCH=$(uname -m)
|
||||
if [ "$ARCH" = "armv7l" ]; then
|
||||
echo "Skipping yarn build on linux arm"
|
||||
node script/yarn.js install --immutable --mode=skip-build
|
||||
else
|
||||
node script/yarn.js install --immutable
|
||||
fi
|
||||
|
||||
122
.github/copilot-instructions.md
vendored
Normal file
122
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
# Copilot Instructions for Electron
|
||||
|
||||
## Build System
|
||||
|
||||
Electron uses `@electron/build-tools` (`e` CLI). Install with `npm i -g @electron/build-tools`.
|
||||
|
||||
```bash
|
||||
e sync # Fetch sources and apply patches
|
||||
e build # Build Electron (GN + Ninja)
|
||||
e build -k 999 # Build, continuing through errors
|
||||
e start # Run built Electron
|
||||
e start --version # Verify Electron launches
|
||||
e test # Run full test suite
|
||||
e debug # Run in debugger (lldb on macOS, gdb on Linux)
|
||||
```
|
||||
|
||||
### Linting
|
||||
|
||||
```bash
|
||||
npm run lint # Run all linters (JS, C++, Python, GN, docs)
|
||||
npm run lint:js # JavaScript/TypeScript only
|
||||
npm run lint:clang-format # C++ formatting only
|
||||
npm run lint:cpp # C++ linting only
|
||||
npm run lint:docs # Documentation only
|
||||
```
|
||||
|
||||
### Running a Single Test
|
||||
|
||||
```bash
|
||||
npm run test -- -g "pattern" # Run tests matching a regex pattern
|
||||
# Example: npm run test -- -g "ipc"
|
||||
```
|
||||
|
||||
### Running a Single Node.js Test
|
||||
|
||||
```bash
|
||||
node script/node-spec-runner.js parallel/test-crypto-keygen
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
Electron embeds Chromium (rendering) and Node.js (backend) to enable desktop apps with web technologies. The parent directory (`../`) is the Chromium source tree.
|
||||
|
||||
### Process Model
|
||||
|
||||
Electron has two primary process types, mirroring Chromium:
|
||||
|
||||
- **Main process** (`shell/browser/` + `lib/browser/`): Controls app lifecycle, creates windows, system APIs
|
||||
- **Renderer process** (`shell/renderer/` + `lib/renderer/`): Runs web content in BrowserWindows
|
||||
|
||||
### Native ↔ JavaScript Bridge
|
||||
|
||||
Each API is implemented as a C++/JS pair:
|
||||
|
||||
- C++ side: `shell/browser/api/electron_api_{name}.cc/.h` — uses `gin::Wrappable` and `ObjectTemplateBuilder`
|
||||
- JS side: `lib/browser/api/{name}.ts` — exports the module, registered in `lib/browser/api/module-list.ts`
|
||||
- Binding: `NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_{name}, Initialize)` in C++ and registered in `shell/common/node_bindings.cc`
|
||||
- Type declaration: `typings/internal-ambient.d.ts` maps `process._linkedBinding('electron_browser_{name}')`
|
||||
|
||||
### Patches System
|
||||
|
||||
Electron patches upstream dependencies (Chromium, Node.js, V8, etc.) rather than forking them. Patches live in `patches/` organized by target, with `patches/config.json` mapping directories to repos.
|
||||
|
||||
```text
|
||||
patches/{target}/*.patch → [e sync] → target repo commits
|
||||
← [e patches] ←
|
||||
```
|
||||
|
||||
Key rules:
|
||||
|
||||
- Fix existing patches rather than creating new ones
|
||||
- Preserve original authorship in TODO comments — never change `TODO(name)` assignees
|
||||
- Each patch commit message must explain why the patch exists
|
||||
- After modifying patches, run `e patches {target}` to export
|
||||
|
||||
When working on the `roller/chromium/main` branch for Chromium upgrades, use `e sync --3` for 3-way merge conflict resolution.
|
||||
|
||||
## Conventions
|
||||
|
||||
### File Naming
|
||||
|
||||
- JS/TS files: kebab-case (`file-name.ts`)
|
||||
- C++ files: snake_case with `electron_api_` prefix (`electron_api_safe_storage.cc`)
|
||||
- Test files: `api-{module-name}-spec.ts` in `spec/`
|
||||
- Source file lists are maintained in `filenames.gni` (with platform-specific sections)
|
||||
|
||||
### JavaScript/TypeScript
|
||||
|
||||
- Semicolons required (`"semi": ["error", "always"]`)
|
||||
- `const` and `let` only (no `var`)
|
||||
- Arrow functions preferred
|
||||
- Import order enforced: `@electron/internal` → `@electron` → `electron` → external → builtin → relative
|
||||
- API naming: `PascalCase` for classes (`BrowserWindow`), `camelCase` for module APIs (`globalShortcut`)
|
||||
- Prefer getters/setters over jQuery-style `.text([text])` patterns
|
||||
|
||||
### C++
|
||||
|
||||
- Follows Chromium coding style, enforced by `clang-format` and `clang-tidy`
|
||||
- Uses Chromium abstractions (`base::`, `content::`, etc.)
|
||||
- Header guards: `#ifndef ELECTRON_SHELL_BROWSER_API_ELECTRON_API_{NAME}_H_`
|
||||
- Platform-specific files: `_mac.mm`, `_win.cc`, `_linux.cc`
|
||||
|
||||
### Testing
|
||||
|
||||
- Framework: Mocha + Chai + Sinon
|
||||
- Test helpers in `spec/lib/` (e.g., `spec-helpers.ts`, `window-helpers.ts`)
|
||||
- Use `defer()` from spec-helpers for cleanup, `closeAllWindows()` for window teardown
|
||||
- Tests import from `electron/main` or `electron/renderer`
|
||||
|
||||
### Documentation
|
||||
|
||||
- API docs in `docs/api/` as Markdown, parsed by `@electron/docs-parser` to generate `electron.d.ts`
|
||||
- API history tracked via YAML blocks in HTML comments within doc files
|
||||
- Docs must pass `npm run lint:docs`
|
||||
|
||||
### Build Configuration
|
||||
|
||||
- `BUILD.gn`: Main GN build config
|
||||
- `buildflags/buildflags.gni`: Feature flags (PDF viewer, extensions, spellchecker)
|
||||
- `build/args/`: Build argument profiles (`testing.gn`, `release.gn`, `all.gn`)
|
||||
- `DEPS`: Dependency versions and checkout paths
|
||||
- `chromium_src/`: Chromium source file overrides (compiled instead of originals)
|
||||
10
.github/problem-matchers/patch-conflict.json
vendored
10
.github/problem-matchers/patch-conflict.json
vendored
@@ -19,6 +19,16 @@
|
||||
"line": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "patch-needs-update",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^((patches\/.*): needs update)$",
|
||||
"message": 1,
|
||||
"file": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
6
.github/workflows/build-git-cache.yml
vendored
6
.github/workflows/build-git-cache.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
container:
|
||||
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
|
||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
options: --user root
|
||||
volumes:
|
||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
container:
|
||||
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
|
||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
options: --user root --device /dev/fuse --cap-add SYS_ADMIN
|
||||
volumes:
|
||||
- /mnt/win-cache:/mnt/win-cache
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
# This job updates the same git cache as linux, so it needs to run after the linux one.
|
||||
needs: build-git-cache-linux
|
||||
container:
|
||||
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
|
||||
image: ghcr.io/electron/build:a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb
|
||||
options: --user root
|
||||
volumes:
|
||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||
|
||||
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '933c7d6ff6802706875270bec2e3c891cf8add3f'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
required: true
|
||||
skip-macos:
|
||||
type: boolean
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
id: set-output
|
||||
run: |
|
||||
if [ -z "${{ inputs.build-image-sha }}" ]; then
|
||||
echo "build-image-sha=933c7d6ff6802706875270bec2e3c891cf8add3f" >> "$GITHUB_OUTPUT"
|
||||
echo "build-image-sha=a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "build-image-sha=${{ inputs.build-image-sha }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
@@ -333,7 +333,7 @@ jobs:
|
||||
build-runs-on: electron-arc-centralus-linux-amd64-32core
|
||||
test-runs-on: electron-arc-centralus-linux-arm64-4core
|
||||
build-container: '{"image":"ghcr.io/electron/build:${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root","volumes":["/mnt/cross-instance-cache:/mnt/cross-instance-cache"]}'
|
||||
test-container: '{"image":"ghcr.io/electron/test:arm32v7-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init","volumes":["/home/runner/externals:/mnt/runner-externals"]}'
|
||||
test-container: '{"image":"ghcr.io/electron/test:arm32v7-${{ needs.checkout-linux.outputs.build-image-sha }}","options":"--user root --privileged --init --memory=12g","volumes":["/home/runner/externals:/mnt/runner-externals"]}'
|
||||
target-platform: linux
|
||||
target-arch: arm
|
||||
is-release: false
|
||||
|
||||
17
.github/workflows/linux-publish.yml
vendored
17
.github/workflows/linux-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '933c7d6ff6802706875270bec2e3c891cf8add3f'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
required: false
|
||||
@@ -43,9 +43,12 @@ jobs:
|
||||
uses: ./src/electron/.github/actions/checkout
|
||||
|
||||
publish-x64:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-build.yml
|
||||
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
|
||||
permissions:
|
||||
artifact-metadata: write
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: checkout-linux
|
||||
with:
|
||||
environment: production-release
|
||||
@@ -60,9 +63,12 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
publish-arm:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-build.yml
|
||||
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
|
||||
permissions:
|
||||
artifact-metadata: write
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: checkout-linux
|
||||
with:
|
||||
environment: production-release
|
||||
@@ -77,9 +83,12 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
publish-arm64:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-build.yml
|
||||
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
|
||||
permissions:
|
||||
artifact-metadata: write
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: checkout-linux
|
||||
with:
|
||||
environment: production-release
|
||||
|
||||
22
.github/workflows/macos-publish.yml
vendored
22
.github/workflows/macos-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '933c7d6ff6802706875270bec2e3c891cf8add3f'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
required: true
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
@@ -47,9 +47,12 @@ jobs:
|
||||
target-platform: macos
|
||||
|
||||
publish-x64-darwin:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-build.yml
|
||||
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
|
||||
permissions:
|
||||
artifact-metadata: write
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: checkout-macos
|
||||
with:
|
||||
environment: production-release
|
||||
@@ -64,9 +67,12 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
publish-x64-mas:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-build.yml
|
||||
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
|
||||
permissions:
|
||||
artifact-metadata: write
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: checkout-macos
|
||||
with:
|
||||
environment: production-release
|
||||
@@ -81,9 +87,12 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
publish-arm64-darwin:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-build.yml
|
||||
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
|
||||
permissions:
|
||||
artifact-metadata: write
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: checkout-macos
|
||||
with:
|
||||
environment: production-release
|
||||
@@ -98,9 +107,12 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
publish-arm64-mas:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-build.yml
|
||||
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
|
||||
permissions:
|
||||
artifact-metadata: write
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: checkout-macos
|
||||
with:
|
||||
environment: production-release
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
name: Check for Non-Maintainer Dependency Change
|
||||
name: Check for Disallowed Non-Maintainer Change
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'yarn.lock'
|
||||
- 'spec/yarn.lock'
|
||||
- '.github/workflows/**'
|
||||
- '.github/actions/**'
|
||||
- '.yarn/**'
|
||||
- '.yarnrc.yml'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
check-for-non-maintainer-dependency-change:
|
||||
name: Check for non-maintainer dependency change
|
||||
name: Check for disallowed non-maintainer change
|
||||
if: ${{ !contains(fromJSON('["MEMBER", "OWNER"]'), github.event.pull_request.author_association) && github.event.pull_request.user.type != 'Bot' && !github.event.pull_request.draft }}
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -24,7 +28,7 @@ jobs:
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
set -eo pipefail
|
||||
REVIEW_COUNT=$(gh pr view $PR_URL --json reviews | jq '[ .reviews[] | select(.author.login == "github-actions") | select(.body | startswith("<!-- no-dependency-change -->")) ] | length')
|
||||
REVIEW_COUNT=$(gh pr view $PR_URL --json reviews | jq '[ .reviews[] | select(.author.login == "github-actions") | select(.body | startswith("<!-- disallowed-non-maintainer-change -->")) ] | length')
|
||||
if [[ $REVIEW_COUNT -eq 0 ]]; then
|
||||
echo "SHOULD_REVIEW=1" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
@@ -34,4 +38,4 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
printf "<!-- no-dependency-change -->\n\nHello @${{ github.event.pull_request.user.login }}! It looks like this pull request touches one of our dependency files, and per [our contribution policy](https://github.com/electron/electron/blob/main/CONTRIBUTING.md#dependencies-upgrades-policy) we do not accept these types of changes in PRs." | gh pr review $PR_URL -r --body-file=-
|
||||
printf "<!-- disallowed-non-maintainer-change -->\n\nHello @${{ github.event.pull_request.user.login }}! It looks like this pull request touches one of our dependency or CI files, and per [our contribution policy](https://github.com/electron/electron/blob/main/CONTRIBUTING.md#dependencies-upgrades-policy) we do not accept these types of changes in PRs." | gh pr review $PR_URL -r --body-file=-
|
||||
|
||||
@@ -54,12 +54,12 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn create-typescript-definitions
|
||||
node script/yarn tsc -p tsconfig.default_app.json --noEmit
|
||||
node script/yarn.js create-typescript-definitions
|
||||
node script/yarn.js tsc -p tsconfig.default_app.json --noEmit
|
||||
for f in build/webpack/*.js
|
||||
do
|
||||
out="${f:29}"
|
||||
if [ "$out" != "base.js" ]; then
|
||||
node script/yarn webpack --config $f --output-filename=$out --output-path=./.tmp --env mode=development
|
||||
node script/yarn.js webpack --config $f --output-filename=$out --output-path=./.tmp --env mode=development
|
||||
fi
|
||||
done
|
||||
|
||||
12
.github/workflows/pipeline-electron-lint.yml
vendored
12
.github/workflows/pipeline-electron-lint.yml
vendored
@@ -78,11 +78,15 @@ jobs:
|
||||
# but then we would lint its contents (at least gn format), and it doesn't pass it.
|
||||
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
node script/yarn lint
|
||||
node script/yarn.js install --immutable
|
||||
node script/yarn.js lint
|
||||
- name: Run Script Typechecker
|
||||
shell: bash
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn tsc -p tsconfig.script.json
|
||||
|
||||
node script/yarn.js tsc -p tsconfig.script.json
|
||||
- name: Check GHA Workflows
|
||||
shell: bash
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/copy-pipeline-segment-publish.js --check
|
||||
|
||||
237
.github/workflows/pipeline-segment-electron-publish.yml
vendored
Normal file
237
.github/workflows/pipeline-segment-electron-publish.yml
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
# AUTOGENERATED FILE - DO NOT EDIT MANUALLY
|
||||
# ONLY EDIT .github/workflows/pipeline-segment-electron-build.yml
|
||||
|
||||
name: Pipeline Segment - Electron Build
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
environment:
|
||||
description: using the production or testing environment
|
||||
required: false
|
||||
type: string
|
||||
target-platform:
|
||||
type: string
|
||||
description: Platform to run on, can be macos, win or linux
|
||||
required: true
|
||||
target-arch:
|
||||
type: string
|
||||
description: Arch to build for, can be x64, arm64, ia32 or arm
|
||||
required: true
|
||||
target-variant:
|
||||
type: string
|
||||
description: Variant to build for, no effect on non-macOS target platforms. Can
|
||||
be darwin, mas or all.
|
||||
default: all
|
||||
build-runs-on:
|
||||
type: string
|
||||
description: What host to run the build
|
||||
required: true
|
||||
build-container:
|
||||
type: string
|
||||
description: JSON container information for aks runs-on
|
||||
required: false
|
||||
default: '{"image":null}'
|
||||
is-release:
|
||||
description: Whether this build job is a release job
|
||||
required: true
|
||||
type: boolean
|
||||
default: false
|
||||
gn-build-type:
|
||||
description: The gn build type - testing or release
|
||||
required: true
|
||||
type: string
|
||||
default: testing
|
||||
generate-symbols:
|
||||
description: Whether or not to generate symbols
|
||||
required: true
|
||||
type: boolean
|
||||
default: false
|
||||
upload-to-storage:
|
||||
description: Whether or not to upload build artifacts to external storage
|
||||
required: true
|
||||
type: string
|
||||
default: "0"
|
||||
is-asan:
|
||||
description: Building the Address Sanitizer (ASan) Linux build
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
enable-ssh:
|
||||
description: Enable SSH debugging
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
permissions: {}
|
||||
concurrency:
|
||||
group: electron-build-${{ inputs.target-platform }}-${{ inputs.target-arch
|
||||
}}-${{ inputs.target-variant }}-${{ inputs.is-asan }}-${{
|
||||
github.ref_protected == true && github.run_id || github.ref }}
|
||||
cancel-in-progress: ${{ github.ref_protected != true }}
|
||||
env:
|
||||
CHROMIUM_GIT_COOKIE: ${{ secrets.CHROMIUM_GIT_COOKIE }}
|
||||
CHROMIUM_GIT_COOKIE_WINDOWS_STRING: ${{ secrets.CHROMIUM_GIT_COOKIE_WINDOWS_STRING }}
|
||||
DD_API_KEY: ${{ secrets.DD_API_KEY }}
|
||||
ELECTRON_ARTIFACTS_BLOB_STORAGE: ${{ secrets.ELECTRON_ARTIFACTS_BLOB_STORAGE }}
|
||||
ELECTRON_RBE_JWT: ${{ secrets.ELECTRON_RBE_JWT }}
|
||||
SUDOWOODO_EXCHANGE_URL: ${{ secrets.SUDOWOODO_EXCHANGE_URL }}
|
||||
SUDOWOODO_EXCHANGE_TOKEN: ${{ secrets.SUDOWOODO_EXCHANGE_TOKEN }}
|
||||
GCLIENT_EXTRA_ARGS: ${{ inputs.target-platform == 'macos' &&
|
||||
'--custom-var=checkout_mac=True --custom-var=host_os=mac' ||
|
||||
inputs.target-platform == 'win' && '--custom-var=checkout_win=True' ||
|
||||
'--custom-var=checkout_arm=True --custom-var=checkout_arm64=True' }}
|
||||
ELECTRON_OUT_DIR: Default
|
||||
ACTIONS_STEP_DEBUG: ${{ secrets.ACTIONS_STEP_DEBUG }}
|
||||
jobs:
|
||||
build:
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
runs-on: ${{ inputs.build-runs-on }}
|
||||
permissions:
|
||||
artifact-metadata: write
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
container: ${{ fromJSON(inputs.build-container) }}
|
||||
environment: ${{ inputs.environment }}
|
||||
env:
|
||||
TARGET_ARCH: ${{ inputs.target-arch }}
|
||||
TARGET_PLATFORM: ${{ inputs.target-platform }}
|
||||
steps:
|
||||
- name: Create src dir
|
||||
run: |
|
||||
mkdir src
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Setup SSH Debugging
|
||||
if: ${{ inputs.target-platform == 'macos' && (inputs.enable-ssh ||
|
||||
env.ACTIONS_STEP_DEBUG == 'true') }}
|
||||
uses: ./src/electron/.github/actions/ssh-debug
|
||||
with:
|
||||
tunnel: "true"
|
||||
env:
|
||||
CLOUDFLARE_TUNNEL_CERT: ${{ secrets.CLOUDFLARE_TUNNEL_CERT }}
|
||||
CLOUDFLARE_TUNNEL_HOSTNAME: ${{ vars.CLOUDFLARE_TUNNEL_HOSTNAME }}
|
||||
CLOUDFLARE_USER_CA_CERT: ${{ secrets.CLOUDFLARE_USER_CA_CERT }}
|
||||
AUTHORIZED_USERS: ${{ secrets.SSH_DEBUG_AUTHORIZED_USERS }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Free up space (macOS)
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
uses: ./src/electron/.github/actions/free-space-macos
|
||||
- name: Check disk space after freeing up space
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
run: df -h
|
||||
- name: Setup Node.js/npm
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
|
||||
with:
|
||||
node-version: 20.19.x
|
||||
cache: yarn
|
||||
cache-dependency-path: src/electron/yarn.lock
|
||||
- name: Install Dependencies
|
||||
uses: ./src/electron/.github/actions/install-dependencies
|
||||
- name: Install AZCopy
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
run: brew install azcopy
|
||||
- name: Set GN_EXTRA_ARGS for Linux
|
||||
if: ${{ inputs.target-platform == 'linux' }}
|
||||
run: >
|
||||
if [ "${{ inputs.target-arch }}" = "arm" ]; then
|
||||
if [ "${{ inputs.is-release }}" = true ]; then
|
||||
GN_EXTRA_ARGS='target_cpu="arm" build_tflite_with_xnnpack=false symbol_level=1'
|
||||
else
|
||||
GN_EXTRA_ARGS='target_cpu="arm" build_tflite_with_xnnpack=false'
|
||||
fi
|
||||
elif [ "${{ inputs.target-arch }}" = "arm64" ]; then
|
||||
GN_EXTRA_ARGS='target_cpu="arm64" fatal_linker_warnings=false enable_linux_installer=false'
|
||||
elif [ "${{ inputs.is-asan }}" = true ]; then
|
||||
GN_EXTRA_ARGS='is_asan=true'
|
||||
fi
|
||||
|
||||
echo "GN_EXTRA_ARGS=$GN_EXTRA_ARGS" >> $GITHUB_ENV
|
||||
- name: Set Chromium Git Cookie
|
||||
uses: ./src/electron/.github/actions/set-chromium-cookie
|
||||
- name: Install Build Tools
|
||||
uses: ./src/electron/.github/actions/install-build-tools
|
||||
- name: Generate DEPS Hash
|
||||
run: |
|
||||
node src/electron/script/generate-deps-hash.js
|
||||
DEPSHASH=v1-src-cache-$(cat src/electron/.depshash)
|
||||
echo "DEPSHASH=$DEPSHASH" >> $GITHUB_ENV
|
||||
echo "CACHE_PATH=$DEPSHASH.tar" >> $GITHUB_ENV
|
||||
- name: Restore src cache via AZCopy
|
||||
if: ${{ inputs.target-platform != 'linux' }}
|
||||
uses: ./src/electron/.github/actions/restore-cache-azcopy
|
||||
with:
|
||||
target-platform: ${{ inputs.target-platform }}
|
||||
- name: Restore src cache via AKS
|
||||
if: ${{ inputs.target-platform == 'linux' }}
|
||||
uses: ./src/electron/.github/actions/restore-cache-aks
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
path: src/electron
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Fix Sync
|
||||
if: ${{ inputs.target-platform != 'linux' }}
|
||||
uses: ./src/electron/.github/actions/fix-sync
|
||||
with:
|
||||
target-platform: ${{ inputs.target-platform }}
|
||||
env:
|
||||
ELECTRON_DEPOT_TOOLS_DISABLE_LOG: true
|
||||
- name: Init Build Tools
|
||||
run: >
|
||||
e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }}
|
||||
--import ${{ inputs.gn-build-type }} --target-cpu ${{
|
||||
inputs.target-arch }} --remote-build siso
|
||||
- name: Run Electron Only Hooks
|
||||
run: |
|
||||
e d gclient runhooks --spec="solutions=[{'name':'src/electron','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':False},'managed':False}]"
|
||||
- name: Regenerate DEPS Hash
|
||||
run: >
|
||||
(cd src/electron && git checkout .) && node
|
||||
src/electron/script/generate-deps-hash.js
|
||||
|
||||
echo "DEPSHASH=$(cat src/electron/.depshash)" >> $GITHUB_ENV
|
||||
- name: Add CHROMIUM_BUILDTOOLS_PATH to env
|
||||
run: echo "CHROMIUM_BUILDTOOLS_PATH=$(pwd)/src/buildtools" >> $GITHUB_ENV
|
||||
- name: Free up space (macOS)
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
uses: ./src/electron/.github/actions/free-space-macos
|
||||
- name: Build Electron
|
||||
if: ${{ inputs.target-platform != 'macos' || (inputs.target-variant == 'all' ||
|
||||
inputs.target-variant == 'darwin') }}
|
||||
uses: ./src/electron/.github/actions/build-electron
|
||||
with:
|
||||
target-arch: ${{ inputs.target-arch }}
|
||||
target-platform: ${{ inputs.target-platform }}
|
||||
artifact-platform: ${{ inputs.target-platform == 'macos' && 'darwin' ||
|
||||
inputs.target-platform }}
|
||||
is-release: ${{ inputs.is-release }}
|
||||
generate-symbols: ${{ inputs.generate-symbols }}
|
||||
upload-to-storage: ${{ inputs.upload-to-storage }}
|
||||
is-asan: ${{ inputs.is-asan }}
|
||||
- name: Set GN_EXTRA_ARGS for MAS Build
|
||||
if: ${{ inputs.target-platform == 'macos' && (inputs.target-variant == 'all' ||
|
||||
inputs.target-variant == 'mas') }}
|
||||
run: |
|
||||
echo "MAS_BUILD=true" >> $GITHUB_ENV
|
||||
GN_EXTRA_ARGS='is_mas_build=true'
|
||||
echo "GN_EXTRA_ARGS=$GN_EXTRA_ARGS" >> $GITHUB_ENV
|
||||
- name: Build Electron (MAS)
|
||||
if: ${{ inputs.target-platform == 'macos' && (inputs.target-variant == 'all' ||
|
||||
inputs.target-variant == 'mas') }}
|
||||
uses: ./src/electron/.github/actions/build-electron
|
||||
with:
|
||||
target-arch: ${{ inputs.target-arch }}
|
||||
target-platform: ${{ inputs.target-platform }}
|
||||
artifact-platform: mas
|
||||
is-release: ${{ inputs.is-release }}
|
||||
generate-symbols: ${{ inputs.generate-symbols }}
|
||||
upload-to-storage: ${{ inputs.upload-to-storage }}
|
||||
step-suffix: (mas)
|
||||
@@ -183,10 +183,6 @@ jobs:
|
||||
sudo security authorizationdb write com.apple.trust-settings.admin allow
|
||||
cd src/electron
|
||||
./script/codesign/generate-identity.sh
|
||||
- name: Install Datadog CLI
|
||||
run: |
|
||||
cd src/electron
|
||||
node script/yarn global add @datadog/datadog-ci
|
||||
- name: Run Electron Tests
|
||||
shell: bash
|
||||
env:
|
||||
@@ -212,7 +208,7 @@ jobs:
|
||||
export ELECTRON_FORCE_TEST_SUITE_EXIT="true"
|
||||
fi
|
||||
fi
|
||||
node script/yarn test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
|
||||
node script/yarn.js test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
|
||||
else
|
||||
chown :builduser .. && chmod g+w ..
|
||||
chown -R :builduser . && chmod -R g+w .
|
||||
@@ -229,9 +225,14 @@ jobs:
|
||||
export MOCHA_TIMEOUT=180000
|
||||
echo "Piping output to ASAN_SYMBOLIZE ($ASAN_SYMBOLIZE)"
|
||||
cd electron
|
||||
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn test --runners=main --trace-uncaught --enable-logging --files $tests_files | $ASAN_SYMBOLIZE
|
||||
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --runners=main --trace-uncaught --enable-logging --files $tests_files | $ASAN_SYMBOLIZE
|
||||
else
|
||||
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn test --runners=main --trace-uncaught --enable-logging --files $tests_files
|
||||
if [ "${{ inputs.target-arch }}" = "arm" ]; then
|
||||
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --skipYarnInstall --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
|
||||
else
|
||||
runuser -u builduser -- xvfb-run script/actions/run-tests.sh script/yarn.js test --runners=main --enableRerun=3 --trace-uncaught --enable-logging --files $tests_files
|
||||
fi
|
||||
|
||||
fi
|
||||
fi
|
||||
- name: Upload Test results to Datadog
|
||||
@@ -243,9 +244,10 @@ jobs:
|
||||
DD_TAGS: "os.architecture:${{ inputs.target-arch }},os.family:${{ inputs.target-platform }},os.platform:${{ inputs.target-platform }},asan:${{ inputs.is-asan }}"
|
||||
run: |
|
||||
if ! [ -z $DD_API_KEY ] && [ -f src/electron/junit/test-results-main.xml ]; then
|
||||
export DATADOG_PATH=`node src/electron/script/yarn global bin`
|
||||
$DATADOG_PATH/datadog-ci junit upload src/electron/junit/test-results-main.xml
|
||||
fi
|
||||
cd src/electron
|
||||
export DATADOG_PATH=`node script/yarn.js bin datadog-ci`
|
||||
$DATADOG_PATH junit upload junit/test-results-main.xml
|
||||
fi
|
||||
if: always() && !cancelled()
|
||||
- name: Upload Test Artifacts
|
||||
if: always() && !cancelled()
|
||||
|
||||
@@ -138,10 +138,16 @@ jobs:
|
||||
unzip -:o dist.zip
|
||||
- name: Setup Linux for Headless Testing
|
||||
run: sh -e /etc/init.d/xvfb start
|
||||
- name: Add Clang problem matcher
|
||||
shell: bash
|
||||
run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json"
|
||||
- name: Run Nan Tests
|
||||
run: |
|
||||
cd src
|
||||
node electron/script/nan-spec-runner.js
|
||||
- name: Remove Clang problem matcher
|
||||
shell: bash
|
||||
run: echo "::remove-matcher owner=clang::"
|
||||
- name: Wait for active SSH sessions
|
||||
shell: bash
|
||||
if: always() && !cancelled()
|
||||
|
||||
17
.github/workflows/windows-publish.yml
vendored
17
.github/workflows/windows-publish.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
build-image-sha:
|
||||
type: string
|
||||
description: 'SHA for electron/build image'
|
||||
default: '933c7d6ff6802706875270bec2e3c891cf8add3f'
|
||||
default: 'a82b87d7a4f5ff0cab61405f8151ac4cf4942aeb'
|
||||
required: true
|
||||
upload-to-storage:
|
||||
description: 'Uploads to Azure storage'
|
||||
@@ -51,9 +51,12 @@ jobs:
|
||||
target-platform: win
|
||||
|
||||
publish-x64-win:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-build.yml
|
||||
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
|
||||
permissions:
|
||||
artifact-metadata: write
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: checkout-windows
|
||||
with:
|
||||
environment: production-release
|
||||
@@ -67,9 +70,12 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
publish-arm64-win:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-build.yml
|
||||
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
|
||||
permissions:
|
||||
artifact-metadata: write
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: checkout-windows
|
||||
with:
|
||||
environment: production-release
|
||||
@@ -83,9 +89,12 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
publish-x86-win:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-build.yml
|
||||
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
|
||||
permissions:
|
||||
artifact-metadata: write
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: checkout-windows
|
||||
with:
|
||||
environment: production-release
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -53,3 +53,5 @@ ts-gen
|
||||
patches/mtime-cache.json
|
||||
|
||||
spec/fixtures/logo.png
|
||||
|
||||
.yarn/install-state.gz
|
||||
942
.yarn/releases/yarn-4.12.0.cjs
vendored
Executable file
942
.yarn/releases/yarn-4.12.0.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
12
.yarnrc.yml
Normal file
12
.yarnrc.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
enableScripts: false
|
||||
|
||||
nmHoistingLimits: workspaces
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
npmMinimalAgeGate: 10080
|
||||
|
||||
npmPreapprovedPackages:
|
||||
- "@electron/*"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.12.0.cjs
|
||||
7
DEPS
7
DEPS
@@ -4,7 +4,7 @@ vars = {
|
||||
'chromium_version':
|
||||
'140.0.7339.249',
|
||||
'node_version':
|
||||
'v22.21.1',
|
||||
'v22.22.0',
|
||||
'nan_version':
|
||||
'e14bdcd1f72d62bca1d541b66da43130384ec213',
|
||||
'squirrel.mac_version':
|
||||
@@ -30,9 +30,6 @@ vars = {
|
||||
# The path of the sysroots.json file.
|
||||
'sysroots_json_path': 'electron/script/sysroots.json',
|
||||
|
||||
# KEEP IN SYNC WITH utils.js FILE
|
||||
'yarn_version': '1.22.22',
|
||||
|
||||
# To be able to build clean Chromium from sources.
|
||||
'apply_patches': True,
|
||||
|
||||
@@ -155,7 +152,7 @@ hooks = [
|
||||
'action': [
|
||||
'python3',
|
||||
'-c',
|
||||
'import os, subprocess; os.chdir(os.path.join("src", "electron")); subprocess.check_call(["python3", "script/lib/npx.py", "yarn@' + (Var("yarn_version")) + '", "install", "--frozen-lockfile"]);',
|
||||
'import os, subprocess; os.chdir(os.path.join("src", "electron")); subprocess.check_call(["node", ".yarn/releases/yarn-4.12.0.cjs", "install", "--immutable"]);',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,77 +6,17 @@ Follow the guidelines below for building **Electron itself** on Linux, for the p
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* At least 25GB disk space and 8GB RAM.
|
||||
* Python >= 3.9.
|
||||
* [Node.js](https://nodejs.org/download/) >= 22.12.0
|
||||
* [clang](https://clang.llvm.org/get_started.html) 3.4 or later.
|
||||
* Development headers of GTK 3 and libnotify.
|
||||
Due to Electron's dependency on Chromium, prerequisites and dependencies for Electron change over time. [Chromium's documentation on building on Linux](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/linux/build_instructions.md) has up to date information for building Chromium on Linux. This documentation can generally
|
||||
be followed for building Electron on Linux as well.
|
||||
|
||||
On Ubuntu >= 20.04, install the following libraries:
|
||||
|
||||
```sh
|
||||
$ sudo apt-get install build-essential clang libdbus-1-dev libgtk-3-dev \
|
||||
libnotify-dev libasound2-dev libcap-dev \
|
||||
libcups2-dev libxtst-dev \
|
||||
libxss1 libnss3-dev gcc-multilib g++-multilib curl \
|
||||
gperf bison python3-dbusmock openjdk-8-jre
|
||||
```
|
||||
|
||||
On Ubuntu < 20.04, install the following libraries:
|
||||
|
||||
```sh
|
||||
$ sudo apt-get install build-essential clang libdbus-1-dev libgtk-3-dev \
|
||||
libnotify-dev libgnome-keyring-dev \
|
||||
libasound2-dev libcap-dev libcups2-dev libxtst-dev \
|
||||
libxss1 libnss3-dev gcc-multilib g++-multilib curl \
|
||||
gperf bison python-dbusmock openjdk-8-jre
|
||||
```
|
||||
|
||||
On RHEL / CentOS, install the following libraries:
|
||||
|
||||
```sh
|
||||
$ sudo yum install clang dbus-devel gtk3-devel libnotify-devel \
|
||||
libgnome-keyring-devel xorg-x11-server-utils libcap-devel \
|
||||
cups-devel libXtst-devel alsa-lib-devel libXrandr-devel \
|
||||
nss-devel python-dbusmock openjdk-8-jre
|
||||
```
|
||||
|
||||
On Fedora, install the following libraries:
|
||||
|
||||
```sh
|
||||
$ sudo dnf install clang dbus-devel gperf gtk3-devel \
|
||||
libnotify-devel libgnome-keyring-devel libcap-devel \
|
||||
cups-devel libXtst-devel alsa-lib-devel libXrandr-devel \
|
||||
nss-devel python-dbusmock
|
||||
```
|
||||
|
||||
On Arch Linux / Manjaro, install the following libraries:
|
||||
|
||||
```sh
|
||||
$ sudo pacman -Syu base-devel clang libdbus gtk2 libnotify \
|
||||
libgnome-keyring alsa-lib libcap libcups libxtst \
|
||||
libxss nss gcc-multilib curl gperf bison \
|
||||
python2 python-dbusmock jdk8-openjdk
|
||||
```
|
||||
|
||||
Other distributions may offer similar packages for installation via package
|
||||
managers such as pacman. Or one can compile from source code.
|
||||
Additionally, Electron's [Linux dependency installer](https://github.com/electron/build-images/blob/main/tools/install-deps.sh) can be referenced to get the current dependencies that Electron requires in addition to what Chromium installs via [build/install-deps.sh](https://chromium.googlesource.com/chromium/src/+/HEAD/build/install-build-deps.sh).
|
||||
|
||||
### Cross compilation
|
||||
|
||||
If you want to build for an `arm` target you should also install the following
|
||||
dependencies:
|
||||
If you want to build for an `arm` target, you can use Electron's [Linux dependency installer](https://github.com/electron/build-images/blob/main/tools/install-deps.sh) to install the additional dependencies by passing the `--arm argument`:
|
||||
|
||||
```sh
|
||||
$ sudo apt-get install libc6-dev-armhf-cross linux-libc-dev-armhf-cross \
|
||||
g++-arm-linux-gnueabihf
|
||||
```
|
||||
|
||||
Similarly for `arm64`, install the following:
|
||||
|
||||
```sh
|
||||
$ sudo apt-get install libc6-dev-arm64-cross linux-libc-dev-arm64-cross \
|
||||
g++-aarch64-linux-gnu
|
||||
$ sudo install-deps.sh --arm
|
||||
```
|
||||
|
||||
And to cross-compile for `arm` or targets, you should pass the
|
||||
|
||||
@@ -119,7 +119,10 @@ export function fetchWithSession (input: RequestInfo, init: (RequestInit & {bypa
|
||||
p.reject(err);
|
||||
});
|
||||
|
||||
if (!req.body?.pipeTo(Writable.toWeb(r as unknown as Writable)).then(() => r.end())) { r.end(); }
|
||||
// pipeTo expects a WritableStream<Uint8Array>. Node.js' Writable.toWeb returns WritableStream<any>,
|
||||
// which causes a TS structural mismatch.
|
||||
const writable = Writable.toWeb(r as unknown as Writable) as unknown as WritableStream<Uint8Array>;
|
||||
if (!req.body?.pipeTo(writable).then(() => r.end())) { r.end(); }
|
||||
|
||||
return p.promise;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { createReadStream } from 'fs';
|
||||
import { Readable } from 'stream';
|
||||
import { ReadableStream } from 'stream/web';
|
||||
|
||||
import type { ReadableStreamDefaultReader } from 'stream/web';
|
||||
|
||||
// Global protocol APIs.
|
||||
const { registerSchemesAsPrivileged, getStandardSchemes, Protocol } = process._linkedBinding('electron_browser_protocol');
|
||||
|
||||
@@ -12,7 +14,7 @@ const ERR_UNEXPECTED = -9;
|
||||
|
||||
const isBuiltInScheme = (scheme: string) => ['http', 'https', 'file'].includes(scheme);
|
||||
|
||||
function makeStreamFromPipe (pipe: any): ReadableStream {
|
||||
function makeStreamFromPipe (pipe: any): ReadableStream<Uint8Array> {
|
||||
const buf = new Uint8Array(1024 * 1024 /* 1 MB */);
|
||||
return new ReadableStream({
|
||||
async pull (controller) {
|
||||
@@ -38,21 +40,26 @@ function makeStreamFromFileInfo ({
|
||||
filePath: string;
|
||||
offset?: number;
|
||||
length?: number;
|
||||
}): ReadableStream {
|
||||
}): ReadableStream<Uint8Array> {
|
||||
// Node's Readable.toWeb produces a WHATWG ReadableStream whose chunks are Uint8Array.
|
||||
return Readable.toWeb(createReadStream(filePath, {
|
||||
start: offset,
|
||||
end: length >= 0 ? offset + length : undefined
|
||||
}));
|
||||
})) as ReadableStream<Uint8Array>;
|
||||
}
|
||||
|
||||
function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): RequestInit['body'] {
|
||||
if (!uploadData) return null;
|
||||
// Optimization: skip creating a stream if the request is just a single buffer.
|
||||
if (uploadData.length === 1 && (uploadData[0] as any).type === 'rawData') return uploadData[0].bytes;
|
||||
if (uploadData.length === 1 && (uploadData[0] as any).type === 'rawData') {
|
||||
return uploadData[0].bytes as any;
|
||||
}
|
||||
|
||||
const chunks = [...uploadData] as any[]; // TODO: types are wrong
|
||||
let current: ReadableStreamDefaultReader | null = null;
|
||||
return new ReadableStream({
|
||||
const chunks = [...uploadData] as any[]; // TODO: refine ProtocolRequest types
|
||||
// Use Node's web stream types explicitly to avoid DOM lib vs Node lib structural mismatches.
|
||||
// Generic <Uint8Array> ensures reader.read() returns value?: Uint8Array consistent with enqueue.
|
||||
let current: ReadableStreamDefaultReader<Uint8Array> | null = null;
|
||||
return new ReadableStream<Uint8Array>({
|
||||
async pull (controller) {
|
||||
if (current) {
|
||||
const { done, value } = await current.read();
|
||||
@@ -67,7 +74,7 @@ function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): Reque
|
||||
if (!chunks.length) { return controller.close(); }
|
||||
const chunk = chunks.shift()!;
|
||||
if (chunk.type === 'rawData') {
|
||||
controller.enqueue(chunk.bytes);
|
||||
controller.enqueue(chunk.bytes as Uint8Array);
|
||||
} else if (chunk.type === 'file') {
|
||||
current = makeStreamFromFileInfo(chunk).getReader();
|
||||
return this.pull!(controller);
|
||||
|
||||
@@ -40,7 +40,7 @@ process.on('uncaughtException', function (error) {
|
||||
// Emit 'exit' event on quit.
|
||||
const { app } = require('electron');
|
||||
|
||||
app.on('quit', (_event, exitCode) => {
|
||||
app.on('quit', (_event: any, exitCode: number) => {
|
||||
process.emit('exit', exitCode);
|
||||
});
|
||||
|
||||
|
||||
28
package.json
28
package.json
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"name": "@electron-ci/dev-root",
|
||||
"version": "0.0.0-development",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
"@azure/storage-blob": "^12.25.0",
|
||||
"@datadog/datadog-ci": "^4.1.2",
|
||||
"@electron/asar": "^3.2.13",
|
||||
"@electron/docs-parser": "^2.0.0",
|
||||
"@electron/fiddle-core": "^1.3.4",
|
||||
"@electron/github-app-auth": "^2.2.1",
|
||||
"@electron/github-app-auth": "^3.2.0",
|
||||
"@electron/lint-roller": "^3.1.1",
|
||||
"@electron/typescript-definitions": "^9.1.2",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
@@ -24,7 +25,6 @@
|
||||
"buffer": "^6.0.3",
|
||||
"chalk": "^4.1.0",
|
||||
"check-for-leaks": "^1.2.1",
|
||||
"dugite": "^2.7.1",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
@@ -40,6 +40,7 @@
|
||||
"lint-staged": "^16.1.0",
|
||||
"markdownlint-cli2": "^0.18.0",
|
||||
"minimist": "^1.2.8",
|
||||
"node-gyp": "^11.4.2",
|
||||
"null-loader": "^4.0.1",
|
||||
"pre-flight": "^2.0.0",
|
||||
"process": "^0.11.10",
|
||||
@@ -56,7 +57,8 @@
|
||||
"url": "^0.11.4",
|
||||
"webpack": "^5.95.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"wrapper-webpack-plugin": "^2.2.0"
|
||||
"wrapper-webpack-plugin": "^2.2.0",
|
||||
"yaml": "^2.8.1"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -131,9 +133,25 @@
|
||||
"DEPS": [
|
||||
"node script/gen-hunspell-filenames.js",
|
||||
"node script/gen-libc++-filenames.js"
|
||||
],
|
||||
".github/workflows/pipeline-segment-electron-build.yml": [
|
||||
"node script/copy-pipeline-segment-publish.js",
|
||||
"git add .github/workflows/pipeline-segment-electron-publish.yml"
|
||||
]
|
||||
},
|
||||
"resolutions": {
|
||||
"nan": "nodejs/nan#e14bdcd1f72d62bca1d541b66da43130384ec213"
|
||||
"dbus-native/xml2js": "0.5.0",
|
||||
"abstract-socket": "github:deepak1556/node-abstractsocket#928cc591decd12aff7dad96449da8afc29832c19",
|
||||
"minimist@npm:~0.0.1": "0.2.4"
|
||||
},
|
||||
"packageManager": "yarn@4.12.0",
|
||||
"workspaces": [
|
||||
"spec",
|
||||
"spec/fixtures/native-addon/*"
|
||||
],
|
||||
"dependenciesMeta": {
|
||||
"abstract-socket": {
|
||||
"built": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,3 +140,4 @@ band-aid_over_an_issue_with_using_deprecated_nsopenpanel_api.patch
|
||||
inspectorpageagent_provisional_frame_speculative_fix.patch
|
||||
expose_referrerscriptinfo_hostdefinedoptionsindex.patch
|
||||
fix_release_mouse_buttons_on_focus_loss_on_wayland.patch
|
||||
cherry-pick-e045399a1ecb.patch
|
||||
|
||||
133
patches/chromium/cherry-pick-e045399a1ecb.patch
Normal file
133
patches/chromium/cherry-pick-e045399a1ecb.patch
Normal file
@@ -0,0 +1,133 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dominik=20R=C3=B6ttsches?= <drott@chromium.org>
|
||||
Date: Thu, 12 Feb 2026 06:35:36 -0800
|
||||
Subject: Avoid stale iteration in CSSFontFeatureValuesMap
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
To avoid invalid iterator state, take a snapshot of the
|
||||
map when creating the iteration source. This addresses
|
||||
the immediate problem of iterating while modifying.
|
||||
|
||||
Remaining work tracked in https://crbug.com/483936078
|
||||
|
||||
Fixed: 483569511
|
||||
Change-Id: Ie29cfdf7ed94bbe189b44c842a5efce571bb2cee
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7566570
|
||||
Commit-Queue: Dominik Röttsches <drott@chromium.org>
|
||||
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1583927}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/css/css_font_feature_values_map.cc b/third_party/blink/renderer/core/css/css_font_feature_values_map.cc
|
||||
index 24303069e2531afebec29977378ba708051e117d..1862dae14a63769f0fe1fe1cf5f6f880148ce37b 100644
|
||||
--- a/third_party/blink/renderer/core/css/css_font_feature_values_map.cc
|
||||
+++ b/third_party/blink/renderer/core/css/css_font_feature_values_map.cc
|
||||
@@ -13,17 +13,16 @@ class FontFeatureValuesMapIterationSource final
|
||||
: public PairSyncIterable<CSSFontFeatureValuesMap>::IterationSource {
|
||||
public:
|
||||
FontFeatureValuesMapIterationSource(const CSSFontFeatureValuesMap& map,
|
||||
- const FontFeatureAliases* aliases)
|
||||
- : map_(map), aliases_(aliases), iterator_(aliases->begin()) {}
|
||||
+ const FontFeatureAliases aliases)
|
||||
+ : map_(map),
|
||||
+ aliases_(std::move(aliases)),
|
||||
+ iterator_(aliases_.begin()) {}
|
||||
|
||||
bool FetchNextItem(ScriptState* script_state,
|
||||
String& map_key,
|
||||
Vector<uint32_t>& map_value,
|
||||
ExceptionState&) override {
|
||||
- if (!aliases_) {
|
||||
- return false;
|
||||
- }
|
||||
- if (iterator_ == aliases_->end()) {
|
||||
+ if (iterator_ == aliases_.end()) {
|
||||
return false;
|
||||
}
|
||||
map_key = iterator_->key;
|
||||
@@ -38,9 +37,13 @@ class FontFeatureValuesMapIterationSource final
|
||||
}
|
||||
|
||||
private:
|
||||
- // Needs to be kept alive while we're iterating over it.
|
||||
const Member<const CSSFontFeatureValuesMap> map_;
|
||||
- const FontFeatureAliases* aliases_;
|
||||
+ // Create a copy to keep the iterator from becoming invalid if there are
|
||||
+ // modifications to the aliases HashMap while iterating.
|
||||
+ // TODO(https://crbug.com/483936078): Implement live/stable iteration over
|
||||
+ // FontFeatureAliases by changing its storage type, avoiding taking a copy
|
||||
+ // here.
|
||||
+ const FontFeatureAliases aliases_;
|
||||
FontFeatureAliases::const_iterator iterator_;
|
||||
};
|
||||
|
||||
@@ -50,8 +53,8 @@ uint32_t CSSFontFeatureValuesMap::size() const {
|
||||
|
||||
PairSyncIterable<CSSFontFeatureValuesMap>::IterationSource*
|
||||
CSSFontFeatureValuesMap::CreateIterationSource(ScriptState*, ExceptionState&) {
|
||||
- return MakeGarbageCollected<FontFeatureValuesMapIterationSource>(*this,
|
||||
- aliases_);
|
||||
+ return MakeGarbageCollected<FontFeatureValuesMapIterationSource>(
|
||||
+ *this, aliases_ ? *aliases_ : FontFeatureAliases());
|
||||
}
|
||||
|
||||
bool CSSFontFeatureValuesMap::GetMapEntry(ScriptState*,
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/font_feature_values_map_iteration.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/font_feature_values_map_iteration.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..eac7198b0b4a58007cbcc77ad3e9357a1009117c
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/font_feature_values_map_iteration.html
|
||||
@@ -0,0 +1,52 @@
|
||||
+<!DOCTYPE html>
|
||||
+<html>
|
||||
+ <head>
|
||||
+ <title>CSSFontFeatureValuesMap Iteration and Modification</title>
|
||||
+ <link
|
||||
+ rel="help"
|
||||
+ href="https://drafts.csswg.org/css-fonts-4/#om-fontfeaturevalues"
|
||||
+ />
|
||||
+ <meta
|
||||
+ name="assert"
|
||||
+ content="Iteration while modifying CSSFontFeatureValuesMap does not crash."
|
||||
+ />
|
||||
+ <script type="text/javascript" src="/resources/testharness.js"></script>
|
||||
+ <script
|
||||
+ type="text/javascript"
|
||||
+ src="/resources/testharnessreport.js"
|
||||
+ ></script>
|
||||
+ </head>
|
||||
+ <body>
|
||||
+ <style>
|
||||
+ @font-feature-values TestFont {
|
||||
+ @styleset {
|
||||
+ a: 1;
|
||||
+ b: 2;
|
||||
+ c: 3;
|
||||
+ }
|
||||
+ }
|
||||
+ </style>
|
||||
+ <script>
|
||||
+ test(() => {
|
||||
+ const rule = document.styleSheets[0].cssRules[0];
|
||||
+ const map = rule.styleset;
|
||||
+ const iterator = map.entries();
|
||||
+ let count = 0;
|
||||
+
|
||||
+ while (count < 10) {
|
||||
+ const { value: entry, done } = iterator.next();
|
||||
+ if (done) break;
|
||||
+
|
||||
+ const [key, value] = entry;
|
||||
+
|
||||
+ map.delete(key);
|
||||
+ for (let i = 0; i < 100; i++) {
|
||||
+ map.set(`newkey_${count}_${i}`, i);
|
||||
+ }
|
||||
+
|
||||
+ count++;
|
||||
+ }
|
||||
+ }, "Iteration of the CSSFontFeatureValuesMap does not crash.");
|
||||
+ </script>
|
||||
+ </body>
|
||||
+</html>
|
||||
@@ -6,3 +6,4 @@ apply_allcan_read_write.patch
|
||||
fix_support_new_variant_of_namedpropertyhandlerconfiguration_and.patch
|
||||
fix_correct_usages_of_v8_returnvalue_void_set_nonempty_for_new.patch
|
||||
chore_remove_deprecated_functioncallbackinfo_holder.patch
|
||||
chore_add_yarnrc_yml_and_yarn_lock_file_to_use_yarn_v4.patch
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -47,3 +47,6 @@ fix_array_out-of-bounds_read_in_boyer-moore_search.patch
|
||||
chore_add_missing_include_of_iterator.patch
|
||||
src_use_cp_utf8_for_wide_file_names_on_win32.patch
|
||||
fix_ensure_traverseparent_bails_on_resource_path_exit.patch
|
||||
remove_obsolete_noarraybufferzerofillscope.patch
|
||||
src_prepare_for_v8_sandboxing.patch
|
||||
test_correct_conditional_secure_heap_flags_test.patch
|
||||
|
||||
@@ -8,10 +8,10 @@ they use themselves as the entry point. We should try to upstream some form
|
||||
of this.
|
||||
|
||||
diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
|
||||
index d12d21905c4823f45cffeea4423e99e81b1008f5..4987cd3f6c9eefb440bca3f58113df6cd5b410ac 100644
|
||||
index 15443a710ccf53fae333da3b1fbb52a970c658d5..464b34829c1a566836bfca6bbc2b87fcf5e50016 100644
|
||||
--- a/lib/internal/process/pre_execution.js
|
||||
+++ b/lib/internal/process/pre_execution.js
|
||||
@@ -267,12 +267,14 @@ function patchProcessObject(expandArgv1) {
|
||||
@@ -265,12 +265,14 @@ function patchProcessObject(expandArgv1) {
|
||||
// the entry point.
|
||||
if (expandArgv1 && process.argv[1] && process.argv[1][0] !== '-') {
|
||||
// Expand process.argv[1] into a full path.
|
||||
|
||||
@@ -86,10 +86,10 @@ index 0ca643aa74d13f278685d2330b791182b55c15b4..cbcecfba33070b820aca0e2814982160
|
||||
NODE_DEFINE_CONSTANT(target, ETIMEDOUT);
|
||||
#endif
|
||||
diff --git a/src/node_errors.cc b/src/node_errors.cc
|
||||
index ae8553ee2022d60fea4572976b14ba9cd253aa45..4386a1bc5678e351ce084cd2c47202561619b164 100644
|
||||
index 238942d45a136facec55ca5a2534e2dc407137e9..36a21b9523351fe2f225ffe7fca184d737640b62 100644
|
||||
--- a/src/node_errors.cc
|
||||
+++ b/src/node_errors.cc
|
||||
@@ -862,10 +862,6 @@ const char* errno_string(int errorno) {
|
||||
@@ -899,10 +899,6 @@ const char* errno_string(int errorno) {
|
||||
ERRNO_CASE(ENOBUFS);
|
||||
#endif
|
||||
|
||||
@@ -100,7 +100,7 @@ index ae8553ee2022d60fea4572976b14ba9cd253aa45..4386a1bc5678e351ce084cd2c4720256
|
||||
#ifdef ENODEV
|
||||
ERRNO_CASE(ENODEV);
|
||||
#endif
|
||||
@@ -904,14 +900,6 @@ const char* errno_string(int errorno) {
|
||||
@@ -941,14 +937,6 @@ const char* errno_string(int errorno) {
|
||||
ERRNO_CASE(ENOSPC);
|
||||
#endif
|
||||
|
||||
@@ -115,7 +115,7 @@ index ae8553ee2022d60fea4572976b14ba9cd253aa45..4386a1bc5678e351ce084cd2c4720256
|
||||
#ifdef ENOSYS
|
||||
ERRNO_CASE(ENOSYS);
|
||||
#endif
|
||||
@@ -994,10 +982,6 @@ const char* errno_string(int errorno) {
|
||||
@@ -1031,10 +1019,6 @@ const char* errno_string(int errorno) {
|
||||
ERRNO_CASE(ESTALE);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ index fe669d40c31a29334b047b9cfee3067f64ef0a7b..9e5de7bbe574add017cd12ee091304d0
|
||||
|
||||
static CFunction fast_timing_safe_equal(CFunction::Make(FastTimingSafeEqual));
|
||||
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
|
||||
index e39852c8e0392e0a9ae5d4ea58be115416e19233..c94b14741c827a81d69a6f036426a344e563ad72 100644
|
||||
index b9f0c97938203b4652780a7d707c5e83319330b0..8a5b6b57321c2843a965a7e51b2ebed991a1e424 100644
|
||||
--- a/src/node_buffer.cc
|
||||
+++ b/src/node_buffer.cc
|
||||
@@ -44,6 +44,14 @@
|
||||
@@ -74,7 +74,7 @@ index e39852c8e0392e0a9ae5d4ea58be115416e19233..c94b14741c827a81d69a6f036426a344
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::Global;
|
||||
using v8::HandleScope;
|
||||
@@ -584,19 +591,24 @@ void SlowCopy(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -583,19 +590,24 @@ void SlowCopy(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
// Assume caller has properly validated args.
|
||||
uint32_t FastCopy(Local<Value> receiver,
|
||||
@@ -107,7 +107,7 @@ index e39852c8e0392e0a9ae5d4ea58be115416e19233..c94b14741c827a81d69a6f036426a344
|
||||
return to_copy;
|
||||
}
|
||||
|
||||
@@ -865,19 +877,17 @@ void Compare(const FunctionCallbackInfo<Value> &args) {
|
||||
@@ -864,19 +876,17 @@ void Compare(const FunctionCallbackInfo<Value> &args) {
|
||||
}
|
||||
|
||||
int32_t FastCompare(v8::Local<v8::Value>,
|
||||
@@ -135,7 +135,7 @@ index e39852c8e0392e0a9ae5d4ea58be115416e19233..c94b14741c827a81d69a6f036426a344
|
||||
}
|
||||
|
||||
static v8::CFunction fast_compare(v8::CFunction::Make(FastCompare));
|
||||
@@ -1149,14 +1159,13 @@ void SlowIndexOfNumber(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -1148,14 +1158,13 @@ void SlowIndexOfNumber(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
int32_t FastIndexOfNumber(v8::Local<v8::Value>,
|
||||
@@ -153,7 +153,7 @@ index e39852c8e0392e0a9ae5d4ea58be115416e19233..c94b14741c827a81d69a6f036426a344
|
||||
}
|
||||
|
||||
static v8::CFunction fast_index_of_number(
|
||||
@@ -1496,21 +1505,31 @@ void SlowWriteString(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -1510,21 +1519,31 @@ void SlowWriteString(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
template <encoding encoding>
|
||||
uint32_t FastWriteString(Local<Value> receiver,
|
||||
|
||||
@@ -18,10 +18,10 @@ This can be removed when Node.js upgrades to a version of V8 containing CLs
|
||||
from the above issue.
|
||||
|
||||
diff --git a/src/api/environment.cc b/src/api/environment.cc
|
||||
index cb37fa080fc8e8d524cfa2758c4a8c2c5652324d..8e227ddd1be50c046a8cf2895a31d607eb7d31de 100644
|
||||
index fd71ceac65ccef1d2832b45b0b5612877cee22c1..ceac508418f489a8077c1bc85a2feaf85bf60480 100644
|
||||
--- a/src/api/environment.cc
|
||||
+++ b/src/api/environment.cc
|
||||
@@ -316,6 +316,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
|
||||
@@ -308,6 +308,10 @@ Isolate* NewIsolate(Isolate::CreateParams* params,
|
||||
MultiIsolatePlatform* platform,
|
||||
const SnapshotData* snapshot_data,
|
||||
const IsolateSettings& settings) {
|
||||
@@ -32,7 +32,7 @@ index cb37fa080fc8e8d524cfa2758c4a8c2c5652324d..8e227ddd1be50c046a8cf2895a31d607
|
||||
Isolate* isolate = Isolate::Allocate();
|
||||
if (isolate == nullptr) return nullptr;
|
||||
|
||||
@@ -359,9 +363,12 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator,
|
||||
@@ -351,9 +355,12 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator,
|
||||
uv_loop_t* event_loop,
|
||||
MultiIsolatePlatform* platform,
|
||||
const EmbedderSnapshotData* snapshot_data,
|
||||
|
||||
653
patches/node/remove_obsolete_noarraybufferzerofillscope.patch
Normal file
653
patches/node/remove_obsolete_noarraybufferzerofillscope.patch
Normal file
@@ -0,0 +1,653 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Robo <hop2deep@gmail.com>
|
||||
Date: Wed, 21 Jan 2026 09:53:15 +0000
|
||||
Subject: remove obsolete NoArrayBufferZeroFillScope
|
||||
|
||||
Replace the scope in favor of the V8 api added in
|
||||
https://chromium-review.googlesource.com/c/v8/v8/+/5679067
|
||||
|
||||
Ports changes from
|
||||
1) https://github.com/nodejs/node/commit/869ea331f3a8215229290e2e6038956874c382a6
|
||||
2) https://github.com/nodejs/node/commit/ef9dc0857a73610f5de5dc9f37afd0a927c4c17f
|
||||
3) partially from https://github.com/nodejs/node/commit/e0a71517fef4ca83f2d40d2d1600022bc82a7f9f
|
||||
|
||||
This is needed to remove dependency on the zero_fill_field_
|
||||
that is exposed to JS
|
||||
Refs https://github.com/nodejs/node/commit/3cdb1cd437f63dd256ae2ab3b7e9016257326cb4
|
||||
|
||||
diff --git a/src/api/environment.cc b/src/api/environment.cc
|
||||
index ceac508418f489a8077c1bc85a2feaf85bf60480..645c4cbc0fcf9ec004dcb55493104796b0d64de2 100644
|
||||
--- a/src/api/environment.cc
|
||||
+++ b/src/api/environment.cc
|
||||
@@ -107,11 +107,7 @@ MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
|
||||
}
|
||||
|
||||
void* NodeArrayBufferAllocator::Allocate(size_t size) {
|
||||
- void* ret;
|
||||
- if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
|
||||
- ret = allocator_->Allocate(size);
|
||||
- else
|
||||
- ret = allocator_->AllocateUninitialized(size);
|
||||
+ void* ret = allocator_->Allocate(size);
|
||||
if (ret != nullptr) [[likely]] {
|
||||
total_mem_usage_.fetch_add(size, std::memory_order_relaxed);
|
||||
}
|
||||
diff --git a/src/crypto/crypto_cipher.cc b/src/crypto/crypto_cipher.cc
|
||||
index c00d3616e08b00b1e0a3a29b2dbb5278e1e14fcc..8939c5e5085d00b098f66074b9ee033f5be55d08 100644
|
||||
--- a/src/crypto/crypto_cipher.cc
|
||||
+++ b/src/crypto/crypto_cipher.cc
|
||||
@@ -20,6 +20,7 @@ using ncrypto::SSLPointer;
|
||||
using v8::Array;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::Context;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
@@ -774,10 +775,10 @@ CipherBase::UpdateResult CipherBase::Update(
|
||||
return kErrorState;
|
||||
}
|
||||
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data());
|
||||
- *out = ArrayBuffer::NewBackingStore(env()->isolate(), buf_len);
|
||||
- }
|
||||
+ *out = ArrayBuffer::NewBackingStore(
|
||||
+ env()->isolate(),
|
||||
+ buf_len,
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
buffer = {
|
||||
.data = reinterpret_cast<const unsigned char*>(data),
|
||||
@@ -852,11 +853,10 @@ bool CipherBase::Final(std::unique_ptr<BackingStore>* out) {
|
||||
|
||||
const int mode = ctx_.getMode();
|
||||
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data());
|
||||
- *out = ArrayBuffer::NewBackingStore(
|
||||
- env()->isolate(), static_cast<size_t>(ctx_.getBlockSize()));
|
||||
- }
|
||||
+ *out = ArrayBuffer::NewBackingStore(
|
||||
+ env()->isolate(),
|
||||
+ static_cast<size_t>(ctx_.getBlockSize()),
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
if (kind_ == kDecipher &&
|
||||
Cipher::FromCtx(ctx_).isSupportedAuthenticatedMode()) {
|
||||
@@ -972,10 +972,10 @@ bool PublicKeyCipher::Cipher(
|
||||
return false;
|
||||
}
|
||||
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- *out = ArrayBuffer::NewBackingStore(env->isolate(), out_len);
|
||||
- }
|
||||
+ *out = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(),
|
||||
+ out_len,
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
if (EVP_PKEY_cipher(
|
||||
ctx.get(),
|
||||
diff --git a/src/crypto/crypto_common.cc b/src/crypto/crypto_common.cc
|
||||
index b81b9005365272217c77e2b9289bd9f877c0e77c..185b1d8fe657b5db64dc92497ca742d69f7a2764 100644
|
||||
--- a/src/crypto/crypto_common.cc
|
||||
+++ b/src/crypto/crypto_common.cc
|
||||
@@ -37,6 +37,7 @@ using ncrypto::X509Pointer;
|
||||
using ncrypto::X509View;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::Context;
|
||||
using v8::EscapableHandleScope;
|
||||
using v8::Integer;
|
||||
@@ -307,11 +308,10 @@ MaybeLocal<Object> ECPointToBuffer(Environment* env,
|
||||
return MaybeLocal<Object>();
|
||||
}
|
||||
|
||||
- std::unique_ptr<BackingStore> bs;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(env->isolate(), len);
|
||||
- }
|
||||
+ std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(),
|
||||
+ len,
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
len = EC_POINT_point2oct(group,
|
||||
point,
|
||||
diff --git a/src/crypto/crypto_ec.cc b/src/crypto/crypto_ec.cc
|
||||
index 2a3107dbbf5c0dfe70c2e105338d186e5620ddbf..f36c84a77313bd57d0a7a902d1a2529636ca1422 100644
|
||||
--- a/src/crypto/crypto_ec.cc
|
||||
+++ b/src/crypto/crypto_ec.cc
|
||||
@@ -29,6 +29,7 @@ using ncrypto::MarkPopErrorOnReturn;
|
||||
using v8::Array;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::Context;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
@@ -201,14 +202,10 @@ void ECDH::ComputeSecret(const FunctionCallbackInfo<Value>& args) {
|
||||
return;
|
||||
}
|
||||
|
||||
- std::unique_ptr<BackingStore> bs;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- // NOTE: field_size is in bits
|
||||
- int field_size = EC_GROUP_get_degree(ecdh->group_);
|
||||
- size_t out_len = (field_size + 7) / 8;
|
||||
- bs = ArrayBuffer::NewBackingStore(env->isolate(), out_len);
|
||||
- }
|
||||
+ int field_size = EC_GROUP_get_degree(ecdh->group_);
|
||||
+ size_t out_len = (field_size + 7) / 8;
|
||||
+ auto bs = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(), out_len, BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
if (!ECDH_compute_key(
|
||||
bs->Data(), bs->ByteLength(), pub, ecdh->key_.get(), nullptr))
|
||||
@@ -257,12 +254,10 @@ void ECDH::GetPrivateKey(const FunctionCallbackInfo<Value>& args) {
|
||||
return THROW_ERR_CRYPTO_OPERATION_FAILED(env,
|
||||
"Failed to get ECDH private key");
|
||||
|
||||
- std::unique_ptr<BackingStore> bs;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(env->isolate(),
|
||||
- BignumPointer::GetByteCount(b));
|
||||
- }
|
||||
+ auto bs = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(),
|
||||
+ BignumPointer::GetByteCount(b),
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
CHECK_EQ(bs->ByteLength(),
|
||||
BignumPointer::EncodePaddedInto(
|
||||
b, static_cast<unsigned char*>(bs->Data()), bs->ByteLength()));
|
||||
diff --git a/src/crypto/crypto_rsa.cc b/src/crypto/crypto_rsa.cc
|
||||
index 1f2fccce6ed8f14525557644e0bdd130eedf3337..1099a8f89bb53083f01942ee14fff453d8cdc0af 100644
|
||||
--- a/src/crypto/crypto_rsa.cc
|
||||
+++ b/src/crypto/crypto_rsa.cc
|
||||
@@ -20,6 +20,7 @@ using ncrypto::EVPKeyPointer;
|
||||
using ncrypto::RSAPointer;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::Int32;
|
||||
using v8::JustVoid;
|
||||
@@ -535,12 +536,10 @@ Maybe<void> GetRsaKeyDetail(Environment* env,
|
||||
return Nothing<void>();
|
||||
}
|
||||
|
||||
- std::unique_ptr<BackingStore> public_exponent;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- public_exponent = ArrayBuffer::NewBackingStore(
|
||||
- env->isolate(), BignumPointer::GetByteCount(e));
|
||||
- }
|
||||
+ auto public_exponent = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(),
|
||||
+ BignumPointer::GetByteCount(e),
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
CHECK_EQ(BignumPointer::EncodePaddedInto(
|
||||
e,
|
||||
static_cast<unsigned char*>(public_exponent->Data()),
|
||||
diff --git a/src/crypto/crypto_sig.cc b/src/crypto/crypto_sig.cc
|
||||
index 2f6e683e3497d4315259773d09443e5215bff28f..c33e93c34ef32c18e6de6bc03698ed6b851b4aa3 100644
|
||||
--- a/src/crypto/crypto_sig.cc
|
||||
+++ b/src/crypto/crypto_sig.cc
|
||||
@@ -21,6 +21,7 @@ using ncrypto::EVPKeyPointer;
|
||||
using ncrypto::EVPMDCtxPointer;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::Boolean;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::FunctionTemplate;
|
||||
@@ -92,11 +93,8 @@ std::unique_ptr<BackingStore> Node_SignFinal(Environment* env,
|
||||
return nullptr;
|
||||
|
||||
size_t sig_len = pkey.size();
|
||||
- std::unique_ptr<BackingStore> sig;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- sig = ArrayBuffer::NewBackingStore(env->isolate(), sig_len);
|
||||
- }
|
||||
+ auto sig = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(), sig_len, BackingStoreInitializationMode::kUninitialized);
|
||||
EVPKeyCtxPointer pkctx = pkey.newCtx();
|
||||
if (pkctx && EVP_PKEY_sign_init(pkctx.get()) > 0 &&
|
||||
ApplyRSAOptions(pkey, pkctx.get(), padding, pss_salt_len) &&
|
||||
@@ -168,11 +166,9 @@ std::unique_ptr<BackingStore> ConvertSignatureToP1363(
|
||||
if (n == kNoDsaSignature)
|
||||
return std::move(signature);
|
||||
|
||||
- std::unique_ptr<BackingStore> buf;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- buf = ArrayBuffer::NewBackingStore(env->isolate(), 2 * n);
|
||||
- }
|
||||
+ auto buf = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(), 2 * n, BackingStoreInitializationMode::kUninitialized);
|
||||
+
|
||||
if (!ExtractP1363(static_cast<unsigned char*>(signature->Data()),
|
||||
static_cast<unsigned char*>(buf->Data()),
|
||||
signature->ByteLength(), n))
|
||||
diff --git a/src/crypto/crypto_tls.cc b/src/crypto/crypto_tls.cc
|
||||
index a80685790bd29102d99929ff81f866e0aee370f1..24336b86f6f25533a7b668e9f9806a5635e3a398 100644
|
||||
--- a/src/crypto/crypto_tls.cc
|
||||
+++ b/src/crypto/crypto_tls.cc
|
||||
@@ -46,6 +46,7 @@ using v8::Array;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::ArrayBufferView;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::Boolean;
|
||||
using v8::Context;
|
||||
using v8::DontDelete;
|
||||
@@ -1087,10 +1088,10 @@ int TLSWrap::DoWrite(WriteWrap* w,
|
||||
// and copying it when it could just be used.
|
||||
|
||||
if (nonempty_count != 1) {
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(env()->isolate(), length);
|
||||
- }
|
||||
+ bs = ArrayBuffer::NewBackingStore(
|
||||
+ env()->isolate(),
|
||||
+ length,
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
size_t offset = 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
memcpy(static_cast<char*>(bs->Data()) + offset,
|
||||
@@ -1107,8 +1108,10 @@ int TLSWrap::DoWrite(WriteWrap* w,
|
||||
written = SSL_write(ssl_.get(), buf->base, buf->len);
|
||||
|
||||
if (written == -1) {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(env()->isolate(), length);
|
||||
+ bs = ArrayBuffer::NewBackingStore(
|
||||
+ env()->isolate(),
|
||||
+ length,
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
memcpy(bs->Data(), buf->base, buf->len);
|
||||
}
|
||||
}
|
||||
@@ -1746,11 +1749,8 @@ void TLSWrap::GetFinished(const FunctionCallbackInfo<Value>& args) {
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
- std::unique_ptr<BackingStore> bs;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(env->isolate(), len);
|
||||
- }
|
||||
+ auto bs = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(), len, BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
CHECK_EQ(bs->ByteLength(),
|
||||
SSL_get_finished(w->ssl_.get(), bs->Data(), bs->ByteLength()));
|
||||
@@ -1777,11 +1777,8 @@ void TLSWrap::GetPeerFinished(const FunctionCallbackInfo<Value>& args) {
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
- std::unique_ptr<BackingStore> bs;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(env->isolate(), len);
|
||||
- }
|
||||
+ auto bs = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(), len, BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
CHECK_EQ(bs->ByteLength(),
|
||||
SSL_get_peer_finished(w->ssl_.get(), bs->Data(), bs->ByteLength()));
|
||||
@@ -1806,11 +1803,8 @@ void TLSWrap::GetSession(const FunctionCallbackInfo<Value>& args) {
|
||||
if (slen <= 0)
|
||||
return; // Invalid or malformed session.
|
||||
|
||||
- std::unique_ptr<BackingStore> bs;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(env->isolate(), slen);
|
||||
- }
|
||||
+ auto bs = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(), slen, BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
unsigned char* p = static_cast<unsigned char*>(bs->Data());
|
||||
CHECK_LT(0, i2d_SSL_SESSION(sess, &p));
|
||||
@@ -1993,11 +1987,8 @@ void TLSWrap::ExportKeyingMaterial(const FunctionCallbackInfo<Value>& args) {
|
||||
uint32_t olen = args[0].As<Uint32>()->Value();
|
||||
Utf8Value label(env->isolate(), args[1]);
|
||||
|
||||
- std::unique_ptr<BackingStore> bs;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(env->isolate(), olen);
|
||||
- }
|
||||
+ auto bs = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(), olen, BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
ByteSource context;
|
||||
bool use_context = !args[2]->IsUndefined();
|
||||
diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc
|
||||
index eb6dad44a49d997097c8fb5009eeb60a7305da27..0a1a3c694f3f544ada62235ade6a03a9deadfede 100644
|
||||
--- a/src/crypto/crypto_x509.cc
|
||||
+++ b/src/crypto/crypto_x509.cc
|
||||
@@ -28,6 +28,7 @@ using v8::Array;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::ArrayBufferView;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::Boolean;
|
||||
using v8::Context;
|
||||
using v8::Date;
|
||||
@@ -683,11 +684,8 @@ MaybeLocal<Object> GetPubKey(Environment* env, OSSL3_CONST RSA* rsa) {
|
||||
int size = i2d_RSA_PUBKEY(rsa, nullptr);
|
||||
CHECK_GE(size, 0);
|
||||
|
||||
- std::unique_ptr<BackingStore> bs;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(env->isolate(), size);
|
||||
- }
|
||||
+ auto bs = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(), size, BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
unsigned char* serialized = reinterpret_cast<unsigned char*>(bs->Data());
|
||||
CHECK_GE(i2d_RSA_PUBKEY(rsa, &serialized), 0);
|
||||
diff --git a/src/encoding_binding.cc b/src/encoding_binding.cc
|
||||
index 31ed995714bb99ab534f26ba9ebc6051c258a1c9..9bdb2a660364c66f3f141b505225dcf35c5bbc65 100644
|
||||
--- a/src/encoding_binding.cc
|
||||
+++ b/src/encoding_binding.cc
|
||||
@@ -15,6 +15,7 @@ namespace encoding_binding {
|
||||
|
||||
using v8::ArrayBuffer;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::Context;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::Isolate;
|
||||
@@ -123,9 +124,8 @@ void BindingData::EncodeUtf8String(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
Local<ArrayBuffer> ab;
|
||||
{
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- std::unique_ptr<BackingStore> bs =
|
||||
- ArrayBuffer::NewBackingStore(isolate, length);
|
||||
+ std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
|
||||
+ isolate, length, BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
CHECK(bs);
|
||||
|
||||
diff --git a/src/env-inl.h b/src/env-inl.h
|
||||
index 98e1e1e75bae94038bba0049447ab48b0acfb8cc..fe395bf89f9c1e5bb2dabc8fceda7b9b2b877415 100644
|
||||
--- a/src/env-inl.h
|
||||
+++ b/src/env-inl.h
|
||||
@@ -44,16 +44,6 @@
|
||||
|
||||
namespace node {
|
||||
|
||||
-NoArrayBufferZeroFillScope::NoArrayBufferZeroFillScope(
|
||||
- IsolateData* isolate_data)
|
||||
- : node_allocator_(isolate_data->node_allocator()) {
|
||||
- if (node_allocator_ != nullptr) node_allocator_->zero_fill_field()[0] = 0;
|
||||
-}
|
||||
-
|
||||
-NoArrayBufferZeroFillScope::~NoArrayBufferZeroFillScope() {
|
||||
- if (node_allocator_ != nullptr) node_allocator_->zero_fill_field()[0] = 1;
|
||||
-}
|
||||
-
|
||||
inline v8::Isolate* IsolateData::isolate() const {
|
||||
return isolate_;
|
||||
}
|
||||
diff --git a/src/env.cc b/src/env.cc
|
||||
index 5fa667382bc957aee800d612f78b18c37a58c67f..49a85e9a23e50f201d0b93d8b205e104c0f0fe2c 100644
|
||||
--- a/src/env.cc
|
||||
+++ b/src/env.cc
|
||||
@@ -39,6 +39,9 @@ namespace node {
|
||||
|
||||
using errors::TryCatchScope;
|
||||
using v8::Array;
|
||||
+using v8::ArrayBuffer;
|
||||
+using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::Boolean;
|
||||
using v8::Context;
|
||||
using v8::CppHeap;
|
||||
@@ -724,9 +727,10 @@ void Environment::add_refs(int64_t diff) {
|
||||
}
|
||||
|
||||
uv_buf_t Environment::allocate_managed_buffer(const size_t suggested_size) {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(isolate_data());
|
||||
- std::unique_ptr<v8::BackingStore> bs =
|
||||
- v8::ArrayBuffer::NewBackingStore(isolate(), suggested_size);
|
||||
+ std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
|
||||
+ isolate(),
|
||||
+ suggested_size,
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
uv_buf_t buf = uv_buf_init(static_cast<char*>(bs->Data()), bs->ByteLength());
|
||||
released_allocated_buffers_.emplace(buf.base, std::move(bs));
|
||||
return buf;
|
||||
diff --git a/src/env.h b/src/env.h
|
||||
index c346e3a9c827993036438685d758a734f9ce8c05..28c8df87c8e2f06e2ed8c554260bfdedb860bb4a 100644
|
||||
--- a/src/env.h
|
||||
+++ b/src/env.h
|
||||
@@ -114,19 +114,6 @@ class ModuleWrap;
|
||||
class Environment;
|
||||
class Realm;
|
||||
|
||||
-// Disables zero-filling for ArrayBuffer allocations in this scope. This is
|
||||
-// similar to how we implement Buffer.allocUnsafe() in JS land.
|
||||
-class NoArrayBufferZeroFillScope {
|
||||
- public:
|
||||
- inline explicit NoArrayBufferZeroFillScope(IsolateData* isolate_data);
|
||||
- inline ~NoArrayBufferZeroFillScope();
|
||||
-
|
||||
- private:
|
||||
- NodeArrayBufferAllocator* node_allocator_;
|
||||
-
|
||||
- friend class Environment;
|
||||
-};
|
||||
-
|
||||
struct IsolateDataSerializeInfo {
|
||||
std::vector<SnapshotIndex> primitive_values;
|
||||
std::vector<PropInfo> template_values;
|
||||
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
|
||||
index 8a5b6b57321c2843a965a7e51b2ebed991a1e424..d7bd35a1025487d7a810b14cd5d3f2ba74cc2378 100644
|
||||
--- a/src/node_buffer.cc
|
||||
+++ b/src/node_buffer.cc
|
||||
@@ -66,6 +66,7 @@ namespace Buffer {
|
||||
using v8::ArrayBuffer;
|
||||
using v8::ArrayBufferView;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::Context;
|
||||
using v8::EscapableHandleScope;
|
||||
using v8::FunctionCallbackInfo;
|
||||
@@ -375,9 +376,8 @@ MaybeLocal<Object> New(Environment* env, size_t length) {
|
||||
|
||||
Local<ArrayBuffer> ab;
|
||||
{
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- std::unique_ptr<BackingStore> bs =
|
||||
- ArrayBuffer::NewBackingStore(isolate, length);
|
||||
+ std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
|
||||
+ isolate, length, BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
CHECK(bs);
|
||||
|
||||
@@ -416,18 +416,14 @@ MaybeLocal<Object> Copy(Environment* env, const char* data, size_t length) {
|
||||
return Local<Object>();
|
||||
}
|
||||
|
||||
- Local<ArrayBuffer> ab;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- std::unique_ptr<BackingStore> bs =
|
||||
- ArrayBuffer::NewBackingStore(isolate, length);
|
||||
+ std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
|
||||
+ isolate, length, BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
- CHECK(bs);
|
||||
+ CHECK(bs);
|
||||
|
||||
- memcpy(bs->Data(), data, length);
|
||||
+ memcpy(bs->Data(), data, length);
|
||||
|
||||
- ab = ArrayBuffer::New(isolate, std::move(bs));
|
||||
- }
|
||||
+ Local<ArrayBuffer> ab = ArrayBuffer::New(isolate, std::move(bs));
|
||||
|
||||
MaybeLocal<Object> obj =
|
||||
New(env, ab, 0, ab->ByteLength())
|
||||
@@ -1439,14 +1435,16 @@ void CreateUnsafeArrayBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
Local<ArrayBuffer> buf;
|
||||
|
||||
- NodeArrayBufferAllocator* allocator = env->isolate_data()->node_allocator();
|
||||
// 0-length, or zero-fill flag is set, or building snapshot
|
||||
if (size == 0 || per_process::cli_options->zero_fill_all_buffers ||
|
||||
- allocator == nullptr) {
|
||||
+ env->isolate_data()->is_building_snapshot()) {
|
||||
buf = ArrayBuffer::New(isolate, size);
|
||||
} else {
|
||||
- std::unique_ptr<BackingStore> store =
|
||||
- ArrayBuffer::NewBackingStore(isolate, size);
|
||||
+ std::unique_ptr<BackingStore> store = ArrayBuffer::NewBackingStore(
|
||||
+ isolate,
|
||||
+ size,
|
||||
+ BackingStoreInitializationMode::kUninitialized,
|
||||
+ v8::BackingStoreOnFailureMode::kReturnNull);
|
||||
if (!store) {
|
||||
return env->ThrowRangeError("Array buffer allocation failed");
|
||||
}
|
||||
diff --git a/src/node_http2.cc b/src/node_http2.cc
|
||||
index 8237c9b7d325dd925ae8798d7795fcd94eeb13d0..a22cf6c4e33e5cf2d3168ce03dc35af8a9584af7 100644
|
||||
--- a/src/node_http2.cc
|
||||
+++ b/src/node_http2.cc
|
||||
@@ -27,6 +27,7 @@ using v8::Array;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::ArrayBufferView;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::Boolean;
|
||||
using v8::Context;
|
||||
using v8::EscapableHandleScope;
|
||||
@@ -298,11 +299,10 @@ Local<Value> Http2Settings::Pack(
|
||||
size_t count,
|
||||
const nghttp2_settings_entry* entries) {
|
||||
EscapableHandleScope scope(env->isolate());
|
||||
- std::unique_ptr<BackingStore> bs;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(env->isolate(), count * 6);
|
||||
- }
|
||||
+ std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(),
|
||||
+ count * 6,
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
if (nghttp2_pack_settings_payload(static_cast<uint8_t*>(bs->Data()),
|
||||
bs->ByteLength(),
|
||||
entries,
|
||||
@@ -468,13 +468,11 @@ Origins::Origins(
|
||||
return;
|
||||
}
|
||||
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs_ = ArrayBuffer::NewBackingStore(env->isolate(),
|
||||
- alignof(nghttp2_origin_entry) - 1 +
|
||||
- count_ * sizeof(nghttp2_origin_entry) +
|
||||
- origin_string_len);
|
||||
- }
|
||||
+ bs_ = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(),
|
||||
+ alignof(nghttp2_origin_entry) - 1 +
|
||||
+ count_ * sizeof(nghttp2_origin_entry) + origin_string_len,
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
|
||||
// Make sure the start address is aligned appropriately for an nghttp2_nv*.
|
||||
char* start = nbytes::AlignUp(static_cast<char*>(bs_->Data()),
|
||||
@@ -2120,12 +2118,10 @@ void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) {
|
||||
// happen, we concatenate the data we received with the already-stored
|
||||
// pending input data, slicing off the already processed part.
|
||||
size_t pending_len = stream_buf_.len - stream_buf_offset_;
|
||||
- std::unique_ptr<BackingStore> new_bs;
|
||||
- {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env()->isolate_data());
|
||||
- new_bs = ArrayBuffer::NewBackingStore(env()->isolate(),
|
||||
- pending_len + nread);
|
||||
- }
|
||||
+ std::unique_ptr<BackingStore> new_bs = ArrayBuffer::NewBackingStore(
|
||||
+ env()->isolate(),
|
||||
+ pending_len + nread,
|
||||
+ BackingStoreInitializationMode::kUninitialized);
|
||||
memcpy(static_cast<char*>(new_bs->Data()),
|
||||
stream_buf_.base + stream_buf_offset_,
|
||||
pending_len);
|
||||
diff --git a/src/node_internals.h b/src/node_internals.h
|
||||
index 12ea72b61b0a5e194207bb369dfed4b8667107cb..18844e18a32d6b07e62481138fa2342765643484 100644
|
||||
--- a/src/node_internals.h
|
||||
+++ b/src/node_internals.h
|
||||
@@ -121,8 +121,6 @@ v8::MaybeLocal<v8::Object> InitializePrivateSymbols(
|
||||
|
||||
class NodeArrayBufferAllocator : public ArrayBufferAllocator {
|
||||
public:
|
||||
- inline uint32_t* zero_fill_field() { return &zero_fill_field_; }
|
||||
-
|
||||
void* Allocate(size_t size) override; // Defined in src/node.cc
|
||||
void* AllocateUninitialized(size_t size) override;
|
||||
void Free(void* data, size_t size) override;
|
||||
@@ -139,7 +137,6 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator {
|
||||
}
|
||||
|
||||
private:
|
||||
- uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land.
|
||||
std::atomic<size_t> total_mem_usage_ {0};
|
||||
|
||||
// Delegate to V8's allocator for compatibility with the V8 memory cage.
|
||||
diff --git a/src/stream_base.cc b/src/stream_base.cc
|
||||
index fc81108120f0066f2d5dabedc74e22cb6c84d8e4..0bf2642599ee91e2d2aa20d6d066c80b3026ecc5 100644
|
||||
--- a/src/stream_base.cc
|
||||
+++ b/src/stream_base.cc
|
||||
@@ -19,6 +19,7 @@ namespace node {
|
||||
using v8::Array;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
using v8::ConstructorBehavior;
|
||||
using v8::Context;
|
||||
using v8::DontDelete;
|
||||
@@ -243,8 +244,8 @@ int StreamBase::Writev(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
std::unique_ptr<BackingStore> bs;
|
||||
if (storage_size > 0) {
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(isolate, storage_size);
|
||||
+ bs = ArrayBuffer::NewBackingStore(
|
||||
+ isolate, storage_size, BackingStoreInitializationMode::kUninitialized);
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
@@ -398,14 +399,14 @@ int StreamBase::WriteString(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
if (try_write) {
|
||||
// Copy partial data
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(isolate, buf.len);
|
||||
+ bs = ArrayBuffer::NewBackingStore(
|
||||
+ isolate, buf.len, BackingStoreInitializationMode::kUninitialized);
|
||||
memcpy(bs->Data(), buf.base, buf.len);
|
||||
data_size = buf.len;
|
||||
} else {
|
||||
// Write it
|
||||
- NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
- bs = ArrayBuffer::NewBackingStore(isolate, storage_size);
|
||||
+ bs = ArrayBuffer::NewBackingStore(
|
||||
+ isolate, storage_size, BackingStoreInitializationMode::kUninitialized);
|
||||
data_size = StringBytes::Write(isolate,
|
||||
static_cast<char*>(bs->Data()),
|
||||
storage_size,
|
||||
276
patches/node/src_prepare_for_v8_sandboxing.patch
Normal file
276
patches/node/src_prepare_for_v8_sandboxing.patch
Normal file
@@ -0,0 +1,276 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: James M Snell <jasnell@gmail.com>
|
||||
Date: Sun, 18 May 2025 10:46:30 -0700
|
||||
Subject: src: prepare for v8 sandboxing
|
||||
|
||||
PR-URL: https://github.com/nodejs/node/pull/58376
|
||||
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
|
||||
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
|
||||
|
||||
diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc
|
||||
index f23cedf4f2449d8edc9a8de1b70332e75d693cdd..5ac2b1a83688fe99b13c37bf375ca6e22497dc18 100644
|
||||
--- a/src/crypto/crypto_dh.cc
|
||||
+++ b/src/crypto/crypto_dh.cc
|
||||
@@ -22,6 +22,8 @@ using ncrypto::DHPointer;
|
||||
using ncrypto::EVPKeyCtxPointer;
|
||||
using ncrypto::EVPKeyPointer;
|
||||
using v8::ArrayBuffer;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
+using v8::BackingStoreOnFailureMode;
|
||||
using v8::ConstructorBehavior;
|
||||
using v8::Context;
|
||||
using v8::DontDelete;
|
||||
@@ -55,12 +57,27 @@ void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const {
|
||||
|
||||
namespace {
|
||||
MaybeLocal<Value> DataPointerToBuffer(Environment* env, DataPointer&& data) {
|
||||
+#ifdef V8_ENABLE_SANDBOX
|
||||
+ auto backing = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(),
|
||||
+ data.size(),
|
||||
+ BackingStoreInitializationMode::kUninitialized,
|
||||
+ BackingStoreOnFailureMode::kReturnNull);
|
||||
+ if (!backing) {
|
||||
+ THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
|
||||
+ return MaybeLocal<Value>();
|
||||
+ }
|
||||
+ if (data.size() > 0) {
|
||||
+ memcpy(backing->Data(), data.get(), data.size());
|
||||
+ }
|
||||
+#else
|
||||
auto backing = ArrayBuffer::NewBackingStore(
|
||||
data.get(),
|
||||
data.size(),
|
||||
[](void* data, size_t len, void* ptr) { DataPointer free_me(data, len); },
|
||||
nullptr);
|
||||
data.release();
|
||||
+#endif // V8_ENABLE_SANDBOX
|
||||
|
||||
auto ab = ArrayBuffer::New(env->isolate(), std::move(backing));
|
||||
return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Value>());
|
||||
diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc
|
||||
index eab18ab9888e2f7c0757fefab80505d8c99dc742..7ecf810ea0f4106c7c44593dae1b0a3cf0673380 100644
|
||||
--- a/src/crypto/crypto_util.cc
|
||||
+++ b/src/crypto/crypto_util.cc
|
||||
@@ -37,6 +37,8 @@ using ncrypto::SSLCtxPointer;
|
||||
using ncrypto::SSLPointer;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::BackingStore;
|
||||
+using v8::BackingStoreInitializationMode;
|
||||
+using v8::BackingStoreOnFailureMode;
|
||||
using v8::BigInt;
|
||||
using v8::Context;
|
||||
using v8::Exception;
|
||||
@@ -359,34 +361,29 @@ ByteSource& ByteSource::operator=(ByteSource&& other) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
-std::unique_ptr<BackingStore> ByteSource::ReleaseToBackingStore(Environment* env) {
|
||||
+std::unique_ptr<BackingStore> ByteSource::ReleaseToBackingStore(
|
||||
+ Environment* env) {
|
||||
// It's ok for allocated_data_ to be nullptr but
|
||||
// only if size_ is zero.
|
||||
CHECK_IMPLIES(size_ > 0, allocated_data_ != nullptr);
|
||||
-#if defined(V8_ENABLE_SANDBOX)
|
||||
- // When V8 sandboxed pointers are enabled, we have to copy into the memory
|
||||
- // cage. We still want to ensure we erase the data on free though, so
|
||||
- // provide a custom deleter that calls OPENSSL_cleanse.
|
||||
- if (!size())
|
||||
- return ArrayBuffer::NewBackingStore(env->isolate(), 0);
|
||||
- std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
|
||||
- void* v8_data = allocator->Allocate(size());
|
||||
- CHECK(v8_data);
|
||||
- memcpy(v8_data, allocated_data_, size());
|
||||
- OPENSSL_clear_free(allocated_data_, size());
|
||||
+#ifdef V8_ENABLE_SANDBOX
|
||||
+ // If the v8 sandbox is enabled, then all array buffers must be allocated
|
||||
+ // via the isolate. External buffers are not allowed. So, instead of wrapping
|
||||
+ // the allocated data we'll copy it instead.
|
||||
+
|
||||
+ // TODO(@jasnell): It would be nice to use an abstracted utility to do this
|
||||
+ // branch instead of duplicating the V8_ENABLE_SANDBOX check each time.
|
||||
std::unique_ptr<BackingStore> ptr = ArrayBuffer::NewBackingStore(
|
||||
- v8_data,
|
||||
+ env->isolate(),
|
||||
size(),
|
||||
- [](void* data, size_t length, void*) {
|
||||
- OPENSSL_cleanse(data, length);
|
||||
- std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
|
||||
- allocator->Free(data, length);
|
||||
- }, nullptr);
|
||||
- CHECK(ptr);
|
||||
- allocated_data_ = nullptr;
|
||||
- data_ = nullptr;
|
||||
- size_ = 0;
|
||||
- return ptr;
|
||||
+ BackingStoreInitializationMode::kUninitialized,
|
||||
+ BackingStoreOnFailureMode::kReturnNull);
|
||||
+ if (!ptr) {
|
||||
+ THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+ memcpy(ptr->Data(), allocated_data_, size());
|
||||
+ OPENSSL_clear_free(allocated_data_, size_);
|
||||
#else
|
||||
std::unique_ptr<BackingStore> ptr = ArrayBuffer::NewBackingStore(
|
||||
allocated_data_,
|
||||
@@ -394,12 +391,12 @@ std::unique_ptr<BackingStore> ByteSource::ReleaseToBackingStore(Environment* env
|
||||
[](void* data, size_t length, void* deleter_data) {
|
||||
OPENSSL_clear_free(deleter_data, length);
|
||||
}, allocated_data_);
|
||||
+#endif // V8_ENABLE_SANDBOX
|
||||
CHECK(ptr);
|
||||
allocated_data_ = nullptr;
|
||||
data_ = nullptr;
|
||||
size_ = 0;
|
||||
return ptr;
|
||||
-#endif // defined(V8_ENABLE_SANDBOX)
|
||||
}
|
||||
|
||||
Local<ArrayBuffer> ByteSource::ToArrayBuffer(Environment* env) {
|
||||
@@ -711,8 +708,19 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
#else
|
||||
void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
- CHECK(args[0]->IsUint32());
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
+#ifdef V8_ENABLE_SANDBOX
|
||||
+ // The v8 sandbox is enabled, so we cannot use the secure heap because
|
||||
+ // the sandbox requires that all array buffers be allocated via the isolate.
|
||||
+ // That is fundamentally incompatible with the secure heap which allocates
|
||||
+ // in openssl's secure heap area. Instead we'll just throw an error here.
|
||||
+ //
|
||||
+ // That said, we really shouldn't get here in the first place since the
|
||||
+ // option to enable the secure heap is only available when the sandbox
|
||||
+ // is disabled.
|
||||
+ UNREACHABLE();
|
||||
+#else
|
||||
+ CHECK(args[0]->IsUint32());
|
||||
uint32_t len = args[0].As<Uint32>()->Value();
|
||||
void* data = OPENSSL_malloc(len);
|
||||
if (data == nullptr) {
|
||||
@@ -730,6 +738,7 @@ void SecureBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
data);
|
||||
Local<ArrayBuffer> buffer = ArrayBuffer::New(env->isolate(), store);
|
||||
args.GetReturnValue().Set(Uint8Array::New(buffer, 0, len));
|
||||
+#endif // V8_ENABLE_SANDBOX
|
||||
}
|
||||
#endif // defined(V8_ENABLE_SANDBOX)
|
||||
|
||||
diff --git a/src/crypto/crypto_x509.cc b/src/crypto/crypto_x509.cc
|
||||
index 0a1a3c694f3f544ada62235ade6a03a9deadfede..0ac2379c0899f1080dd325d492496555c5e1c6af 100644
|
||||
--- a/src/crypto/crypto_x509.cc
|
||||
+++ b/src/crypto/crypto_x509.cc
|
||||
@@ -29,6 +29,7 @@ using v8::ArrayBuffer;
|
||||
using v8::ArrayBufferView;
|
||||
using v8::BackingStore;
|
||||
using v8::BackingStoreInitializationMode;
|
||||
+using v8::BackingStoreOnFailureMode;
|
||||
using v8::Boolean;
|
||||
using v8::Context;
|
||||
using v8::Date;
|
||||
@@ -168,18 +169,20 @@ MaybeLocal<Value> ToV8Value(Local<Context> context, const BIOPointer& bio) {
|
||||
MaybeLocal<Value> ToBuffer(Environment* env, BIOPointer* bio) {
|
||||
if (bio == nullptr || !*bio) return {};
|
||||
BUF_MEM* mem = *bio;
|
||||
-#if defined(V8_ENABLE_SANDBOX)
|
||||
- std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
|
||||
- void* v8_data = allocator->Allocate(mem->length);
|
||||
- CHECK(v8_data);
|
||||
- memcpy(v8_data, mem->data, mem->length);
|
||||
- std::unique_ptr<v8::BackingStore> backing = ArrayBuffer::NewBackingStore(
|
||||
- v8_data,
|
||||
+#ifdef V8_ENABLE_SANDBOX
|
||||
+ // If the v8 sandbox is enabled, then all array buffers must be allocated
|
||||
+ // via the isolate. External buffers are not allowed. So, instead of wrapping
|
||||
+ // the BIOPointer we'll copy it instead.
|
||||
+ auto backing = ArrayBuffer::NewBackingStore(
|
||||
+ env->isolate(),
|
||||
mem->length,
|
||||
- [](void* data, size_t length, void*) {
|
||||
- std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
|
||||
- allocator->Free(data, length);
|
||||
- }, nullptr);
|
||||
+ BackingStoreInitializationMode::kUninitialized,
|
||||
+ BackingStoreOnFailureMode::kReturnNull);
|
||||
+ if (!backing) {
|
||||
+ THROW_ERR_MEMORY_ALLOCATION_FAILED(env);
|
||||
+ return MaybeLocal<Value>();
|
||||
+ }
|
||||
+ memcpy(backing->Data(), mem->data, mem->length);
|
||||
#else
|
||||
auto backing = ArrayBuffer::NewBackingStore(
|
||||
mem->data,
|
||||
@@ -188,8 +191,7 @@ MaybeLocal<Value> ToBuffer(Environment* env, BIOPointer* bio) {
|
||||
BIOPointer free_me(static_cast<BIO*>(data));
|
||||
},
|
||||
bio->release());
|
||||
-#endif
|
||||
-
|
||||
+#endif // V8_ENABLE_SANDBOX
|
||||
auto ab = ArrayBuffer::New(env->isolate(), std::move(backing));
|
||||
Local<Value> ret;
|
||||
if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&ret)) return {};
|
||||
diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc
|
||||
index 7b2efa49468c0bed2f5935552addd3ab37d0a50b..97eb62047b6692d63deacf2e7346e85351337e85 100644
|
||||
--- a/src/js_native_api_v8.cc
|
||||
+++ b/src/js_native_api_v8.cc
|
||||
@@ -114,7 +114,7 @@ napi_status NewExternalString(napi_env env,
|
||||
CHECK_NEW_STRING_ARGS(env, str, length, result);
|
||||
|
||||
napi_status status;
|
||||
-#if defined(V8_ENABLE_SANDBOX)
|
||||
+#ifdef V8_ENABLE_SANDBOX
|
||||
status = create_api(env, str, length, result);
|
||||
if (status == napi_ok) {
|
||||
if (copied != nullptr) {
|
||||
diff --git a/src/node_api.cc b/src/node_api.cc
|
||||
index 2769997f0ede0e921fcb8826942609e497e5f9cb..d9b17780f6143f1c3f8488a20144376963e43fbc 100644
|
||||
--- a/src/node_api.cc
|
||||
+++ b/src/node_api.cc
|
||||
@@ -1056,7 +1056,7 @@ napi_create_external_buffer(napi_env env,
|
||||
NAPI_PREAMBLE(env);
|
||||
CHECK_ARG(env, result);
|
||||
|
||||
-#if defined(V8_ENABLE_SANDBOX)
|
||||
+#ifdef V8_ENABLE_SANDBOX
|
||||
return napi_set_last_error(env, napi_no_external_buffers_allowed);
|
||||
#else
|
||||
v8::Isolate* isolate = env->isolate;
|
||||
diff --git a/src/node_options.cc b/src/node_options.cc
|
||||
index b067685822dc056e446e1a9402a5a6cba86cc722..cf70816d3ebacee1aaec6d465e6ebdc5f1dec5c3 100644
|
||||
--- a/src/node_options.cc
|
||||
+++ b/src/node_options.cc
|
||||
@@ -83,6 +83,8 @@ void PerProcessOptions::CheckOptions(std::vector<std::string>* errors,
|
||||
}
|
||||
|
||||
// Any value less than 2 disables use of the secure heap.
|
||||
+#ifndef V8_ENABLE_SANDBOX
|
||||
+ // The secure heap is not supported when V8_ENABLE_SANDBOX is enabled.
|
||||
if (secure_heap >= 2) {
|
||||
if ((secure_heap & (secure_heap - 1)) != 0)
|
||||
errors->push_back("--secure-heap must be a power of 2");
|
||||
@@ -95,6 +97,7 @@ void PerProcessOptions::CheckOptions(std::vector<std::string>* errors,
|
||||
if ((secure_heap_min & (secure_heap_min - 1)) != 0)
|
||||
errors->push_back("--secure-heap-min must be a power of 2");
|
||||
}
|
||||
+#endif // V8_ENABLE_SANDBOX
|
||||
#endif // HAVE_OPENSSL
|
||||
|
||||
if (use_largepages != "off" &&
|
||||
@@ -1243,6 +1246,7 @@ PerProcessOptionsParser::PerProcessOptionsParser(
|
||||
"force FIPS crypto (cannot be disabled)",
|
||||
&PerProcessOptions::force_fips_crypto,
|
||||
kAllowedInEnvvar);
|
||||
+#ifndef V8_ENABLE_SANDBOX
|
||||
AddOption("--secure-heap",
|
||||
"total size of the OpenSSL secure heap",
|
||||
&PerProcessOptions::secure_heap,
|
||||
@@ -1251,6 +1255,7 @@ PerProcessOptionsParser::PerProcessOptionsParser(
|
||||
"minimum allocation size from the OpenSSL secure heap",
|
||||
&PerProcessOptions::secure_heap_min,
|
||||
kAllowedInEnvvar);
|
||||
+#endif // V8_ENABLE_SANDBOX
|
||||
#endif // HAVE_OPENSSL
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
AddOption("--openssl-legacy-provider",
|
||||
@@ -6,63 +6,6 @@ Subject: support V8 sandboxed pointers
|
||||
This refactors several allocators to allocate within the V8 memory cage,
|
||||
allowing them to be compatible with the V8_SANDBOXED_POINTERS feature.
|
||||
|
||||
diff --git a/src/api/environment.cc b/src/api/environment.cc
|
||||
index fd71ceac65ccef1d2832b45b0b5612877cee22c1..cb37fa080fc8e8d524cfa2758c4a8c2c5652324d 100644
|
||||
--- a/src/api/environment.cc
|
||||
+++ b/src/api/environment.cc
|
||||
@@ -106,6 +106,14 @@ MaybeLocal<Value> PrepareStackTraceCallback(Local<Context> context,
|
||||
return result;
|
||||
}
|
||||
|
||||
+NodeArrayBufferAllocator::NodeArrayBufferAllocator() {
|
||||
+ zero_fill_field_ = static_cast<uint32_t*>(allocator_->Allocate(sizeof(*zero_fill_field_)));
|
||||
+}
|
||||
+
|
||||
+NodeArrayBufferAllocator::~NodeArrayBufferAllocator() {
|
||||
+ allocator_->Free(zero_fill_field_, sizeof(*zero_fill_field_));
|
||||
+}
|
||||
+
|
||||
void* NodeArrayBufferAllocator::Allocate(size_t size) {
|
||||
void* ret;
|
||||
if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers)
|
||||
diff --git a/src/crypto/crypto_dh.cc b/src/crypto/crypto_dh.cc
|
||||
index f23cedf4f2449d8edc9a8de1b70332e75d693cdd..976653dd1e9363e046788fc3419a9b649ceb2ea4 100644
|
||||
--- a/src/crypto/crypto_dh.cc
|
||||
+++ b/src/crypto/crypto_dh.cc
|
||||
@@ -55,13 +55,32 @@ void DiffieHellman::MemoryInfo(MemoryTracker* tracker) const {
|
||||
|
||||
namespace {
|
||||
MaybeLocal<Value> DataPointerToBuffer(Environment* env, DataPointer&& data) {
|
||||
+#if defined(V8_ENABLE_SANDBOX)
|
||||
+ std::unique_ptr<v8::BackingStore> backing;
|
||||
+ if (data.size() > 0) {
|
||||
+ std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
|
||||
+ void* v8_data = allocator->Allocate(data.size());
|
||||
+ CHECK(v8_data);
|
||||
+ memcpy(v8_data, data.get(), data.size());
|
||||
+ backing = ArrayBuffer::NewBackingStore(
|
||||
+ v8_data,
|
||||
+ data.size(),
|
||||
+ [](void* data, size_t length, void*) {
|
||||
+ std::unique_ptr<ArrayBuffer::Allocator> allocator(ArrayBuffer::Allocator::NewDefaultAllocator());
|
||||
+ allocator->Free(data, length);
|
||||
+ }, nullptr);
|
||||
+ } else {
|
||||
+ NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
|
||||
+ backing = v8::ArrayBuffer::NewBackingStore(env->isolate(), data.size());
|
||||
+ }
|
||||
+#else
|
||||
auto backing = ArrayBuffer::NewBackingStore(
|
||||
data.get(),
|
||||
data.size(),
|
||||
[](void* data, size_t len, void* ptr) { DataPointer free_me(data, len); },
|
||||
nullptr);
|
||||
data.release();
|
||||
-
|
||||
+#endif
|
||||
auto ab = ArrayBuffer::New(env->isolate(), std::move(backing));
|
||||
return Buffer::New(env, ab, 0, ab->ByteLength()).FromMaybe(Local<Value>());
|
||||
}
|
||||
diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc
|
||||
index 4505786745c54a529f904d5e7813a86204e0a78b..eab18ab9888e2f7c0757fefab80505d8c99dc742 100644
|
||||
--- a/src/crypto/crypto_util.cc
|
||||
@@ -188,6 +131,28 @@ index f616223cfb0f6e10f7cf57ada9704316bde2797e..eb6dad44a49d997097c8fb5009eeb60a
|
||||
auto ab = ArrayBuffer::New(env->isolate(), std::move(backing));
|
||||
Local<Value> ret;
|
||||
if (!Buffer::New(env, ab, 0, ab->ByteLength()).ToLocal(&ret)) return {};
|
||||
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
|
||||
index 357dc5f6d1c1c2d3756a94c1326b0502403e1eaf..b9f0c97938203b4652780a7d707c5e83319330b0 100644
|
||||
--- a/src/node_buffer.cc
|
||||
+++ b/src/node_buffer.cc
|
||||
@@ -1412,7 +1412,7 @@ inline size_t CheckNumberToSize(Local<Value> number) {
|
||||
CHECK(value >= 0 && value < maxSize);
|
||||
size_t size = static_cast<size_t>(value);
|
||||
#ifdef V8_ENABLE_SANDBOX
|
||||
- CHECK_LE(size, kMaxSafeBufferSizeForSandbox);
|
||||
+ CHECK_LE(size, v8::internal::kMaxSafeBufferSizeForSandbox);
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
@@ -1437,7 +1437,7 @@ void CreateUnsafeArrayBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
buf = ArrayBuffer::New(isolate, size);
|
||||
} else {
|
||||
std::unique_ptr<BackingStore> store =
|
||||
- ArrayBuffer::NewBackingStoreForNodeLTS(isolate, size);
|
||||
+ ArrayBuffer::NewBackingStore(isolate, size);
|
||||
if (!store) {
|
||||
return env->ThrowRangeError("Array buffer allocation failed");
|
||||
}
|
||||
diff --git a/src/node_i18n.cc b/src/node_i18n.cc
|
||||
index 6be3920632b25db450025ebab6a2636e4811cdbe..b49916d2b5fc5e58cf3fb67329430fd3df8fb813 100644
|
||||
--- a/src/node_i18n.cc
|
||||
@@ -228,30 +193,6 @@ index 6be3920632b25db450025ebab6a2636e4811cdbe..b49916d2b5fc5e58cf3fb67329430fd3
|
||||
}
|
||||
|
||||
constexpr const char* EncodingName(const enum encoding encoding) {
|
||||
diff --git a/src/node_internals.h b/src/node_internals.h
|
||||
index 12ea72b61b0a5e194207bb369dfed4b8667107cb..64442215714a98f648971e517ddd9c77e38fe3f2 100644
|
||||
--- a/src/node_internals.h
|
||||
+++ b/src/node_internals.h
|
||||
@@ -121,7 +121,9 @@ v8::MaybeLocal<v8::Object> InitializePrivateSymbols(
|
||||
|
||||
class NodeArrayBufferAllocator : public ArrayBufferAllocator {
|
||||
public:
|
||||
- inline uint32_t* zero_fill_field() { return &zero_fill_field_; }
|
||||
+ NodeArrayBufferAllocator();
|
||||
+ ~NodeArrayBufferAllocator() override;
|
||||
+ inline uint32_t* zero_fill_field() { return zero_fill_field_; }
|
||||
|
||||
void* Allocate(size_t size) override; // Defined in src/node.cc
|
||||
void* AllocateUninitialized(size_t size) override;
|
||||
@@ -139,7 +141,7 @@ class NodeArrayBufferAllocator : public ArrayBufferAllocator {
|
||||
}
|
||||
|
||||
private:
|
||||
- uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land.
|
||||
+ uint32_t* zero_fill_field_ = nullptr; // Boolean but exposed as uint32 to JS land.
|
||||
std::atomic<size_t> total_mem_usage_ {0};
|
||||
|
||||
// Delegate to V8's allocator for compatibility with the V8 memory cage.
|
||||
diff --git a/src/node_serdes.cc b/src/node_serdes.cc
|
||||
index c55a2e28066147ae5ca5def10ec76ccc03c634b4..c54183c72944989219b6437c9e571a3f7f3f8dd5 100644
|
||||
--- a/src/node_serdes.cc
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Tue, 4 Nov 2025 21:20:26 +0100
|
||||
Subject: test: correct conditional secure heap flags test
|
||||
|
||||
PR-URL: https://github.com/nodejs/node/pull/60385
|
||||
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
|
||||
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
|
||||
(cherry picked from commit 53c4a39fec941e04150554fdd3e654b48f2e1b31)
|
||||
|
||||
diff --git a/test/parallel/test-process-env-allowed-flags-are-documented.js b/test/parallel/test-process-env-allowed-flags-are-documented.js
|
||||
index afd43cfffe638f4f084f1c36949068e7239eadc3..c70e073bab888c349e8f5c691f5679a3796c896c 100644
|
||||
--- a/test/parallel/test-process-env-allowed-flags-are-documented.js
|
||||
+++ b/test/parallel/test-process-env-allowed-flags-are-documented.js
|
||||
@@ -49,6 +49,8 @@ if (!hasOpenSSL3) {
|
||||
documented.delete('--openssl-shared-config');
|
||||
}
|
||||
|
||||
+const isV8Sandboxed = process.config.variables.v8_enable_sandbox;
|
||||
+
|
||||
// Filter out options that are conditionally present.
|
||||
const conditionalOpts = [
|
||||
{
|
||||
@@ -74,6 +76,9 @@ const conditionalOpts = [
|
||||
}, {
|
||||
include: process.features.inspector,
|
||||
filter: (opt) => opt.startsWith('--inspect') || opt === '--debug-port'
|
||||
+ }, {
|
||||
+ include: !isV8Sandboxed,
|
||||
+ filter: (opt) => ['--secure-heap', '--secure-heap-min'].includes(opt)
|
||||
},
|
||||
];
|
||||
documented.forEach((opt) => {
|
||||
@@ -40,6 +40,7 @@ DevToolsSecurity -enable
|
||||
# security import "$dir"/public.key -k $KEY_CHAIN
|
||||
|
||||
# Generate Trust Settings
|
||||
# TODO: Remove NPX
|
||||
npm_config_yes=true npx ts-node "$(dirname $0)"/gen-trust.ts "$dir"/certificate.cer "$dir"/trust.xml
|
||||
|
||||
# Import Trust Settings
|
||||
|
||||
32
script/copy-pipeline-segment-publish.js
Normal file
32
script/copy-pipeline-segment-publish.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const yaml = require('yaml');
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const PREFIX = '# AUTOGENERATED FILE - DO NOT EDIT MANUALLY\n# ONLY EDIT .github/workflows/pipeline-segment-electron-build.yml\n\n';
|
||||
|
||||
const base = path.resolve(__dirname, '../.github/workflows/pipeline-segment-electron-build.yml');
|
||||
const target = path.resolve(__dirname, '../.github/workflows/pipeline-segment-electron-publish.yml');
|
||||
|
||||
const baseContents = fs.readFileSync(base, 'utf-8');
|
||||
|
||||
const parsedBase = yaml.parse(baseContents);
|
||||
parsedBase.jobs.build.permissions = {
|
||||
'artifact-metadata': 'write',
|
||||
attestations: 'write',
|
||||
contents: 'read',
|
||||
'id-token': 'write'
|
||||
};
|
||||
|
||||
if (process.argv.includes('--check')) {
|
||||
if (fs.readFileSync(target, 'utf-8') !== PREFIX + yaml.stringify(parsedBase)) {
|
||||
console.error(`${target} is out of date`);
|
||||
console.error('Please run "copy-pipeline-segment-publish.js" to update it');
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
fs.writeFileSync(
|
||||
target,
|
||||
PREFIX + yaml.stringify(parsedBase)
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
const chalk = require('chalk');
|
||||
const { GitProcess } = require('dugite');
|
||||
|
||||
const childProcess = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
@@ -61,13 +61,16 @@ function getAbsoluteElectronExec () {
|
||||
return path.resolve(SRC_DIR, getElectronExec());
|
||||
}
|
||||
|
||||
async function handleGitCall (args, gitDir) {
|
||||
const details = await GitProcess.exec(args, gitDir);
|
||||
if (details.exitCode === 0) {
|
||||
return details.stdout.replace(/^\*|\s+|\s+$/, '');
|
||||
function handleGitCall (args, gitDir) {
|
||||
const result = childProcess.spawnSync('git', args, {
|
||||
cwd: gitDir,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (result.status === 0) {
|
||||
return result.stdout.replace(/^\*|\s+|\s+$/, '');
|
||||
} else {
|
||||
const error = GitProcess.parseError(details.stderr);
|
||||
console.log(`${fail} couldn't parse git process call: `, error);
|
||||
console.log(`${fail} couldn't parse git process call: `, result.stderr);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { GitProcess } = require('dugite');
|
||||
const { ESLint } = require('eslint');
|
||||
const minimist = require('minimist');
|
||||
|
||||
@@ -153,7 +152,7 @@ const LINTERS = [{
|
||||
}, {
|
||||
key: 'javascript',
|
||||
roots: ['build', 'default_app', 'lib', 'npm', 'script', 'spec'],
|
||||
ignoreRoots: ['spec/node_modules'],
|
||||
ignoreRoots: ['spec/node_modules', 'spec/fixtures/native-addon'],
|
||||
test: filename => filename.endsWith('.js') || filename.endsWith('.ts') || filename.endsWith('.mjs'),
|
||||
run: async (opts, filenames) => {
|
||||
const eslint = new ESLint({
|
||||
@@ -282,7 +281,7 @@ const LINTERS = [{
|
||||
}, {
|
||||
key: 'md',
|
||||
roots: ['.'],
|
||||
ignoreRoots: ['.git', 'node_modules', 'spec/node_modules'],
|
||||
ignoreRoots: ['.git', 'node_modules', 'spec/node_modules', 'spec/fixtures/native-addon'],
|
||||
test: filename => filename.endsWith('.md'),
|
||||
run: async (opts, filenames) => {
|
||||
const { getCodeBlocks } = await import('@electron/lint-roller/dist/lib/markdown.js');
|
||||
@@ -431,9 +430,13 @@ function populateLinterWithArgs (linter, opts) {
|
||||
}
|
||||
|
||||
async function findChangedFiles (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));
|
||||
const result = childProcess.spawnSync('git', ['diff', '--name-only', '--cached'], {
|
||||
cwd: top,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
console.log('Failed to find changed files', result.stderr);
|
||||
process.exit(1);
|
||||
}
|
||||
const relativePaths = result.stdout.split(/\r\n|\r|\n/g);
|
||||
|
||||
@@ -9,7 +9,7 @@ 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 { YARN_SCRIPT_PATH } = require('./yarn');
|
||||
|
||||
if (!require.main) {
|
||||
throw new Error('Must call the nan spec runner directly');
|
||||
@@ -110,13 +110,12 @@ async function main () {
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
|
||||
if (buildStatus !== 0 || signal != null) {
|
||||
console.error('Failed to build nan test modules');
|
||||
return process.exit(buildStatus !== 0 ? buildStatus : signal);
|
||||
}
|
||||
|
||||
const { status: installStatus, signal: installSignal } = cp.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install'], {
|
||||
const { status: installStatus, signal: installSignal } = cp.spawnSync(process.execPath, [YARN_SCRIPT_PATH, 'install'], {
|
||||
env,
|
||||
cwd: NAN_DIR,
|
||||
stdio: 'inherit',
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
"parallel/test-tls-passphrase",
|
||||
"parallel/test-tls-peer-certificate",
|
||||
"parallel/test-tls-pfx-authorizationerror",
|
||||
"parallel/test-tls-psk-alpn-callback-exception-handling",
|
||||
"parallel/test-tls-psk-circuit",
|
||||
"parallel/test-tls-reduced-SECLEVEL-in-cipher",
|
||||
"parallel/test-tls-root-certificates",
|
||||
|
||||
@@ -13,6 +13,7 @@ import { createGitHubTokenStrategy } from '../github-token';
|
||||
import { ELECTRON_ORG, ELECTRON_REPO, ElectronReleaseRepo, NIGHTLY_REPO } from '../types';
|
||||
|
||||
const rootPackageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../../package.json'), 'utf-8'));
|
||||
rootPackageJson.name = 'electron';
|
||||
|
||||
if (!process.env.ELECTRON_NPM_OTP) {
|
||||
console.error('Please set ELECTRON_NPM_OTP');
|
||||
@@ -197,6 +198,7 @@ new Promise<string>((resolve, reject) => {
|
||||
});
|
||||
})
|
||||
.then((tarballPath) => {
|
||||
// TODO: Remove NPX
|
||||
const existingVersionJSON = childProcess.execSync(`npx npm@7 view ${rootPackageJson.name}@${currentElectronVersion} --json`).toString('utf-8');
|
||||
// It's possible this is a re-run and we already have published the package, if not we just publish like normal
|
||||
if (!existingVersionJSON) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import { GitProcess } from 'dugite';
|
||||
import { valid, compare, gte, lte } from 'semver';
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { basename } from 'node:path';
|
||||
import { parseArgs } from 'node:util';
|
||||
|
||||
@@ -20,8 +20,12 @@ const semverify = (version: string) => version.replace(/^origin\//, '').replace(
|
||||
|
||||
const runGit = async (args: string[]) => {
|
||||
console.info(`Running: git ${args.join(' ')}`);
|
||||
const response = await GitProcess.exec(args, ELECTRON_DIR);
|
||||
if (response.exitCode !== 0) {
|
||||
const response = spawnSync('git', args, {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (response.status !== 0) {
|
||||
throw new Error(response.stderr.trim());
|
||||
}
|
||||
return response.stdout.trim();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import { GitProcess } from 'dugite';
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
||||
import { resolve as _resolve } from 'node:path';
|
||||
|
||||
@@ -105,8 +105,13 @@ class Pool {
|
||||
**/
|
||||
|
||||
const runGit = async (dir: string, args: string[]) => {
|
||||
const response = await GitProcess.exec(args, dir);
|
||||
if (response.exitCode !== 0) {
|
||||
const response = spawnSync('git', args, {
|
||||
cwd: dir,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe'],
|
||||
maxBuffer: 100 * 1024 * 1024 // 100MB buffer to handle large git outputs
|
||||
});
|
||||
if (response.status !== 0) {
|
||||
throw new Error(response.stderr.trim());
|
||||
}
|
||||
return response.stdout.trim();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import * as chalk from 'chalk';
|
||||
import { GitProcess } from 'dugite';
|
||||
|
||||
import { execSync } from 'node:child_process';
|
||||
import { execSync, spawnSync } from 'node:child_process';
|
||||
import { join } from 'node:path';
|
||||
|
||||
import { createGitHubTokenStrategy } from './github-token';
|
||||
@@ -166,11 +165,12 @@ async function createRelease (
|
||||
}
|
||||
|
||||
async function pushRelease (branch: string) {
|
||||
const pushDetails = await GitProcess.exec(
|
||||
['push', 'origin', `HEAD:${branch}`, '--follow-tags'],
|
||||
ELECTRON_DIR
|
||||
);
|
||||
if (pushDetails.exitCode === 0) {
|
||||
const pushDetails = spawnSync('git', ['push', 'origin', `HEAD:${branch}`, '--follow-tags'], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (pushDetails.status === 0) {
|
||||
console.log(
|
||||
`${pass} Successfully pushed the release. Wait for ` +
|
||||
'release builds to finish before running "npm run release".'
|
||||
@@ -191,11 +191,12 @@ async function runReleaseBuilds (branch: string, newVersion: string) {
|
||||
|
||||
async function tagRelease (version: string) {
|
||||
console.log(`Tagging release ${version}.`);
|
||||
const checkoutDetails = await GitProcess.exec(
|
||||
['tag', '-a', '-m', version, version],
|
||||
ELECTRON_DIR
|
||||
);
|
||||
if (checkoutDetails.exitCode === 0) {
|
||||
const checkoutDetails = spawnSync('git', ['tag', '-a', '-m', version, version], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (checkoutDetails.status === 0) {
|
||||
console.log(`${pass} Successfully tagged ${version}.`);
|
||||
} else {
|
||||
console.log(
|
||||
|
||||
@@ -368,6 +368,14 @@ def upload_io_to_github(release, filename, filepath, version):
|
||||
sys.stdout.buffer.write(c)
|
||||
sys.stdout.flush()
|
||||
|
||||
if "GITHUB_OUTPUT" in os.environ:
|
||||
output_path = os.environ["GITHUB_OUTPUT"]
|
||||
with open(output_path, "r+", encoding='utf-8') as github_output:
|
||||
if len(github_output.readlines()) > 0:
|
||||
github_output.write(",")
|
||||
else:
|
||||
github_output.write('UPLOADED_PATHS=')
|
||||
github_output.write(filepath)
|
||||
|
||||
def upload_sha256_checksum(version, file_path, key_prefix=None):
|
||||
checksum_path = f'{file_path}.sha256sum'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { GitProcess } from 'dugite';
|
||||
import * as semver from 'semver';
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
import { ELECTRON_DIR } from '../lib/utils';
|
||||
|
||||
export enum PreType {
|
||||
@@ -27,7 +28,11 @@ export const isStable = (v: string) => {
|
||||
|
||||
export async function nextAlpha (v: string) {
|
||||
const next = semver.coerce(semver.clean(v));
|
||||
const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-alpha.*`], ELECTRON_DIR);
|
||||
const tagBlob = spawnSync('git', ['tag', '--list', '-l', `v${next}-alpha.*`], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
const tags = tagBlob.stdout.split('\n').filter(e => e !== '');
|
||||
tags.sort((t1, t2) => {
|
||||
const a = parseInt(t1.split('.').pop()!, 10);
|
||||
@@ -41,7 +46,11 @@ export async function nextAlpha (v: string) {
|
||||
|
||||
export async function nextBeta (v: string) {
|
||||
const next = semver.coerce(semver.clean(v));
|
||||
const tagBlob = await GitProcess.exec(['tag', '--list', '-l', `v${next}-beta.*`], ELECTRON_DIR);
|
||||
const tagBlob = spawnSync('git', ['tag', '--list', '-l', `v${next}-beta.*`], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
const tags = tagBlob.stdout.split('\n').filter(e => e !== '');
|
||||
tags.sort((t1, t2) => {
|
||||
const a = parseInt(t1.split('.').pop()!, 10);
|
||||
@@ -57,7 +66,11 @@ export async function nextNightly (v: string) {
|
||||
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 = spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
}).stdout.trim();
|
||||
if (branch === 'main') {
|
||||
next = semver.inc(await getLastMajorForMain(), 'major');
|
||||
} else if (isStable(v)) {
|
||||
@@ -69,8 +82,12 @@ export async function nextNightly (v: string) {
|
||||
|
||||
async function getLastMajorForMain () {
|
||||
let branchNames;
|
||||
const result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]*-x-y'], ELECTRON_DIR);
|
||||
if (result.exitCode === 0) {
|
||||
const result = spawnSync('git', ['branch', '-a', '--remote', '--list', 'origin/[0-9]*-x-y'], {
|
||||
cwd: ELECTRON_DIR,
|
||||
encoding: 'utf8',
|
||||
stdio: ['inherit', 'pipe', 'pipe']
|
||||
});
|
||||
if (result.status === 0) {
|
||||
branchNames = result.stdout.trim().split('\n');
|
||||
const filtered = branchNames.map(b => b.replace('origin/', ''));
|
||||
return getNextReleaseBranch(filtered);
|
||||
|
||||
@@ -114,11 +114,14 @@ async function runClangTidy (
|
||||
outDir: string,
|
||||
filenames: string[],
|
||||
checks: string = '',
|
||||
jobs: number = 1
|
||||
jobs: number = 1,
|
||||
fix: boolean = false
|
||||
): Promise<boolean> {
|
||||
const cmd = path.resolve(LLVM_BIN, 'clang-tidy');
|
||||
const args = [`-p=${outDir}`, '--use-color'];
|
||||
const args = [`-p=${outDir}`];
|
||||
|
||||
if (!process.env.CI) args.push('--use-color');
|
||||
if (fix) args.push('--fix');
|
||||
if (checks) args.push(`--checks=${checks}`);
|
||||
|
||||
// Remove any files that aren't in the compilation database to prevent
|
||||
@@ -209,7 +212,7 @@ function parseCommandLine () {
|
||||
if (!arg || arg.startsWith('-')) {
|
||||
console.log(
|
||||
'Usage: script/run-clang-tidy.ts [-h|--help] [--jobs|-j] ' +
|
||||
'[--checks] --out-dir OUTDIR [file1 file2]'
|
||||
'[--fix] [--checks] --out-dir OUTDIR [file1 file2]'
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -218,7 +221,7 @@ function parseCommandLine () {
|
||||
};
|
||||
|
||||
const opts = minimist(process.argv.slice(2), {
|
||||
boolean: ['help'],
|
||||
boolean: ['fix', 'help'],
|
||||
string: ['checks', 'out-dir'],
|
||||
default: { jobs: 1 },
|
||||
alias: { help: 'h', jobs: 'j' },
|
||||
@@ -270,17 +273,23 @@ async function main (): Promise<boolean> {
|
||||
const filenames = [];
|
||||
|
||||
if (opts._.length > 0) {
|
||||
if (opts._.some((filename) => filename.endsWith('.h'))) {
|
||||
throw new ErrorWithExitCode(
|
||||
'Filenames must be for translation units, not headers', 3
|
||||
);
|
||||
}
|
||||
|
||||
filenames.push(...opts._.map((filename) => path.resolve(filename)));
|
||||
} else {
|
||||
filenames.push(
|
||||
...(await findMatchingFiles(
|
||||
path.resolve(SOURCE_ROOT, 'shell'),
|
||||
(filename: string) => /.*\.(?:cc|h|mm)$/.test(filename)
|
||||
(filename: string) => /.*\.(?:cc|mm)$/.test(filename)
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
return runClangTidy(outDir, filenames, opts.checks, opts.jobs);
|
||||
return runClangTidy(outDir, filenames, opts.checks, opts.jobs, opts.fix);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
@@ -21,6 +21,7 @@ const fail = chalk.red('✗');
|
||||
const FAILURE_STATUS_KEY = 'Electron_Spec_Runner_Failures';
|
||||
|
||||
const args = minimist(process.argv, {
|
||||
boolean: ['skipYarnInstall'],
|
||||
string: ['runners', 'target', 'electronVersion'],
|
||||
number: ['enableRerun'],
|
||||
unknown: arg => unknownFlags.push(arg)
|
||||
@@ -36,10 +37,9 @@ for (const flag of unknownFlags) {
|
||||
}
|
||||
|
||||
const utils = require('./lib/utils');
|
||||
const { YARN_VERSION } = require('./yarn');
|
||||
const { YARN_SCRIPT_PATH } = require('./yarn');
|
||||
|
||||
const BASE = path.resolve(__dirname, '../..');
|
||||
const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
||||
|
||||
const runners = new Map([
|
||||
['main', { description: 'Main process specs', run: runMainProcessElectronTests }]
|
||||
@@ -96,7 +96,7 @@ async function main () {
|
||||
const somethingChanged = (currentSpecHash !== lastSpecHash) ||
|
||||
(lastSpecInstallHash !== currentSpecInstallHash);
|
||||
|
||||
if (somethingChanged) {
|
||||
if (somethingChanged && !args.skipYarnInstall) {
|
||||
await installSpecModules(path.resolve(__dirname, '..', 'spec'));
|
||||
await getSpecHash().then(saveSpecHash);
|
||||
}
|
||||
@@ -375,7 +375,7 @@ async function runTestUsingElectron (specDir, testName, shouldRerun, additionalA
|
||||
if (shouldRerun) {
|
||||
await rerunFailedTests(specDir, testName);
|
||||
} else {
|
||||
return false;
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
console.log(`${pass} Electron ${testName} process tests passed.`);
|
||||
@@ -419,7 +419,8 @@ async function installSpecModules (dir) {
|
||||
if (fs.existsSync(path.resolve(dir, 'node_modules'))) {
|
||||
await fs.promises.rm(path.resolve(dir, 'node_modules'), { force: true, recursive: true });
|
||||
}
|
||||
const { status } = childProcess.spawnSync(NPX_CMD, [`yarn@${YARN_VERSION}`, 'install', '--frozen-lockfile'], {
|
||||
const yarnArgs = [YARN_SCRIPT_PATH, 'install', '--immutable'];
|
||||
const { status } = childProcess.spawnSync(process.execPath, yarnArgs, {
|
||||
env,
|
||||
cwd: dir,
|
||||
stdio: 'inherit',
|
||||
@@ -429,14 +430,27 @@ async function installSpecModules (dir) {
|
||||
console.log(`${fail} Failed to yarn install in '${dir}'`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
const { status: rebuildStatus } = childProcess.spawnSync('npm', ['rebuild', 'abstract-socket'], {
|
||||
env,
|
||||
cwd: dir,
|
||||
stdio: 'inherit',
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
if (rebuildStatus !== 0) {
|
||||
console.log(`${fail} Failed to rebuild abstract-socket native module`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSpecHash () {
|
||||
return Promise.all([
|
||||
(async () => {
|
||||
const hasher = crypto.createHash('SHA256');
|
||||
hasher.update(fs.readFileSync(path.resolve(__dirname, '../yarn.lock')));
|
||||
hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/package.json')));
|
||||
hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/yarn.lock')));
|
||||
hasher.update(fs.readFileSync(path.resolve(__dirname, '../script/spec-runner.js')));
|
||||
return hasher.digest('hex');
|
||||
})(),
|
||||
|
||||
18
script/yarn.js
Normal file → Executable file
18
script/yarn.js
Normal file → Executable file
@@ -1,21 +1,7 @@
|
||||
const cp = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const YARN_VERSION = /'yarn_version': '(.+?)'/.exec(fs.readFileSync(path.resolve(__dirname, '../DEPS'), 'utf8'))[1];
|
||||
const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
||||
exports.YARN_SCRIPT_PATH = path.resolve(__dirname, '..', '.yarn/releases/yarn-4.12.0.cjs');
|
||||
|
||||
if (require.main === module) {
|
||||
const child = cp.spawn(NPX_CMD, [`yarn@${YARN_VERSION}`, ...process.argv.slice(2)], {
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
npm_config_yes: 'true'
|
||||
},
|
||||
shell: process.platform === 'win32'
|
||||
});
|
||||
|
||||
child.on('exit', code => process.exit(code));
|
||||
require(exports.YARN_SCRIPT_PATH);
|
||||
}
|
||||
|
||||
exports.YARN_VERSION = YARN_VERSION;
|
||||
|
||||
@@ -155,7 +155,8 @@ void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
|
||||
if (rightmostMenuPoint > screenRight)
|
||||
position.x = position.x - [menu size].width;
|
||||
|
||||
[popup_controllers_[window_id] setCloseCallback:std::move(close_callback)];
|
||||
[popup_controllers_[window_id]
|
||||
setPopupCloseCallback:std::move(close_callback)];
|
||||
|
||||
if (frame && frame->render_frame_host()) {
|
||||
auto* rfh = frame->render_frame_host()->GetOutermostMainFrameOrEmbedder();
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "base/win/wrapped_window_proc.h"
|
||||
#include "shell/common/color_util.h"
|
||||
#include "shell/common/process_util.h"
|
||||
#include "skia/ext/skia_utils_win.h"
|
||||
#include "ui/gfx/color_utils.h"
|
||||
#include "ui/gfx/win/hwnd_util.h"
|
||||
|
||||
@@ -88,7 +89,7 @@ std::string SystemPreferences::GetAccentColor() {
|
||||
if (!color.has_value())
|
||||
return "";
|
||||
|
||||
return hexColorDWORDToRGBA(color.value());
|
||||
return ToRGBAHex(skia::COLORREFToSkColor(color.value()), false);
|
||||
}
|
||||
|
||||
std::string SystemPreferences::GetColor(gin_helper::ErrorThrower thrower,
|
||||
|
||||
@@ -56,11 +56,6 @@ static NSDictionary* UNNotificationResponseToNSDictionary(
|
||||
}
|
||||
|
||||
- (void)applicationWillFinishLaunching:(NSNotification*)notify {
|
||||
// Don't add the "Enter Full Screen" menu item automatically.
|
||||
[[NSUserDefaults standardUserDefaults]
|
||||
setBool:NO
|
||||
forKey:@"NSFullScreenMenuItemEverywhere"];
|
||||
|
||||
[[[NSWorkspace sharedWorkspace] notificationCenter]
|
||||
addObserver:self
|
||||
selector:@selector(willPowerOff:)
|
||||
@@ -114,7 +109,14 @@ static NSDictionary* UNNotificationResponseToNSDictionary(
|
||||
}
|
||||
|
||||
- (NSMenu*)applicationDockMenu:(NSApplication*)sender {
|
||||
return menu_controller_ ? menu_controller_.menu : nil;
|
||||
if (!menu_controller_)
|
||||
return nil;
|
||||
|
||||
// Manually refresh menu state since menuWillOpen: is not called
|
||||
// by macOS for dock menus for some reason before they are displayed.
|
||||
NSMenu* menu = menu_controller_.menu;
|
||||
[menu_controller_ refreshMenuTree:menu];
|
||||
return menu;
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication*)sender openFile:(NSString*)filename {
|
||||
|
||||
@@ -555,7 +555,7 @@ bool NativeWindowViews::IsFocused() const {
|
||||
}
|
||||
|
||||
void NativeWindowViews::Show() {
|
||||
if (is_modal() && NativeWindow::parent())
|
||||
if (is_modal() && NativeWindow::parent() && !widget()->IsVisible())
|
||||
static_cast<NativeWindowViews*>(parent())->IncrementChildModals();
|
||||
|
||||
widget()->native_widget_private()->Show(GetRestoredState(), gfx::Rect());
|
||||
|
||||
@@ -28,7 +28,7 @@ class ElectronMenuModel;
|
||||
NSMenu* __strong menu_;
|
||||
BOOL isMenuOpen_;
|
||||
BOOL useDefaultAccelerator_;
|
||||
base::OnceClosure closeCallback;
|
||||
base::OnceClosure popupCloseCallback;
|
||||
}
|
||||
|
||||
// Builds a NSMenu from the pre-built model (must not be nil). Changes made
|
||||
@@ -36,7 +36,7 @@ class ElectronMenuModel;
|
||||
- (id)initWithModel:(electron::ElectronMenuModel*)model
|
||||
useDefaultAccelerator:(BOOL)use;
|
||||
|
||||
- (void)setCloseCallback:(base::OnceClosure)callback;
|
||||
- (void)setPopupCloseCallback:(base::OnceClosure)callback;
|
||||
|
||||
// Populate current NSMenu with |model|.
|
||||
- (void)populateWithModel:(electron::ElectronMenuModel*)model;
|
||||
@@ -57,6 +57,10 @@ class ElectronMenuModel;
|
||||
// Whether the menu is currently open.
|
||||
- (BOOL)isMenuOpen;
|
||||
|
||||
// Recursively refreshes the menu tree starting from |menu|, applying the
|
||||
// model state (enabled, checked, hidden etc) to each menu item.
|
||||
- (void)refreshMenuTree:(NSMenu*)menu;
|
||||
|
||||
// NSMenuDelegate methods this class implements. Subclasses should call super
|
||||
// if extending the behavior.
|
||||
- (void)menuWillOpen:(NSMenu*)menu;
|
||||
|
||||
@@ -186,8 +186,8 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
model_ = nullptr;
|
||||
}
|
||||
|
||||
- (void)setCloseCallback:(base::OnceClosure)callback {
|
||||
closeCallback = std::move(callback);
|
||||
- (void)setPopupCloseCallback:(base::OnceClosure)callback {
|
||||
popupCloseCallback = std::move(callback);
|
||||
}
|
||||
|
||||
- (void)populateWithModel:(electron::ElectronMenuModel*)model {
|
||||
@@ -221,9 +221,9 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
isMenuOpen_ = NO;
|
||||
if (model_)
|
||||
model_->MenuWillClose();
|
||||
if (!closeCallback.is_null()) {
|
||||
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
|
||||
std::move(closeCallback));
|
||||
if (!popupCloseCallback.is_null()) {
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE, std::move(popupCloseCallback));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -490,8 +490,6 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
: NSControlStateValueOff;
|
||||
}
|
||||
|
||||
// Recursively refreshes the menu tree starting from |menu|, applying the
|
||||
// model state to each menu item.
|
||||
- (void)refreshMenuTree:(NSMenu*)menu {
|
||||
for (NSMenuItem* item in menu.itemArray) {
|
||||
[self applyStateToMenuItem:item];
|
||||
@@ -557,22 +555,43 @@ NSArray* ConvertSharingItemToNS(const SharingItem& item) {
|
||||
|
||||
- (void)menuWillOpen:(NSMenu*)menu {
|
||||
isMenuOpen_ = YES;
|
||||
|
||||
// macOS automatically injects a duplicate "Toggle Full Screen" menu item
|
||||
// when we set menu.delegate on submenus. Remove hidden duplicates.
|
||||
for (NSMenuItem* item in menu.itemArray) {
|
||||
if (item.isHidden && item.action == @selector(toggleFullScreenMode:))
|
||||
[menu removeItem:item];
|
||||
}
|
||||
|
||||
[self refreshMenuTree:menu];
|
||||
if (model_)
|
||||
model_->MenuWillShow();
|
||||
}
|
||||
|
||||
- (void)menuDidClose:(NSMenu*)menu {
|
||||
if (isMenuOpen_) {
|
||||
isMenuOpen_ = NO;
|
||||
if (model_)
|
||||
model_->MenuWillClose();
|
||||
// Post async task so that itemSelected runs before the close callback
|
||||
// deletes the controller from the map which deallocates it
|
||||
if (!closeCallback.is_null()) {
|
||||
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
|
||||
std::move(closeCallback));
|
||||
}
|
||||
// If the menu is already closed, do nothing.
|
||||
if (!isMenuOpen_)
|
||||
return;
|
||||
|
||||
bool has_close_cb = !popupCloseCallback.is_null();
|
||||
|
||||
// There are two scenarios where we should emit menu-did-close:
|
||||
// 1. It's a popup and the top level menu is closed.
|
||||
// 2. It's an application menu, and the current menu's supermenu
|
||||
// is the top-level menu.
|
||||
if (menu != menu_) {
|
||||
if (has_close_cb || menu.supermenu != menu_)
|
||||
return;
|
||||
}
|
||||
|
||||
isMenuOpen_ = NO;
|
||||
if (model_)
|
||||
model_->MenuWillClose();
|
||||
// Post async task so that itemSelected runs before the close callback
|
||||
// deletes the controller from the map which deallocates it.
|
||||
if (has_close_cb) {
|
||||
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
|
||||
std::move(popupCloseCallback));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
|
||||
#include "base/apple/foundation_util.h"
|
||||
#include "base/apple/scoped_cftyperef.h"
|
||||
@@ -30,7 +29,7 @@
|
||||
@interface PopUpButtonHandler : NSObject
|
||||
|
||||
@property(nonatomic, weak) NSSavePanel* savePanel;
|
||||
@property(nonatomic, strong) NSArray* contentTypesList;
|
||||
@property(nonatomic, strong) NSArray* fileTypesList;
|
||||
|
||||
- (instancetype)initWithPanel:(NSSavePanel*)panel
|
||||
andTypesList:(NSArray*)typesList;
|
||||
@@ -41,14 +40,14 @@
|
||||
@implementation PopUpButtonHandler
|
||||
|
||||
@synthesize savePanel;
|
||||
@synthesize contentTypesList;
|
||||
@synthesize fileTypesList;
|
||||
|
||||
- (instancetype)initWithPanel:(NSSavePanel*)panel
|
||||
andTypesList:(NSArray*)typesList {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self setSavePanel:panel];
|
||||
[self setContentTypesList:typesList];
|
||||
[self setFileTypesList:typesList];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -56,19 +55,17 @@
|
||||
- (void)selectFormat:(id)sender {
|
||||
NSPopUpButton* button = (NSPopUpButton*)sender;
|
||||
NSInteger selectedItemIndex = [button indexOfSelectedItem];
|
||||
NSArray* list = [self contentTypesList];
|
||||
NSArray* content_types = [list objectAtIndex:selectedItemIndex];
|
||||
NSArray* list = [self fileTypesList];
|
||||
NSArray* fileTypes = [list objectAtIndex:selectedItemIndex];
|
||||
|
||||
__block BOOL allowAllFiles = NO;
|
||||
[content_types
|
||||
enumerateObjectsUsingBlock:^(UTType* type, NSUInteger idx, BOOL* stop) {
|
||||
if ([[type preferredFilenameExtension] isEqual:@"*"]) {
|
||||
allowAllFiles = YES;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
[[self savePanel] setAllowedContentTypes:allowAllFiles ? @[] : content_types];
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
// If we meet a '*' file extension, we allow all file types.
|
||||
if ([fileTypes count] == 0 || [fileTypes containsObject:@"*"])
|
||||
[[self savePanel] setAllowedFileTypes:nil];
|
||||
else
|
||||
[[self savePanel] setAllowedFileTypes:fileTypes];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -105,7 +102,7 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
|
||||
|
||||
// Create array to keep file types and their name.
|
||||
for (const Filter& filter : filters) {
|
||||
NSMutableOrderedSet* content_types_set =
|
||||
NSMutableOrderedSet* file_type_set =
|
||||
[NSMutableOrderedSet orderedSetWithCapacity:filters.size()];
|
||||
[filter_names addObject:@(filter.first.c_str())];
|
||||
|
||||
@@ -119,46 +116,30 @@ void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) {
|
||||
ext.erase(0, pos + 1);
|
||||
}
|
||||
|
||||
if (ext == "*") {
|
||||
[content_types_set addObject:[UTType typeWithFilenameExtension:@"*"]];
|
||||
break;
|
||||
} else if (ext == "app") {
|
||||
// This handles a bug on macOS where the "app" extension by default
|
||||
// maps to "com.apple.application-file", which is for an Application
|
||||
// file (older single-file carbon based apps), and not modern
|
||||
// Application Bundles (multi file packages as you'd see for all modern
|
||||
// applications).
|
||||
UTType* superType =
|
||||
[UTType typeWithIdentifier:@"com.apple.application-bundle"];
|
||||
if (UTType* utt = [UTType typeWithFilenameExtension:@"app"
|
||||
conformingToType:superType]) {
|
||||
[content_types_set addObject:utt];
|
||||
}
|
||||
} else {
|
||||
if (UTType* utt = [UTType typeWithFilenameExtension:@(ext.c_str())])
|
||||
[content_types_set addObject:utt];
|
||||
}
|
||||
[file_type_set addObject:@(ext.c_str())];
|
||||
}
|
||||
|
||||
[file_types_list addObject:content_types_set];
|
||||
[file_types_list addObject:[file_type_set array]];
|
||||
}
|
||||
|
||||
NSArray* content_types = [file_types_list objectAtIndex:0];
|
||||
// Passing empty array to setAllowedFileTypes will cause exception.
|
||||
NSArray* file_types = nil;
|
||||
NSUInteger count = [file_types_list count];
|
||||
if (count > 0) {
|
||||
file_types = [[file_types_list objectAtIndex:0] allObjects];
|
||||
// If we meet a '*' file extension, we allow all the file types and no
|
||||
// need to set the specified file types.
|
||||
if ([file_types count] == 0 || [file_types containsObject:@"*"])
|
||||
file_types = nil;
|
||||
}
|
||||
|
||||
__block BOOL allowAllFiles = NO;
|
||||
[content_types
|
||||
enumerateObjectsUsingBlock:^(UTType* type, NSUInteger idx, BOOL* stop) {
|
||||
if ([[type preferredFilenameExtension] isEqual:@"*"]) {
|
||||
allowAllFiles = YES;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[dialog setAllowedFileTypes:file_types];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
[dialog setAllowedContentTypes:allowAllFiles ? @[] : content_types];
|
||||
|
||||
// Don't add file format picker.
|
||||
if ([file_types_list count] <= 1)
|
||||
return;
|
||||
if (count <= 1)
|
||||
return; // don't add file format picker
|
||||
|
||||
// Add file format picker.
|
||||
ElectronAccessoryView* accessoryView = [[ElectronAccessoryView alloc]
|
||||
|
||||
@@ -82,11 +82,13 @@ GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap& bitmap) {
|
||||
constexpr GdkColorspace kColorspace = GDK_COLORSPACE_RGB;
|
||||
constexpr gboolean kHasAlpha = true;
|
||||
constexpr int kBitsPerSample = 8;
|
||||
return gdk_pixbuf_new_from_bytes(
|
||||
g_bytes_new(std::data(bytes), std::size(bytes)), kColorspace, kHasAlpha,
|
||||
kBitsPerSample, width, height,
|
||||
GBytes* gbytes = g_bytes_new(std::data(bytes), std::size(bytes));
|
||||
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_bytes(
|
||||
gbytes, kColorspace, kHasAlpha, kBitsPerSample, width, height,
|
||||
gdk_pixbuf_calculate_rowstride(kColorspace, kHasAlpha, kBitsPerSample,
|
||||
width, height));
|
||||
g_bytes_unref(gbytes);
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
} // namespace gtk_util
|
||||
|
||||
@@ -2,7 +2,6 @@ import { app, BrowserWindow, Menu, session, net as electronNet, WebContents, uti
|
||||
|
||||
import { assert, expect } from 'chai';
|
||||
import * as semver from 'semver';
|
||||
import split = require('split')
|
||||
|
||||
import * as cp from 'node:child_process';
|
||||
import { once } from 'node:events';
|
||||
@@ -11,6 +10,7 @@ import * as http from 'node:http';
|
||||
import * as https from 'node:https';
|
||||
import * as net from 'node:net';
|
||||
import * as path from 'node:path';
|
||||
import * as readline from 'node:readline';
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import { promisify } from 'node:util';
|
||||
|
||||
@@ -260,11 +260,11 @@ describe('app module', () => {
|
||||
const firstExited = once(first, 'exit');
|
||||
|
||||
// Wait for the first app to boot.
|
||||
const firstStdoutLines = first.stdout.pipe(split());
|
||||
while ((await once(firstStdoutLines, 'data')).toString() !== 'started') {
|
||||
const firstStdoutLines = readline.createInterface({ input: first.stdout });
|
||||
while ((await once(firstStdoutLines, 'line')).toString() !== 'started') {
|
||||
// wait.
|
||||
}
|
||||
const additionalDataPromise = once(firstStdoutLines, 'data');
|
||||
const additionalDataPromise = once(firstStdoutLines, 'line');
|
||||
|
||||
const secondInstanceArgs = [process.execPath, appPath, ...testArgs.args, '--some-switch', 'some-arg'];
|
||||
const second = cp.spawn(secondInstanceArgs[0], secondInstanceArgs.slice(1));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, net, protocol, screen, webContents, webFrameMain, session, WebContents, WebFrameMain } from 'electron/main';
|
||||
import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, net, protocol, screen, webContents, webFrameMain, session, systemPreferences, WebContents, WebFrameMain } from 'electron/main';
|
||||
|
||||
import { expect } from 'chai';
|
||||
|
||||
@@ -2606,6 +2606,14 @@ describe('BrowserWindow module', () => {
|
||||
expect(accentColor).to.match(/^#[0-9A-F]{6}$/i);
|
||||
});
|
||||
|
||||
it('matches the systemPreferences system color when true', () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.setAccentColor(true);
|
||||
const accentColor = w.getAccentColor() as string;
|
||||
const systemColor = systemPreferences.getAccentColor().slice(0, 6);
|
||||
expect(accentColor).to.equal(`#${systemColor}`);
|
||||
});
|
||||
|
||||
it('returns the correct accent color after multiple changes', () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
|
||||
|
||||
@@ -671,7 +671,7 @@ describe('contextBridge', () => {
|
||||
it('should release the global hold on methods sent across contexts', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
const trackedValues: WeakRef<object>[] = [];
|
||||
require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length }));
|
||||
require('electron').ipcRenderer.on('get-gc-info', (e: any) => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length }));
|
||||
contextBridge.exposeInMainWorld('example', {
|
||||
getFunction: () => () => 123,
|
||||
track: (value: object) => { trackedValues.push(new WeakRef(value)); }
|
||||
@@ -699,7 +699,7 @@ describe('contextBridge', () => {
|
||||
it('should not leak the global hold on methods sent across contexts when reloading a sandboxed renderer', async () => {
|
||||
await makeBindingWindow(() => {
|
||||
const trackedValues: WeakRef<object>[] = [];
|
||||
require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length }));
|
||||
require('electron').ipcRenderer.on('get-gc-info', (e: any) => e.sender.send('gc-info', { trackedValues: trackedValues.filter(value => value.deref()).length }));
|
||||
contextBridge.exposeInMainWorld('example', {
|
||||
getFunction: () => () => 123,
|
||||
track: (value: object) => { trackedValues.push(new WeakRef(value)); }
|
||||
|
||||
@@ -243,7 +243,6 @@ describe('ipc module', () => {
|
||||
await w.webContents.executeJavaScript(`(${function () {
|
||||
try {
|
||||
const buffer = new ArrayBuffer(10);
|
||||
// @ts-expect-error
|
||||
require('electron').ipcRenderer.postMessage('port', '', [buffer]);
|
||||
} catch (e) {
|
||||
require('electron').ipcRenderer.postMessage('port', { error: (e as Error).message });
|
||||
@@ -323,7 +322,7 @@ describe('ipc module', () => {
|
||||
w.loadURL('about:blank');
|
||||
await w.webContents.executeJavaScript(`(${function () {
|
||||
const { ipcRenderer } = require('electron');
|
||||
ipcRenderer.on('port', e => {
|
||||
ipcRenderer.on('port', (e: any) => {
|
||||
const [port] = e.ports;
|
||||
port.start();
|
||||
port.onclose = () => {
|
||||
@@ -480,8 +479,8 @@ describe('ipc module', () => {
|
||||
w.loadURL('about:blank');
|
||||
await w.webContents.executeJavaScript(`(${function () {
|
||||
const { ipcRenderer } = require('electron');
|
||||
ipcRenderer.on('port', ev => {
|
||||
const [port] = ev.ports;
|
||||
ipcRenderer.on('port', (e: any) => {
|
||||
const [port] = e.ports;
|
||||
port.onmessage = () => {
|
||||
ipcRenderer.send('done');
|
||||
};
|
||||
@@ -498,9 +497,9 @@ describe('ipc module', () => {
|
||||
w.loadURL('about:blank');
|
||||
await w.webContents.executeJavaScript(`(${function () {
|
||||
const { ipcRenderer } = require('electron');
|
||||
ipcRenderer.on('port', e1 => {
|
||||
e1.ports[0].onmessage = e2 => {
|
||||
e2.ports[0].onmessage = e3 => {
|
||||
ipcRenderer.on('port', (e1: any) => {
|
||||
e1.ports[0].onmessage = (e2: any) => {
|
||||
e2.ports[0].onmessage = (e3: any) => {
|
||||
ipcRenderer.send('done', e3.data);
|
||||
};
|
||||
};
|
||||
@@ -587,7 +586,7 @@ describe('ipc module', () => {
|
||||
w.loadURL('about:blank');
|
||||
await w.webContents.executeJavaScript(`(${function () {
|
||||
const { ipcRenderer } = require('electron');
|
||||
ipcRenderer.on('foo', (_e, msg) => {
|
||||
ipcRenderer.on('foo', (_e: Event, msg: string) => {
|
||||
ipcRenderer.send('bar', msg);
|
||||
});
|
||||
}})()`);
|
||||
|
||||
@@ -1019,13 +1019,15 @@ describe('webContents module', () => {
|
||||
assert(devToolsWebContents !== null);
|
||||
|
||||
const windowFocused = once(window, 'focus');
|
||||
const devToolsBlurred = once(devToolsWebContents, 'blur');
|
||||
window.focus();
|
||||
await windowFocused;
|
||||
await Promise.all([windowFocused, devToolsBlurred]);
|
||||
|
||||
expect(devToolsWebContents.isFocused()).to.be.false();
|
||||
const devToolsWebContentsFocused = once(devToolsWebContents, 'focus');
|
||||
const windowBlurred = once(window, 'blur');
|
||||
window.webContents.inspectElement(100, 100);
|
||||
await devToolsWebContentsFocused;
|
||||
await Promise.all([devToolsWebContentsFocused, windowBlurred]);
|
||||
|
||||
expect(devToolsWebContents.isFocused()).to.be.true();
|
||||
expect(window.isFocused()).to.be.false();
|
||||
@@ -2639,7 +2641,13 @@ describe('webContents module', () => {
|
||||
const errMsg = Buffer.concat(stderr).toString().trim();
|
||||
console.error(`Error parsing PDF file, exit code was ${code}; signal was ${signal}, error: ${errMsg}`);
|
||||
}
|
||||
return JSON.parse(Buffer.concat(stdout).toString().trim());
|
||||
try {
|
||||
return JSON.parse(Buffer.concat(stdout).toString().trim());
|
||||
} catch (err) {
|
||||
console.error('Error parsing PDF file:', err);
|
||||
console.error('Raw output:', Buffer.concat(stdout).toString().trim());
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
let w: BrowserWindow;
|
||||
|
||||
7
spec/fixtures/native-addon/echo/package.json
vendored
7
spec/fixtures/native-addon/echo/package.json
vendored
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"main": "./lib/echo.js",
|
||||
"name": "@electron-ci/echo",
|
||||
"version": "0.0.1"
|
||||
"version": "0.0.1",
|
||||
"main": "./lib/echo.js",
|
||||
"scripts": {
|
||||
"install": "node-gyp configure && node-gyp build"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"main": "./lib/test-array-buffer.js",
|
||||
"name": "@electron-ci/external-ab",
|
||||
"version": "0.0.1"
|
||||
"version": "0.0.1",
|
||||
"main": "./lib/test-array-buffer.js",
|
||||
"scripts": {
|
||||
"install": "node-gyp configure && node-gyp build"
|
||||
}
|
||||
}
|
||||
|
||||
13
spec/fixtures/native-addon/is-valid-window/package.json
vendored
Normal file
13
spec/fixtures/native-addon/is-valid-window/package.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@electron-ci/is-valid-window",
|
||||
"version": "0.0.5",
|
||||
"main": "./lib/is-valid-window.js",
|
||||
"private": true,
|
||||
"licenses": "MIT",
|
||||
"dependencies": {
|
||||
"nan": "2.x"
|
||||
},
|
||||
"scripts": {
|
||||
"install": "node-gyp configure && node-gyp build"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"main": "./lib/osr-gpu.js",
|
||||
"name": "@electron-ci/osr-gpu",
|
||||
"version": "0.0.1"
|
||||
"version": "0.0.1",
|
||||
"main": "./lib/osr-gpu.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"install": "node-gyp configure && node-gyp build"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"name": "@electron-ci/uv-dlopen",
|
||||
"version": "0.0.1",
|
||||
"main": "index.js"
|
||||
"main": "index.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"install": "node-gyp configure && node-gyp build"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"main": "./lib/is-valid-window.js",
|
||||
"name": "is-valid-window",
|
||||
"version": "0.0.5",
|
||||
"licenses": "Public Domain",
|
||||
"dependencies": {
|
||||
"nan": "2.x"
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,13 @@
|
||||
"node-gyp-install": "node-gyp install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-ci/echo": "*",
|
||||
"@electron-ci/external-ab": "*",
|
||||
"@electron-ci/is-valid-window": "*",
|
||||
"@electron-ci/osr-gpu": "*",
|
||||
"@electron-ci/uv-dlopen": "*",
|
||||
"@electron/fuses": "^1.8.0",
|
||||
"@electron/packager": "^18.3.2",
|
||||
"@types/basic-auth": "^1.1.8",
|
||||
"@types/busboy": "^1.5.4",
|
||||
"@types/chai": "^4.3.19",
|
||||
@@ -15,19 +22,11 @@
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/send": "^0.14.5",
|
||||
"@types/split": "^1.0.5",
|
||||
"@types/sinon": "^9.0.4",
|
||||
"@types/uuid": "^3.4.6",
|
||||
"@types/w3c-web-serial": "^1.0.7",
|
||||
"express": "^4.20.0",
|
||||
"@electron-ci/echo": "file:./fixtures/native-addon/echo",
|
||||
"@electron-ci/is-valid-window": "file:./is-valid-window",
|
||||
"@electron-ci/uv-dlopen": "file:./fixtures/native-addon/uv-dlopen/",
|
||||
"@electron-ci/osr-gpu": "file:./fixtures/native-addon/osr-gpu/",
|
||||
"@electron-ci/external-ab": "file:./fixtures/native-addon/external-ab/",
|
||||
"@electron/fuses": "^1.8.0",
|
||||
"@electron/packager": "^18.3.2",
|
||||
"@types/sinon": "^9.0.4",
|
||||
"@types/ws": "^7.2.0",
|
||||
"abstract-socket": "github:deepak1556/node-abstractsocket#928cc591decd12aff7dad96449da8afc29832c19",
|
||||
"basic-auth": "^2.0.1",
|
||||
"busboy": "^1.6.0",
|
||||
"chai": "^4.2.0",
|
||||
@@ -35,26 +34,20 @@
|
||||
"coffeescript": "^2.4.1",
|
||||
"dbus-native": "github:nornagon/dbus-native#master",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"express": "^4.20.0",
|
||||
"graceful-fs": "^4.1.15",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mocha": "^10.0.0",
|
||||
"mocha-junit-reporter": "^1.18.0",
|
||||
"mocha-multi-reporters": "^1.1.7",
|
||||
"pdfjs-dist": "^4.2.67",
|
||||
"pdfjs-dist": "4.2.67",
|
||||
"ps-list": "^7.0.0",
|
||||
"q": "^1.5.1",
|
||||
"send": "^0.19.0",
|
||||
"sinon": "^9.0.1",
|
||||
"split": "^1.0.1",
|
||||
"uuid": "^3.3.3",
|
||||
"winreg": "1.2.4",
|
||||
"ws": "^7.5.10",
|
||||
"yargs": "^16.0.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"nan": "file:../../third_party/nan",
|
||||
"dbus-native/optimist/minimist": "1.2.7",
|
||||
"dbus-native/xml2js": "0.5.0",
|
||||
"abstract-socket": "github:deepak1556/node-abstractsocket#928cc591decd12aff7dad96449da8afc29832c19"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { expect } from 'chai';
|
||||
import { GitProcess, IGitExecutionOptions, IGitResult } from 'dugite';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import { SpawnSyncReturns } from 'node:child_process';
|
||||
import * as path from 'node:path';
|
||||
|
||||
import * as notes from '../script/release/notes/notes';
|
||||
|
||||
/* Fake a Dugite GitProcess that only returns the specific
|
||||
/* Fake a git spawnSync that only returns the specific
|
||||
commits that we want to test */
|
||||
|
||||
class Commit {
|
||||
@@ -43,10 +43,10 @@ class GitFake {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
exec (args: string[], path: string, options?: IGitExecutionOptions | undefined): Promise<IGitResult> {
|
||||
exec (command: string, args: string[], options?: any): SpawnSyncReturns<string> {
|
||||
let stdout = '';
|
||||
const stderr = '';
|
||||
const exitCode = 0;
|
||||
const status = 0;
|
||||
|
||||
if (args.length === 3 && args[0] === 'merge-base') {
|
||||
// expected form: `git merge-base branchName1 branchName2`
|
||||
@@ -78,10 +78,10 @@ class GitFake {
|
||||
// git branch --all --contains ${ref} --sort version:refname
|
||||
stdout = args[3];
|
||||
} else {
|
||||
console.error('unhandled GitProcess.exec():', args);
|
||||
console.error('unhandled git spawnSync():', args);
|
||||
}
|
||||
|
||||
return Promise.resolve({ exitCode, stdout, stderr });
|
||||
return { status, stdout, stderr, pid: 0, output: [null, stdout, stderr], signal: null };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,8 +118,14 @@ describe('release notes', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
const wrapper = (args: string[], path: string, options?: IGitExecutionOptions | undefined) => gitFake.exec(args, path, options);
|
||||
sandbox.replace(GitProcess, 'exec', wrapper);
|
||||
const wrapper = (command: unknown, args: unknown, options?: unknown) => {
|
||||
if (command === 'git' && Array.isArray(args)) {
|
||||
return gitFake.exec(command as string, args as string[], options);
|
||||
}
|
||||
// Default behavior for non-git commands
|
||||
return { status: 0, stdout: '', stderr: '', pid: 0, output: [null, '', ''], signal: null };
|
||||
};
|
||||
sandbox.stub(require('node:child_process'), 'spawnSync').callsFake(wrapper);
|
||||
|
||||
gitFake.setBranch(oldBranch, [...sharedHistory, oldFix]);
|
||||
});
|
||||
|
||||
@@ -88,8 +88,8 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', function ()
|
||||
await closeWindow(w);
|
||||
});
|
||||
|
||||
// Context menu test can not run on Windows.
|
||||
const shouldRun = process.platform !== 'win32';
|
||||
// Context menu test can not run on Windows or Linux (https://github.com/electron/electron/pull/48657 broke linux).
|
||||
const shouldRun = process.platform !== 'win32' && process.platform !== 'linux';
|
||||
|
||||
ifit(shouldRun)('should detect correctly spelled words as correct', async () => {
|
||||
await w.webContents.executeJavaScript('document.body.querySelector("textarea").value = "typography"');
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { expect } from 'chai';
|
||||
import { GitProcess, IGitExecutionOptions, IGitResult } from 'dugite';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import { SpawnSyncReturns } from 'node:child_process';
|
||||
|
||||
import { ifdescribe } from './lib/spec-helpers';
|
||||
import { nextVersion } from '../script/release/version-bumper';
|
||||
|
||||
@@ -33,10 +34,10 @@ class GitFake {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
exec (args: string[], path: string, options?: IGitExecutionOptions | undefined): Promise<IGitResult> {
|
||||
exec (command: string, args: string[], options?: any): SpawnSyncReturns<string> {
|
||||
let stdout = '';
|
||||
const stderr = '';
|
||||
const exitCode = 0;
|
||||
const status = 0;
|
||||
|
||||
// handle for promoting from current master HEAD
|
||||
let branch = 'stable';
|
||||
@@ -48,7 +49,7 @@ class GitFake {
|
||||
if (!this.branches[branch]) this.setBranch(branch);
|
||||
|
||||
stdout = this.branches[branch].join('\n');
|
||||
return Promise.resolve({ exitCode, stdout, stderr });
|
||||
return { status, stdout, stderr, pid: 0, output: [null, stdout, stderr], signal: null };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,8 +164,14 @@ describe('version-bumper', () => {
|
||||
const gitFake = new GitFake();
|
||||
|
||||
beforeEach(() => {
|
||||
const wrapper = (args: string[], path: string, options?: IGitExecutionOptions | undefined) => gitFake.exec(args, path, options);
|
||||
sandbox.replace(GitProcess, 'exec', wrapper);
|
||||
const wrapper = (command: unknown, args: unknown, options?: unknown) => {
|
||||
if (command === 'git' && Array.isArray(args)) {
|
||||
return gitFake.exec(command as string, args as string[], options);
|
||||
}
|
||||
// Default behavior for non-git commands
|
||||
return { status: 0, stdout: '', stderr: '', pid: 0, output: [null, '', ''], signal: null };
|
||||
};
|
||||
sandbox.stub(require('node:child_process'), 'spawnSync').callsFake(wrapper);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
2843
spec/yarn.lock
2843
spec/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user