Compare commits
131 Commits
v42.0.0-be
...
sort-out-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95daeb01a5 | ||
|
|
c0dfb5cba1 | ||
|
|
9a23d776b4 | ||
|
|
8a493dc501 | ||
|
|
903e65e048 | ||
|
|
fef2fd2941 | ||
|
|
4d8fd31e5f | ||
|
|
96486a4102 | ||
|
|
3f8238b92c | ||
|
|
64c5440eec | ||
|
|
30cf60a935 | ||
|
|
ec30e4cdae | ||
|
|
40033db422 | ||
|
|
c3d441cf7d | ||
|
|
14583d22e6 | ||
|
|
68bfe49120 | ||
|
|
2c6332a7d6 | ||
|
|
ddc1bd9553 | ||
|
|
12109371d3 | ||
|
|
69891d04bf | ||
|
|
188813e206 | ||
|
|
8b768b8211 | ||
|
|
82b97ddf5b | ||
|
|
16f408a502 | ||
|
|
246aa63910 | ||
|
|
230f02faf2 | ||
|
|
1362d7b94d | ||
|
|
877fe479b5 | ||
|
|
f41438ff73 | ||
|
|
c6e201c965 | ||
|
|
156a4e610c | ||
|
|
81f8fc1880 | ||
|
|
343d6e5f3f | ||
|
|
e7080835f1 | ||
|
|
7c1a6f7e95 | ||
|
|
22ac2b13fb | ||
|
|
a8acb96608 | ||
|
|
97773bf50c | ||
|
|
1e0846749b | ||
|
|
8cd766ff53 | ||
|
|
e5b20a11d2 | ||
|
|
e0bd4ffc39 | ||
|
|
bbbcae1a12 | ||
|
|
3e1666be08 | ||
|
|
a06b49aca1 | ||
|
|
d318893aa0 | ||
|
|
f133e2f775 | ||
|
|
b44b9ba316 | ||
|
|
d5e4429724 | ||
|
|
8f11366f50 | ||
|
|
0dabcfdec4 | ||
|
|
b4460a05da | ||
|
|
0a1ea1f028 | ||
|
|
b41ec6586a | ||
|
|
4eff8f20f2 | ||
|
|
8cb61e8b9b | ||
|
|
b9731b89dc | ||
|
|
d64e1146dd | ||
|
|
ae6b219545 | ||
|
|
c44d60cfe4 | ||
|
|
9928c7d828 | ||
|
|
f5bc6f7949 | ||
|
|
a839fb94aa | ||
|
|
2e2c56adde | ||
|
|
678adeaf7c | ||
|
|
1d14694dec | ||
|
|
a48f03fb8d | ||
|
|
f6b43cb0ef | ||
|
|
7451d560ba | ||
|
|
27edd6e21c | ||
|
|
ec3a18d438 | ||
|
|
02d4101ca3 | ||
|
|
fdaba4c6b0 | ||
|
|
542ff828ab | ||
|
|
4371a4dceb | ||
|
|
60f4b07723 | ||
|
|
f282bec8ef | ||
|
|
cef388de3d | ||
|
|
1828690467 | ||
|
|
f4c4cd14ac | ||
|
|
3db3996102 | ||
|
|
dbcf0fb5f0 | ||
|
|
29750dda08 | ||
|
|
6df6ec5f09 | ||
|
|
882a6b2cf9 | ||
|
|
b8fa540fd3 | ||
|
|
dee8f5a0ff | ||
|
|
32f8e2ce45 | ||
|
|
4e6324e00b | ||
|
|
7f21d31498 | ||
|
|
639d3b99b7 | ||
|
|
0c7bde54d4 | ||
|
|
8a0c20431c | ||
|
|
72797d7b42 | ||
|
|
9ccc752a43 | ||
|
|
6993eb3c78 | ||
|
|
d9649f9e16 | ||
|
|
5b2b9cdeff | ||
|
|
e31a95b15f | ||
|
|
1ad832a4c1 | ||
|
|
8e077a09f3 | ||
|
|
95f0d8156b | ||
|
|
b881f86c8f | ||
|
|
5959ecc3ee | ||
|
|
a6a44692dc | ||
|
|
12ea28c23e | ||
|
|
ade684dc35 | ||
|
|
4ec6923898 | ||
|
|
e86cd9da96 | ||
|
|
d6db1a27af | ||
|
|
76331f0564 | ||
|
|
7cb6a737a9 | ||
|
|
3659b97563 | ||
|
|
7d72eb809e | ||
|
|
8ba0ae7fa8 | ||
|
|
36350d78d0 | ||
|
|
9b80324d7f | ||
|
|
a549c56faa | ||
|
|
958278c273 | ||
|
|
b7e9bbed0c | ||
|
|
eec3fe967e | ||
|
|
01714757e3 | ||
|
|
ffad67222d | ||
|
|
078586fab0 | ||
|
|
a561dd97a6 | ||
|
|
b9cbcde600 | ||
|
|
36b0709942 | ||
|
|
cf84efbbb9 | ||
|
|
58cd1aba10 | ||
|
|
26a3a8679a | ||
|
|
a1e4c260ea |
@@ -11,7 +11,6 @@
|
||||
"Bash(e patches:*)",
|
||||
"Bash(e sync:*)",
|
||||
"Skill(electron-chromium-upgrade)",
|
||||
"Skill(electron-node-upgrade)",
|
||||
"Read(*)",
|
||||
"Bash(echo:*)",
|
||||
"Bash(e build:*)",
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
---
|
||||
name: electron-node-upgrade
|
||||
description: Guide for performing Node.js version upgrades in the Electron project. Use when working on the roller/node/main branch to fix patch conflicts during `e sync --3`. Covers the patch application workflow, conflict resolution, analyzing upstream Node.js changes, and proper commit formatting for patch fixes.
|
||||
---
|
||||
|
||||
# Electron Node.js Upgrade: Phase One
|
||||
|
||||
## Summary
|
||||
|
||||
Run `e sync --3` repeatedly, fixing patch conflicts as they arise, until it succeeds. Then export patches and commit changes atomically.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
Phase One is complete when:
|
||||
- `e sync --3` exits with code 0 (no patch failures)
|
||||
- All changes are committed per the commit guidelines
|
||||
|
||||
Do not stop until these criteria are met.
|
||||
|
||||
**CRITICAL** Do not delete or skip patches unless 100% certain the patch is no longer needed. For major version upgrades, patches that shim deprecated V8 APIs or backport upstream changes are often deletable because the new Node.js version already incorporates them — but verify before removing. Complicated conflicts or hard to resolve issues should be presented to the user after you have exhausted all other options. Do not delete the patch just because you can't solve it.
|
||||
|
||||
**CRITICAL** Never use `git am --skip` and then manually recreate a patch by making a new commit. This destroys the original patch's authorship, commit message, and position in the series. If `git am --continue` reports "No changes", investigate why — the changes were likely absorbed by a prior conflict resolution's 3-way merge. Present this situation to the user rather than skipping and recreating.
|
||||
|
||||
## Context
|
||||
|
||||
The `roller/node/main` branch is created by automation to update Electron's Node.js dependency version in `DEPS`. No work has been done to handle breaking changes between the old and new versions.
|
||||
|
||||
There are two types of Node.js version updates:
|
||||
- **Bumps** (patch/minor): Automated by `electron-roller[bot]` with commit title `chore: bump node to v{version}`. Trivial patch index updates are handled automatically by `patchup[bot]`. These often land cleanly, but may require manual patch fixes.
|
||||
- **Major upgrades** (e.g., v22 → v24): Manual, large PRs with commit title `chore: upgrade Node.js to v{X}.{Y}.{Z}`. These typically involve deleting obsolete patches, adapting many others, and updating `@types/node` in `package.json`.
|
||||
|
||||
**Key directories:**
|
||||
- Current directory: Electron repo (always run `e` commands here)
|
||||
- `../third_party/electron_node`: Node.js repo (where patches apply)
|
||||
- `patches/node/`: Patch files for Node.js
|
||||
- `docs/development/patches.md`: Patch system documentation
|
||||
|
||||
## Pre-flight Checks
|
||||
|
||||
Run these once at the start of each upgrade session:
|
||||
|
||||
1. **Clear rerere cache** (if enabled): `git rerere clear` in both the electron and `../third_party/electron_node` repos. Stale recorded resolutions from a prior attempt can silently apply wrong merges.
|
||||
2. **Ensure pre-commit hooks are installed**: Check that `.git/hooks/pre-commit` exists. If not, run `yarn husky` to install it. The hook runs `lint-staged` which handles clang-format for C++ files.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Run `e sync --3` (the `--3` flag enables 3-way merge, always required)
|
||||
2. If succeeds → skip to step 5
|
||||
3. If patch fails:
|
||||
- Identify target repo and patch from error output
|
||||
- Analyze failure (see references/patch-analysis.md)
|
||||
- Fix conflict in `../third_party/electron_node` working directory
|
||||
- Run `git am --continue` in `../third_party/electron_node`
|
||||
- Repeat until all patches for that repo apply
|
||||
- IMPORTANT: Once `git am --continue` succeeds you MUST run `e patches node` to export fixes
|
||||
- Return to step 1
|
||||
4. When `e sync --3` succeeds, run `e patches all`
|
||||
5. **Read `references/phase-one-commit-guidelines.md` NOW**, then commit changes following those instructions exactly.
|
||||
|
||||
## Commands Reference
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `e sync --3` | Clone deps and apply patches with 3-way merge |
|
||||
| `git am --continue` | Continue after resolving conflict (run in node repo) |
|
||||
| `e patches node` | Export commits from node repo to patch files |
|
||||
| `e patches all` | Export all patches from all targets |
|
||||
| `e patches node --commit-updates` | Export patches and auto-commit trivial changes |
|
||||
| `e patches --list-targets` | List targets and config paths |
|
||||
|
||||
## Patch System Mental Model
|
||||
|
||||
```
|
||||
patches/node/*.patch → [e sync --3] → ../third_party/electron_node commits
|
||||
← [e patches] ←
|
||||
```
|
||||
|
||||
## When to Edit Patches
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| During active `git am` conflict | Fix in node repo, then `git am --continue` |
|
||||
| Modifying patch outside conflict | Edit `.patch` file directly |
|
||||
| Creating new patch (rare, avoid) | Commit in node repo, then `e patches node` |
|
||||
|
||||
Fix existing patches 99% of the time rather than creating new ones.
|
||||
|
||||
## Patch Fixing Rules
|
||||
|
||||
1. **Preserve authorship**: Keep original author in TODO comments (from patch `From:` field)
|
||||
2. **Never change TODO assignees**: `TODO(name)` must retain original name
|
||||
3. **Update descriptions**: If upstream changed APIs or macros, update patch commit message to reflect current state
|
||||
4. **Never skip-and-recreate a patch**: If `git am --continue` says "No changes — did you forget to use 'git add'?", do NOT run `git am --skip` and create a replacement commit. The patch's changes were already absorbed by a prior 3-way merge resolution. This means an earlier conflict resolution pulled in too many changes. Present the situation to the user for guidance — the correct fix may require re-doing an earlier resolution more carefully to keep each patch's changes separate.
|
||||
|
||||
# Electron Node.js Upgrade: Phase Two
|
||||
|
||||
## Summary
|
||||
|
||||
Run `e build -k 999 -- --quiet` repeatedly, fixing build issues as they arise, until it succeeds. Then run `e start --version` to validate Electron launches and commit changes atomically.
|
||||
|
||||
Run Phase Two immediately after Phase One is complete.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
Phase Two is complete when:
|
||||
- `e build -k 999 -- --quiet` exits with code 0 (no build failures)
|
||||
- `e start --version` has been run to check Electron launches
|
||||
- All changes are committed per the commit guidelines
|
||||
|
||||
Do not stop until these criteria are met. Do not delete code or features, never comment out code in order to take short cut. Make all existing code, logic and intention work.
|
||||
|
||||
## Context
|
||||
|
||||
The `roller/node/main` branch is created by automation to update Electron's Node.js dependency version in `DEPS`. No work has been done to handle breaking changes between the old and new versions. Node.js APIs (especially internal V8 integration, OpenSSL/BoringSSL compatibility, and build system files) frequently change between versions. In every case the code in Electron must be updated to account for the change in Node.js, strongly avoid making changes to the code in Node.js to fix Electron's build.
|
||||
|
||||
**Key directories:**
|
||||
- Current directory: Electron repo (always run `e` commands here)
|
||||
- `../third_party/electron_node`: Node.js repo (do not touch this code to fix build issues, just read it to obtain context)
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Run `e build -k 999 -- --quiet` (the `--quiet` flag suppresses per-target status lines, showing only errors and the final result)
|
||||
2. If succeeds → skip to step 6
|
||||
3. If build fails:
|
||||
- Identify underlying file in "electron" from the compilation error message
|
||||
- Analyze failure
|
||||
- Fix build issue by adapting Electron's code for the change in Node.js
|
||||
- Run `e build -t {target_that_failed}.o` to build just the failed target we were specifically fixing
|
||||
- You can identify the target_that_failed from the failure line in the build log. E.g. `FAILED: 2e506007-8d5d-4f38-bdd1-b5cd77999a77 "./obj/electron/shell/browser/api/electron_api_utility_process.o" CXX obj/electron/shell/browser/api/electron_api_utility_process.o` the target name is `obj/electron/shell/browser/api/electron_api_utility_process.o`
|
||||
- **Read `references/phase-two-commit-guidelines.md` NOW**, then commit changes following those instructions exactly.
|
||||
- Return to step 1
|
||||
4. **CRITICAL**: After ANY commit (especially patch commits), immediately run `git status` in the electron repo
|
||||
- Look for other modified `.patch` files that only have index/hunk header changes
|
||||
- These are dependent patches affected by your fix
|
||||
- Commit them immediately with: `git commit -am "chore: update patches (trivial only)"`
|
||||
5. Return to step 1
|
||||
6. When `e build` succeeds, run `e start --version`
|
||||
7. Check if you have any pending changes in the Node.js repo by running `git status` in `../third_party/electron_node`
|
||||
- If you have changes follow the instructions below in "A. Patch Fixes" to correctly commit those modifications into the appropriate patch file
|
||||
|
||||
## Commands Reference
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `e build -k 999 -- --quiet` | Build Electron, continue on errors, suppress status lines |
|
||||
| `e build -t {target}.o` | Build just one specific target to verify a fix |
|
||||
| `e start --version` | Validate Electron launches after successful build |
|
||||
|
||||
## Two Types of Build Fixes
|
||||
|
||||
### A. Patch Fixes (for files in patched Node.js files)
|
||||
|
||||
When the error is in a file that Electron patches (check with `grep -l "filename" patches/node/*.patch`):
|
||||
|
||||
1. Edit the file in the Node.js source tree (`../third_party/electron_node/...`)
|
||||
2. Create a fixup commit targeting the original patch commit:
|
||||
```bash
|
||||
cd ../third_party/electron_node
|
||||
git add <modified-file>
|
||||
git commit --fixup=<original-patch-commit-hash>
|
||||
GIT_SEQUENCE_EDITOR=: git rebase --autosquash --autostash -i <commit>^
|
||||
```
|
||||
3. Export the updated patch: `e patches node`
|
||||
4. Commit the updated patch file following `references/phase-one-commit-guidelines.md`.
|
||||
|
||||
To find the original patch commit to fixup: `git log --oneline | grep -i "keyword from patch name"`
|
||||
|
||||
The base commit for rebase is the Node.js commit before patches were applied. Find it by checking the `refs/patches/upstream-head` ref.
|
||||
|
||||
### B. Electron Code Fixes (for files in shell/, electron/, etc.)
|
||||
|
||||
When the error is in Electron's own source code:
|
||||
|
||||
1. Edit files directly in the electron repo
|
||||
2. Commit directly (no patch export needed)
|
||||
|
||||
# Critical: Read Before Committing
|
||||
|
||||
- Before ANY Phase One commits: Read `references/phase-one-commit-guidelines.md`
|
||||
- Before ANY Phase Two commits: Read `references/phase-two-commit-guidelines.md`
|
||||
|
||||
# High-Churn Patches
|
||||
|
||||
These patches consistently require the most work during Node.js upgrades:
|
||||
|
||||
- **`fix_handle_boringssl_and_openssl_incompatibilities.patch`** — Electron uses BoringSSL (via Chromium) while Node.js expects OpenSSL. This patch is large and complex, and upstream OpenSSL API changes frequently break it.
|
||||
- **`fix_crypto_tests_to_run_with_bssl.patch`** — Companion to the above; adapts Node.js crypto tests for BoringSSL. Can grow significantly during major upgrades.
|
||||
- **`support_v8_sandboxed_pointers.patch`** — V8 sandbox pointer support requires careful adaptation when V8 APIs change.
|
||||
- **`build_add_gn_build_files.patch`** — The GN build file patch is large and touches many build targets. Upstream build system changes frequently conflict.
|
||||
|
||||
# Major Version Upgrades
|
||||
|
||||
Major Node.js version transitions (e.g., v22 → v24) are significantly more involved than patch bumps:
|
||||
|
||||
1. **Expect patch deletions.** Electron uses Chromium's V8, which is often ahead of the V8 version bundled in Node.js. Many patches exist to bridge this gap — shimming newer V8 APIs that Chromium's V8 has but Node.js' older V8 doesn't. When Node.js bumps to a newer major version, its V8 catches up to Chromium's, and those bridge patches can be deleted. In the v22 → v24 upgrade, 17 patches were deleted for this reason.
|
||||
2. **Update `@types/node`** in `package.json` to match the new major version.
|
||||
3. **Post-upgrade regressions are expected.** Even after the upgrade lands, follow-up fix PRs for edge cases (ESM path handling, certificate loading, platform-specific issues) are normal.
|
||||
|
||||
# Skill Directory Structure
|
||||
This skill has additional reference files in `references/`:
|
||||
- patch-analysis.md - How to analyze patch failures
|
||||
- phase-one-commit-guidelines.md - Commit format for Phase One
|
||||
- phase-two-commit-guidelines.md - Commit format for Phase Two
|
||||
|
||||
Read these when referenced in the workflow steps.
|
||||
@@ -1,112 +0,0 @@
|
||||
# Analyzing Patch Failures
|
||||
|
||||
## Investigation Steps
|
||||
|
||||
1. **Read the patch file** at `patches/node/{patch_name}.patch`
|
||||
|
||||
2. **Examine current state** of the file in the Node.js repo at mentioned line numbers
|
||||
|
||||
3. **Check recent upstream changes:**
|
||||
```bash
|
||||
cd ../third_party/electron_node
|
||||
git log --oneline -10 -- {file}
|
||||
```
|
||||
|
||||
4. **Find Node.js PR** in commit messages:
|
||||
```
|
||||
PR-URL: https://github.com/nodejs/node/pull/{PR_NUMBER}
|
||||
```
|
||||
|
||||
## Critical: Resolve by Intent, Not by Mechanical Merge
|
||||
|
||||
When resolving a patch conflict, do NOT blindly preserve the patch's old code. Instead:
|
||||
|
||||
1. **Understand the upstream commit's full scope** — not just the conflicting hunk.
|
||||
Run `git show <commit> --stat` and read diffs for all affected files.
|
||||
Upstream may have removed structs, members, or methods that the patch
|
||||
references in other hunks or files.
|
||||
|
||||
2. **Re-read the patch commit message** to understand its *intent* — what
|
||||
behavior does it need to preserve or add?
|
||||
|
||||
3. **Implement the intent against the new upstream code.** If the patch's
|
||||
purpose is "add BoringSSL compatibility", add only the compatibility
|
||||
layer — don't also restore old code that upstream separately removed.
|
||||
|
||||
### Lesson: Upstream Removals Break Patch References
|
||||
|
||||
- **Trigger:** Patch conflict involves an upstream refactor (not just context drift)
|
||||
- **Strategy:** After identifying the upstream commit, check its full diff for
|
||||
removed types, members, and methods. If the patch's old code references
|
||||
something removed, the resolution must use the new upstream mechanism.
|
||||
|
||||
### Lesson: Separate Patch Purpose from Patch Implementation
|
||||
|
||||
- **Trigger:** Conflict between "upstream simplified code" vs "patch has older code"
|
||||
- **Strategy:** Identify the *minimal* change the patch needs. If the patch
|
||||
wraps code in a conditional, only add the conditional — don't restore old
|
||||
code that was inside the conditional but was separately cleaned up upstream.
|
||||
|
||||
### Lesson: Finish the Adaptation at Conflict Time
|
||||
|
||||
- **Trigger:** A patch conflict involves an upstream API removal or replacement
|
||||
- **Strategy:** When resolving the conflict, fully adapt the patch to use the
|
||||
new API in the same commit. Don't remove the old code and leave behind stale
|
||||
references that will "be fixed in Phase Two." Each patch fix commit should be
|
||||
a complete resolution.
|
||||
|
||||
## Common Failure Patterns
|
||||
|
||||
| Pattern | Cause | Solution |
|
||||
|---------|-------|----------|
|
||||
| Context lines don't match | Surrounding code changed | Update context in patch |
|
||||
| File not found | File renamed/moved | Update patch target path |
|
||||
| Function not found | Refactored upstream | Find new function name |
|
||||
| OpenSSL → BoringSSL mismatch | Crypto API change | Update to BoringSSL-compatible API |
|
||||
| GYP/GN build change | Build system refactor | Adapt build patch to new structure |
|
||||
| Deleted code | Feature removed | Verify patch still needed |
|
||||
| V8 API bridge patch conflicts | Node.js caught up to Chromium's V8 | Patch may be deletable — verify the API is now in Node.js' V8 natively |
|
||||
|
||||
## Using Git Blame
|
||||
|
||||
To find the commit that changed specific lines:
|
||||
|
||||
```bash
|
||||
cd ../third_party/electron_node
|
||||
git blame -L {start},{end} -- {file}
|
||||
git log -1 {commit_sha} # Look for PR-URL: line
|
||||
```
|
||||
|
||||
## Verifying Patch Necessity
|
||||
|
||||
Before deleting a patch, verify:
|
||||
1. The patched functionality was intentionally removed upstream
|
||||
2. Electron doesn't need the patch for other reasons
|
||||
3. No other code depends on the patched behavior
|
||||
|
||||
**V8 bridge patches:** Electron uses Chromium's V8, which is often ahead of the V8 bundled in Node.js. Many patches exist to bridge this version gap — adapting Node.js code to work with newer V8 APIs that Chromium's V8 exposes. During major Node.js upgrades, Node.js' V8 catches up to Chromium's, and these bridge patches often become unnecessary. Check whether the API the patch shims is now available natively in the new Node.js version's V8.
|
||||
|
||||
When in doubt, keep the patch and adapt it.
|
||||
|
||||
## Phase Two: Build-Time Patch Issues
|
||||
|
||||
Sometimes patches that applied successfully in Phase One cause build errors in Phase Two. This can happen when:
|
||||
|
||||
1. **Incomplete types**: A patch disables a header include, but new upstream code uses the type
|
||||
2. **Missing members**: A patch modifies a class, but upstream added new code referencing the original
|
||||
|
||||
### Finding Which Patch Affects a File
|
||||
|
||||
```bash
|
||||
grep -l "filename.cc" patches/node/*.patch
|
||||
```
|
||||
|
||||
### Matching Existing Patch Patterns
|
||||
|
||||
When fixing build errors in patched files, examine the existing patch to understand its style:
|
||||
- Does it use `#if 0` / `#endif` guards?
|
||||
- Does it use `#if BUILDFLAG(...)` conditionals?
|
||||
- Does it use `#ifndef` / `#ifdef` guards for BoringSSL vs OpenSSL?
|
||||
- What's the pattern for disabled functionality?
|
||||
|
||||
Apply fixes consistent with the existing patch style.
|
||||
@@ -1,111 +0,0 @@
|
||||
# Phase One Commit Guidelines
|
||||
|
||||
Only follow these instructions if there are uncommitted changes to `patches/` after Phase One succeeds.
|
||||
|
||||
Ignore other instructions about making commit messages, our guidelines are CRITICALLY IMPORTANT and must be followed.
|
||||
|
||||
## Each Commit Must Be Complete
|
||||
|
||||
When resolving a patch conflict, fully adapt the patch to the new upstream code in the same commit. If the upstream change removes an API the patch uses, update the patch to use the replacement API now — don't leave stale references knowing they'll need fixing later. The goal is that each commit represents a finished resolution, not a partial one that defers known work to a future phase.
|
||||
|
||||
## Commit Message Style
|
||||
|
||||
**Titles** follow the 60/80-character guideline: simple changes fit within 60 characters, otherwise the limit is 80 characters.
|
||||
|
||||
Always include a `Co-Authored-By` trailer identifying the AI model that assisted (e.g., `Co-Authored-By: <AI model attribution>`).
|
||||
|
||||
### Patch conflict fixes
|
||||
|
||||
Use `fix(patch):` prefix. The title should name the upstream change, not your response to it:
|
||||
|
||||
```
|
||||
fix(patch): {topic headline}
|
||||
|
||||
Ref: {Node.js commit or issue link}
|
||||
|
||||
Co-Authored-By: <AI model attribution>
|
||||
```
|
||||
|
||||
Only add a description body if it provides clarity beyond the title. For straightforward context drift or simple API renames, the title + Ref is sufficient.
|
||||
|
||||
Examples:
|
||||
- `fix(patch): stop using v8::PropertyCallbackInfo<T>::This()`
|
||||
- `fix(patch): BoringSSL and OpenSSL incompatibilities`
|
||||
- `fix(patch): refactor module_wrap.cc FixedArray::Get params`
|
||||
|
||||
### Upstreamed patch removal
|
||||
|
||||
When patches are no longer needed (applied cleanly with "already applied" or confirmed upstreamed), group ALL removals into a single commit:
|
||||
|
||||
```
|
||||
chore: remove upstreamed patch
|
||||
```
|
||||
|
||||
or (if multiple):
|
||||
|
||||
```
|
||||
chore: remove upstreamed patches
|
||||
```
|
||||
|
||||
Most Node.js patches in Electron are Electron-authored (no upstream `PR-URL:`). If the patch originated from an upstream Node.js PR, no extra `Ref:` is needed. Otherwise, add a `Ref:` pointing to the relevant Node.js issue or commit if one exists.
|
||||
|
||||
### Trivial patch updates
|
||||
|
||||
After all fix commits, stage remaining trivial changes (index, line numbers, context only):
|
||||
|
||||
```bash
|
||||
git add patches
|
||||
git commit -m "chore: update patches (trivial only)"
|
||||
```
|
||||
|
||||
**Conflict resolution can produce trivial results.** A `git am` conflict doesn't always mean the patch content changed — context drift alone can cause a conflict. After resolving and exporting, inspect the patch diff: if only index hashes, line numbers, and context lines changed (not the patch's own `+`/`-` lines), it's trivial and belongs here, not in a `fix(patch):` commit.
|
||||
|
||||
## Atomic Commits
|
||||
|
||||
Each patch conflict fix gets its own commit with its own Ref.
|
||||
|
||||
IMPORTANT: Try really hard to find the PR or commit reference per the instructions below. Each change you made should in theory have been in response to a change made in Node.js that you identified or can identify. Try for a while to identify and include the ref in the commit message. Do not give up easily.
|
||||
|
||||
## Finding Commit/Issue References
|
||||
|
||||
Use `git log` or `git blame` on Node.js source files in `../third_party/electron_node`. Look for:
|
||||
|
||||
```
|
||||
PR-URL: https://github.com/nodejs/node/pull/XXXXX
|
||||
```
|
||||
|
||||
or issue references in the patch itself:
|
||||
|
||||
```
|
||||
Refs: https://github.com/nodejs/node/issues/XXXXX
|
||||
```
|
||||
|
||||
Note: Most Node.js patches in Electron are Electron-authored and won't have upstream references. In that case, check `git log` in the Node.js repo to find which upstream commit caused the conflict.
|
||||
|
||||
If no reference found after searching: `Ref: Unable to locate reference`
|
||||
|
||||
## Example Commits
|
||||
|
||||
### Patch conflict fix (simple — title is sufficient)
|
||||
|
||||
```
|
||||
fix(patch): stop using v8::PropertyCallbackInfo<T>::This()
|
||||
|
||||
Ref: https://github.com/nodejs/node/issues/60616
|
||||
|
||||
Co-Authored-By: <AI model attribution>
|
||||
```
|
||||
|
||||
### Patch conflict fix (complex — description adds value)
|
||||
|
||||
```
|
||||
fix(patch): BoringSSL and OpenSSL incompatibilities
|
||||
|
||||
Upstream updated OpenSSL APIs that diverge from BoringSSL. Adapted
|
||||
the compatibility shims in crypto patches to use the BoringSSL
|
||||
equivalents.
|
||||
|
||||
Ref: Unable to locate reference
|
||||
|
||||
Co-Authored-By: <AI model attribution>
|
||||
```
|
||||
@@ -1,96 +0,0 @@
|
||||
# Phase Two Commit Guidelines
|
||||
|
||||
Only follow these instructions if there are uncommitted changes in the Electron repo after any fixes are made during Phase Two that result a target that was failing, successfully building.
|
||||
|
||||
Ignore other instructions about making commit messages, our guidelines are CRITICALLY IMPORTANT and must be followed.
|
||||
|
||||
## Commit Message Style
|
||||
|
||||
**Titles** follow the 60/80-character guideline: simple changes fit within 60 characters, otherwise the limit is 80 characters. Exception: upstream Node.js PR titles are used verbatim even if longer.
|
||||
|
||||
Always include a `Co-Authored-By` trailer identifying the AI model that assisted (e.g., `Co-Authored-By: <AI model attribution>`).
|
||||
|
||||
## Two Commit Types
|
||||
|
||||
### For Electron Source Changes (shell/, electron/, etc.)
|
||||
|
||||
When the upstream Node.js commit has a `PR-URL:`:
|
||||
|
||||
```
|
||||
node#{PR-Number}: {upstream PR's original title}
|
||||
|
||||
Ref: {Node.js PR link}
|
||||
|
||||
Co-Authored-By: <AI model attribution>
|
||||
```
|
||||
|
||||
When there is no `PR-URL:` but there is an issue reference or commit:
|
||||
|
||||
```
|
||||
fix: {description of the adaptation}
|
||||
|
||||
Ref: {Node.js issue or commit link}
|
||||
|
||||
Co-Authored-By: <AI model attribution>
|
||||
```
|
||||
|
||||
Use the **upstream commit's original title** when available — do not paraphrase or rewrite it. To find it: check the commit message in `../third_party/electron_node` for `PR-URL:` or `Refs:` lines.
|
||||
|
||||
Only add a description body if it provides clarity beyond what the title already says (e.g., when Electron's adaptation is non-obvious). For simple renames, method additions, or straightforward API updates, the title + Ref link is sufficient.
|
||||
|
||||
Each change should have its own commit and its own Ref. Logically group into commits that make sense rather than one giant commit. You may include multiple "Ref" links if required.
|
||||
|
||||
IMPORTANT: Try really hard to find a reference. Each change you made should in theory have been in response to a change in Node.js. Check `git log` and `git blame` in the Node.js repo. Do not give up easily.
|
||||
|
||||
### For Patch Updates (patches/node/*.patch)
|
||||
|
||||
Use the same fixup workflow as Phase One and follow `references/phase-one-commit-guidelines.md` for the commit message format (`fix(patch):` prefix, topic style).
|
||||
|
||||
## Dependent Patch Header Updates
|
||||
|
||||
After any patch modification, check for other affected patches:
|
||||
|
||||
```bash
|
||||
git status
|
||||
# If other .patch files show as modified with only index, line number, and context changes:
|
||||
git add patches/
|
||||
git commit -m "chore: update patches (trivial only)"
|
||||
```
|
||||
|
||||
## Finding References
|
||||
|
||||
Use `git log` or `git blame` on Node.js source files in `../third_party/electron_node`. Look for:
|
||||
|
||||
```
|
||||
PR-URL: https://github.com/nodejs/node/pull/XXXXX
|
||||
Refs: https://github.com/nodejs/node/issues/XXXXX
|
||||
```
|
||||
|
||||
Note: Many Node.js patches in Electron are Electron-authored and won't have upstream `PR-URL:` lines. Check the patch's own commit message for `Refs:` lines, or use `git log` in the Node.js repo to find which upstream commit caused the build break.
|
||||
|
||||
If no reference found after searching: `Ref: Unable to locate reference`
|
||||
|
||||
## Example Commits
|
||||
|
||||
### Electron Source Fix (with upstream PR)
|
||||
|
||||
```
|
||||
node#61898: src: stop using v8::PropertyCallbackInfo<T>::This()
|
||||
|
||||
Ref: https://github.com/nodejs/node/pull/61898
|
||||
|
||||
Co-Authored-By: <AI model attribution>
|
||||
```
|
||||
|
||||
### Electron Source Fix (with issue reference, no PR)
|
||||
|
||||
```
|
||||
fix: adapt to v8::PropertyCallbackInfo<T>::This() removal
|
||||
|
||||
Updated NodeBindings to use HolderV2() after upstream Node.js
|
||||
stopped using the deprecated This() API.
|
||||
|
||||
Ref: https://github.com/nodejs/node/issues/60616
|
||||
|
||||
Co-Authored-By: <AI model attribution>
|
||||
```
|
||||
1
.github/CODEOWNERS
vendored
@@ -19,6 +19,7 @@ DEPS @electron/wg-upgrades
|
||||
/lib/renderer/security-warnings.ts @electron/wg-security
|
||||
|
||||
# Infra WG
|
||||
/.claude/ @electron/wg-infra
|
||||
/.github/actions/ @electron/wg-infra
|
||||
/.github/workflows/*-publish.yml @electron/wg-infra
|
||||
/.github/workflows/build.yml @electron/wg-infra
|
||||
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -5,6 +5,8 @@ Thank you for your Pull Request. Please provide a description above and review
|
||||
the requirements below.
|
||||
|
||||
Contributors guide: https://github.com/electron/electron/blob/main/CONTRIBUTING.md
|
||||
|
||||
NOTE: PRS submitted without this template will be automatically closed.
|
||||
-->
|
||||
|
||||
#### Checklist
|
||||
|
||||
48
.github/actions/build-electron/action.yml
vendored
@@ -47,6 +47,16 @@ runs:
|
||||
- name: Add Clang problem matcher
|
||||
shell: bash
|
||||
run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json"
|
||||
- name: Download previous object checksums
|
||||
shell: bash
|
||||
if: ${{ (github.event_name == 'push' || github.event_name == 'pull_request') && inputs.is-asan != 'true' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
ARTIFACT_NAME: object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json
|
||||
SEARCH_BRANCH: ${{ case(github.event_name == 'push', github.ref_name, github.event.pull_request.base.ref) }}
|
||||
REPO: ${{ github.repository }}
|
||||
OUTPUT_PATH: src/previous-object-checksums.json
|
||||
run: node src/electron/.github/actions/build-electron/download-previous-object-checksums.mjs
|
||||
- name: Build Electron ${{ inputs.step-suffix }}
|
||||
if: ${{ inputs.target-platform != 'win' }}
|
||||
shell: bash
|
||||
@@ -67,17 +77,22 @@ runs:
|
||||
if [ "${{ inputs.is-release }}" = "true" ]; then
|
||||
NINJA_SUMMARIZE_BUILD=1 e build --target electron:release_build
|
||||
else
|
||||
NINJA_SUMMARIZE_BUILD=1 e build --target electron:testing_build
|
||||
NINJA_SUMMARIZE_BUILD=1 e build --target electron:release_build
|
||||
fi
|
||||
cp out/Default/.ninja_log out/electron_ninja_log
|
||||
node electron/script/check-symlinks.js
|
||||
|
||||
# Upload build stats to Datadog
|
||||
if ! [ -z $DD_API_KEY ]; then
|
||||
npx node electron/script/build-stats.mjs out/Default/siso.INFO --upload-stats || true
|
||||
# Build stats and object checksums
|
||||
BUILD_STATS_ARGS="out/Default/siso.INFO --out-dir out/Default --output-object-checksums object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json"
|
||||
if [ -f previous-object-checksums.json ]; then
|
||||
BUILD_STATS_ARGS="$BUILD_STATS_ARGS --input-object-checksums previous-object-checksums.json"
|
||||
fi
|
||||
if ! [ -z "$DD_API_KEY" ]; then
|
||||
BUILD_STATS_ARGS="$BUILD_STATS_ARGS --upload-stats"
|
||||
else
|
||||
echo "Skipping build-stats.mjs upload because DD_API_KEY is not set"
|
||||
fi
|
||||
node electron/script/build-stats.mjs $BUILD_STATS_ARGS || true
|
||||
- name: Build Electron (Windows) ${{ inputs.step-suffix }}
|
||||
if: ${{ inputs.target-platform == 'win' }}
|
||||
shell: powershell
|
||||
@@ -95,16 +110,21 @@ runs:
|
||||
Copy-Item out\Default\.ninja_log out\electron_ninja_log
|
||||
node electron\script\check-symlinks.js
|
||||
|
||||
# Upload build stats to Datadog
|
||||
# Build stats and object checksums
|
||||
$statsArgs = @("out\Default\siso.exe.INFO", "--out-dir", "out\Default", "--output-object-checksums", "object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json")
|
||||
if (Test-Path previous-object-checksums.json) {
|
||||
$statsArgs += @("--input-object-checksums", "previous-object-checksums.json")
|
||||
}
|
||||
if ($env:DD_API_KEY) {
|
||||
try {
|
||||
npx node electron\script\build-stats.mjs out\Default\siso.exe.INFO --upload-stats ; $LASTEXITCODE = 0
|
||||
} catch {
|
||||
Write-Host "Build stats upload failed, continuing..."
|
||||
}
|
||||
$statsArgs += "--upload-stats"
|
||||
} else {
|
||||
Write-Host "Skipping build-stats.mjs upload because DD_API_KEY is not set"
|
||||
}
|
||||
try {
|
||||
& node electron\script\build-stats.mjs @statsArgs ; $LASTEXITCODE = 0
|
||||
} catch {
|
||||
Write-Host "Build stats failed, continuing..."
|
||||
}
|
||||
- name: Verify dist.zip ${{ inputs.step-suffix }}
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -205,7 +225,6 @@ runs:
|
||||
fi
|
||||
- name: Generate FFMpeg ${{ inputs.step-suffix }}
|
||||
shell: bash
|
||||
if: ${{ inputs.is-release == 'true' }}
|
||||
run: |
|
||||
cd src
|
||||
# Reuse the hermetic mac_sdk_path that `e build` wrote for out/Default so
|
||||
@@ -302,3 +321,10 @@ runs:
|
||||
with:
|
||||
name: out_gen_artifacts_${{ env.ARTIFACT_KEY }}
|
||||
path: ./src/out/Default/gen
|
||||
- name: Upload Object Checksums ${{ inputs.step-suffix }}
|
||||
if: ${{ always() && !cancelled() && inputs.is-asan != 'true' }}
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: object_checksums_${{ inputs.artifact-platform }}_${{ inputs.target-arch }}
|
||||
path: ./src/object-checksums.${{ inputs.artifact-platform }}_${{ inputs.target-arch }}.json
|
||||
archive: false
|
||||
|
||||
82
.github/actions/build-electron/download-previous-object-checksums.mjs
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
import { Octokit } from '@octokit/rest';
|
||||
|
||||
import { writeFileSync } from 'node:fs';
|
||||
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
const repo = process.env.REPO;
|
||||
const artifactName = process.env.ARTIFACT_NAME;
|
||||
const branch = process.env.SEARCH_BRANCH;
|
||||
const outputPath = process.env.OUTPUT_PATH;
|
||||
|
||||
const required = { GITHUB_TOKEN: token, REPO: repo, ARTIFACT_NAME: artifactName, SEARCH_BRANCH: branch, OUTPUT_PATH: outputPath };
|
||||
const missing = Object.entries(required).filter(([, v]) => !v).map(([k]) => k);
|
||||
if (missing.length > 0) {
|
||||
console.error(`Missing required environment variables: ${missing.join(', ')}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const [owner, repoName] = repo.split('/');
|
||||
const octokit = new Octokit({ auth: token });
|
||||
|
||||
async function main () {
|
||||
console.log(`Searching for artifact '${artifactName}' on branch '${branch}'...`);
|
||||
|
||||
// Resolve the "Build" workflow name to an ID, mirroring how `gh run list --workflow` works
|
||||
// under the hood (it uses /repos/{owner}/{repo}/actions/workflows/{id}/runs).
|
||||
const { data: workflows } = await octokit.actions.listRepoWorkflows({ owner, repo: repoName });
|
||||
const buildWorkflow = workflows.workflows.find((w) => w.name === 'Build');
|
||||
if (!buildWorkflow) {
|
||||
console.log('Could not find "Build" workflow, continuing without previous checksums');
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: runs } = await octokit.actions.listWorkflowRuns({
|
||||
owner,
|
||||
repo: repoName,
|
||||
workflow_id: buildWorkflow.id,
|
||||
branch,
|
||||
status: 'completed',
|
||||
event: 'push',
|
||||
per_page: 20,
|
||||
exclude_pull_requests: true
|
||||
});
|
||||
|
||||
for (const run of runs.workflow_runs) {
|
||||
const { data: artifacts } = await octokit.actions.listWorkflowRunArtifacts({
|
||||
owner,
|
||||
repo: repoName,
|
||||
run_id: run.id,
|
||||
name: artifactName
|
||||
});
|
||||
|
||||
if (artifacts.artifacts.length > 0) {
|
||||
const artifact = artifacts.artifacts[0];
|
||||
console.log(`Found artifact in run ${run.id} (artifact ID: ${artifact.id}), downloading...`);
|
||||
|
||||
// Non-archived artifacts are still downloaded from the /zip endpoint
|
||||
const response = await octokit.actions.downloadArtifact({
|
||||
owner,
|
||||
repo: repoName,
|
||||
artifact_id: artifact.id,
|
||||
archive_format: 'zip'
|
||||
});
|
||||
|
||||
if (response.headers['content-type'] !== 'application/json') {
|
||||
console.error(`Unexpected content type for artifact download: ${response.headers['content-type']}`);
|
||||
console.error('Expected application/json, continuing without previous checksums');
|
||||
return;
|
||||
}
|
||||
|
||||
writeFileSync(outputPath, JSON.stringify(response.data));
|
||||
console.log('Downloaded previous object checksums successfully');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`No previous object checksums found in last ${runs.workflow_runs.length} runs, continuing without them`);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error('Failed to download previous object checksums, continuing without them:', err.message);
|
||||
process.exit(0);
|
||||
});
|
||||
20
.github/actions/checkout/action.yml
vendored
@@ -191,31 +191,19 @@ runs:
|
||||
# only permits .tar/.tgz so we keep the extension and decode on restore.
|
||||
tar -cf - src | zstd -T0 --long=30 -f -o $CACHE_FILE
|
||||
echo "Compressed src to $(du -sh $CACHE_FILE | cut -f1 -d' ')"
|
||||
cp ./$CACHE_FILE $CACHE_DRIVE/
|
||||
- name: Persist Src Cache
|
||||
if: ${{ steps.check-cache.outputs.cache_exists == 'false' && inputs.use-cache == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
final_cache_path=$CACHE_DRIVE/$CACHE_FILE
|
||||
# Upload to a run-unique temp name first so concurrent readers never
|
||||
# observe a partially-written file, and an interrupted copy can't leave
|
||||
# a truncated file at the final path. Orphaned temp files get swept by
|
||||
# the clean-orphaned-cache-uploads workflow.
|
||||
tmp_cache_path=$final_cache_path.upload-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}
|
||||
echo "Uploading to temp path: $tmp_cache_path"
|
||||
cp ./$CACHE_FILE $tmp_cache_path
|
||||
|
||||
echo "Using cache key: $DEPSHASH"
|
||||
if [ -f "$final_cache_path" ]; then
|
||||
echo "Cache already persisted at $final_cache_path by a concurrent run; discarding ours"
|
||||
rm -f $tmp_cache_path
|
||||
else
|
||||
mv -f $tmp_cache_path $final_cache_path
|
||||
echo "Cache key persisted in $final_cache_path"
|
||||
fi
|
||||
|
||||
echo "Checking path: $final_cache_path"
|
||||
if [ ! -f "$final_cache_path" ]; then
|
||||
echo "Cache key not found"
|
||||
exit 1
|
||||
else
|
||||
echo "Cache key persisted in $final_cache_path"
|
||||
fi
|
||||
- name: Wait for active SSH sessions
|
||||
shell: bash
|
||||
|
||||
2
.github/actions/fix-sync/action.yml
vendored
@@ -133,7 +133,7 @@ runs:
|
||||
run : |
|
||||
cd src/third_party/angle
|
||||
rm -f .git/objects/info/alternates
|
||||
git remote set-url origin https://github.com/google/angle.git
|
||||
git remote set-url origin https://chromium.googlesource.com/angle/angle.git
|
||||
cp .git/config .git/config.backup
|
||||
git remote remove origin
|
||||
mv .git/config.backup .git/config
|
||||
|
||||
@@ -15,7 +15,7 @@ runs:
|
||||
git config --global core.preloadindex true
|
||||
git config --global core.longpaths true
|
||||
fi
|
||||
export BUILD_TOOLS_SHA=1b7bd25dae4a780bb3170fff56c9327b53aaf7eb
|
||||
export BUILD_TOOLS_SHA=a0cc95a1884a631559bcca0c948465b725d9295a
|
||||
npm i -g @electron/build-tools
|
||||
# Update depot_tools to ensure python
|
||||
e d update_depot_tools
|
||||
@@ -29,4 +29,4 @@ runs:
|
||||
else
|
||||
echo "$HOME/.electron_build_tools/third_party/depot_tools" >> $GITHUB_PATH
|
||||
echo "$HOME/.electron_build_tools/third_party/depot_tools/python-bin" >> $GITHUB_PATH
|
||||
fi
|
||||
fi
|
||||
2
.github/problem-matchers/clang.json
vendored
@@ -5,7 +5,7 @@
|
||||
"fromPath": "src/out/Default/args.gn",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(.+)[(:](\\d+)[:,](\\d+)\\)?:\\s+(warning|fatal error|error):\\s+(.*)$",
|
||||
"regexp": "^(.+)[(:](\\d+)[:,](\\d+)\\)?:\\s+(warning|error):\\s+(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
From 85b561ea4dbc76ba98af020b970f3aa6b20fdb9e Mon Sep 17 00:00:00 2001
|
||||
From: Samuel Attard <sam@electronjs.org>
|
||||
Date: Wed, 8 Apr 2026 23:24:15 -0700
|
||||
Subject: [PATCH] siso: reuse the outer *os.File for chunked ReadAt in
|
||||
fileParser.readFile
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The per-chunk goroutine currently re-opens fname to get its own handle
|
||||
for ReadAt. (*os.File).ReadAt is documented as safe for concurrent
|
||||
calls on the same File (on Windows it is ReadFile with an OVERLAPPED
|
||||
offset, so there is no shared seek state), so the extra open is
|
||||
redundant — the goroutines can share the outer f.
|
||||
|
||||
Besides halving the CreateFileW calls per subninja, this avoids an
|
||||
intermittent 'The parameter is incorrect.' (ERROR_INVALID_PARAMETER)
|
||||
from bindflt.sys when out/ is a mapped directory inside a Windows
|
||||
container: bindflt's handle-relative NtCreateFile path races when a
|
||||
second relative open arrives while the first handle to the same target
|
||||
is still being set up. Absolute paths and single opens do not trigger
|
||||
it; see microsoft/Windows-Containers#<tbd>.
|
||||
---
|
||||
siso/toolsupport/ninjautil/file_parser.go | 7 -------
|
||||
1 file changed, 7 deletions(-)
|
||||
|
||||
diff --git a/siso/toolsupport/ninjautil/file_parser.go b/siso/toolsupport/ninjautil/file_parser.go
|
||||
index 8c18d084..63116662 100644
|
||||
--- a/siso/toolsupport/ninjautil/file_parser.go
|
||||
+++ b/siso/toolsupport/ninjautil/file_parser.go
|
||||
@@ -111,13 +111,6 @@ func (p *fileParser) readFile(ctx context.Context, fname string) ([]byte, error)
|
||||
eg.Go(func() error {
|
||||
p.sema <- struct{}{}
|
||||
defer func() { <-p.sema }()
|
||||
- f, err := os.Open(fname)
|
||||
- if err != nil {
|
||||
- return err
|
||||
- }
|
||||
- defer func() {
|
||||
- _ = f.Close()
|
||||
- }()
|
||||
for len(chunkBuf) > 0 {
|
||||
n, err := f.ReadAt(chunkBuf, pos)
|
||||
if err != nil {
|
||||
--
|
||||
2.53.0
|
||||
|
||||
2
.github/workflows/apply-patches.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
# Use dorny/paths-filter instead of the path filter under the on: pull_request: block
|
||||
# so that the output can be used to conditionally run the apply-patches job, which lets
|
||||
# the job be marked as a required status check (conditional skip counts as a success).
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
|
||||
3
.github/workflows/audit-branch-ci.yml
vendored
@@ -86,6 +86,7 @@ jobs:
|
||||
!message.startsWith("Response status code does not indicate success") &&
|
||||
!message.startsWith("The hosted runner lost communication with the server") &&
|
||||
!message.startsWith("Dependabot encountered an error performing the update") &&
|
||||
!message.startsWith("The action 'Run Electron Tests' has timed out") &&
|
||||
!/Unable to make request/.test(message) &&
|
||||
!/The requested URL returned error/.test(message),
|
||||
)
|
||||
@@ -154,7 +155,7 @@ jobs:
|
||||
await core.summary.write();
|
||||
- name: Send Slack message if errors
|
||||
if: ${{ always() && steps.audit-errors.outputs.errorsFound && github.ref == 'refs/heads/main' }}
|
||||
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
|
||||
uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3.0.1
|
||||
with:
|
||||
payload: |
|
||||
link: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
|
||||
25
.github/workflows/branch-created.yml
vendored
@@ -31,8 +31,8 @@ jobs:
|
||||
else
|
||||
echo "Not a release branch: $BRANCH_NAME"
|
||||
fi
|
||||
- name: Determine Unsupported Major Version
|
||||
id: determine-unsupported-major
|
||||
- name: Determine Next Unsupported Major Version
|
||||
id: determine-next-unsupported-major
|
||||
if: ${{ steps.check-major-version.outputs.MAJOR }}
|
||||
env:
|
||||
MAJOR: ${{ steps.check-major-version.outputs.MAJOR }}
|
||||
@@ -50,26 +50,27 @@ jobs:
|
||||
|
||||
# Find the oldest version where eolDate >= stableDate of the new major
|
||||
# This gives us the oldest supported version when the new major goes stable
|
||||
UNSUPPORTED_MAJOR=$(echo "$SCHEDULE" | jq -r --arg stableDate "$STABLE_DATE" '
|
||||
NEXT_UNSUPPORTED_MAJOR=$(echo "$SCHEDULE" | jq -r --arg stableDate "$STABLE_DATE" '
|
||||
[.[] | select(.eolDate != null and .eolDate >= $stableDate)] | sort_by(.version | split(".")[0] | tonumber) | first | .version | split(".")[0]
|
||||
')
|
||||
|
||||
if [[ -z "$UNSUPPORTED_MAJOR" || "$UNSUPPORTED_MAJOR" == "null" ]]; then
|
||||
if [[ -z "$NEXT_UNSUPPORTED_MAJOR" || "$NEXT_UNSUPPORTED_MAJOR" == "null" ]]; then
|
||||
echo "Could not determine oldest supported version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "SCHEDULE=$SCHEDULE" >> "$GITHUB_OUTPUT"
|
||||
echo "UNSUPPORTED_MAJOR=$UNSUPPORTED_MAJOR" >> "$GITHUB_OUTPUT"
|
||||
echo "NEXT_UNSUPPORTED_MAJOR=$NEXT_UNSUPPORTED_MAJOR" >> "$GITHUB_OUTPUT"
|
||||
- name: New Release Branch Tasks
|
||||
if: ${{ steps.check-major-version.outputs.MAJOR }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: electron/electron
|
||||
MAJOR: ${{ steps.check-major-version.outputs.MAJOR }}
|
||||
UNSUPPORTED_MAJOR: ${{ steps.determine-unsupported-major.outputs.UNSUPPORTED_MAJOR }}
|
||||
NEXT_UNSUPPORTED_MAJOR: ${{ steps.determine-next-unsupported-major.outputs.NEXT_UNSUPPORTED_MAJOR }}
|
||||
run: |
|
||||
PREVIOUS_MAJOR=$((MAJOR - 1))
|
||||
UNSUPPORTED_MAJOR=$((NEXT_UNSUPPORTED_MAJOR - 1))
|
||||
|
||||
# Create new labels
|
||||
gh label create $MAJOR-x-y --color 8d9ee8 || true
|
||||
@@ -108,8 +109,8 @@ jobs:
|
||||
id: generate-project-metadata
|
||||
env:
|
||||
MAJOR: ${{ steps.check-major-version.outputs.MAJOR }}
|
||||
UNSUPPORTED_MAJOR: ${{ steps.determine-unsupported-major.outputs.UNSUPPORTED_MAJOR }}
|
||||
SCHEDULE: ${{ steps.determine-unsupported-major.outputs.SCHEDULE }}
|
||||
NEXT_UNSUPPORTED_MAJOR: ${{ steps.determine-next-unsupported-major.outputs.NEXT_UNSUPPORTED_MAJOR }}
|
||||
SCHEDULE: ${{ steps.determine-next-unsupported-major.outputs.SCHEDULE }}
|
||||
with:
|
||||
script: |
|
||||
const schedule = JSON.parse(process.env.SCHEDULE)
|
||||
@@ -144,7 +145,7 @@ jobs:
|
||||
major,
|
||||
"next-major": nextMajor,
|
||||
"prev-major": prevMajor,
|
||||
"ending-support-major": parseInt(process.env.UNSUPPORTED_MAJOR),
|
||||
"ending-support-major": parseInt(process.env.NEXT_UNSUPPORTED_MAJOR),
|
||||
"beta-date": betaDate,
|
||||
"beta-prep-week": betaPrepWeek.toISOString().split('T')[0],
|
||||
"beta-prep-week-end": betaPrepWeekEnd.toISOString().split('T')[0],
|
||||
@@ -156,7 +157,7 @@ jobs:
|
||||
}))
|
||||
- name: Create Release Project Board
|
||||
if: ${{ steps.check-major-version.outputs.MAJOR }}
|
||||
uses: dsanders11/project-actions/copy-project@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
|
||||
uses: dsanders11/project-actions/copy-project@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
|
||||
id: create-release-board
|
||||
with:
|
||||
drafts: true
|
||||
@@ -176,7 +177,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
- name: Find Previous Release Project Board
|
||||
if: ${{ steps.check-major-version.outputs.MAJOR }}
|
||||
uses: dsanders11/project-actions/find-project@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
|
||||
uses: dsanders11/project-actions/find-project@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
|
||||
id: find-prev-release-board
|
||||
with:
|
||||
fail-if-project-not-found: false
|
||||
@@ -184,7 +185,7 @@ jobs:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
- name: Close Previous Release Project Board
|
||||
if: ${{ steps.find-prev-release-board.outputs.number }}
|
||||
uses: dsanders11/project-actions/close-project@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
|
||||
uses: dsanders11/project-actions/close-project@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
|
||||
with:
|
||||
project-number: ${{ steps.find-prev-release-board.outputs.number }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
53
.github/workflows/build.yml
vendored
@@ -61,7 +61,7 @@ jobs:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
||||
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
@@ -200,15 +200,6 @@ jobs:
|
||||
generate-sas-token: 'true'
|
||||
target-platform: win
|
||||
|
||||
# Build a patched siso binary for Windows CI in parallel with checkout-windows.
|
||||
# The Windows build jobs download the resulting artifact and use it via SISO_PATH.
|
||||
build-siso-windows:
|
||||
needs: setup
|
||||
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
|
||||
uses: ./.github/workflows/pipeline-segment-build-siso-windows.yml
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# GN Check Jobs
|
||||
macos-gn-check:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-gn-check.yml
|
||||
@@ -393,7 +384,7 @@ jobs:
|
||||
issues: read
|
||||
pull-requests: read
|
||||
uses: ./.github/workflows/pipeline-electron-build-and-test.yml
|
||||
needs: [checkout-windows, build-siso-windows]
|
||||
needs: checkout-windows
|
||||
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
|
||||
with:
|
||||
build-runs-on: electron-arc-centralus-windows-amd64-16core
|
||||
@@ -412,7 +403,7 @@ jobs:
|
||||
issues: read
|
||||
pull-requests: read
|
||||
uses: ./.github/workflows/pipeline-electron-build-and-test.yml
|
||||
needs: [checkout-windows, build-siso-windows]
|
||||
needs: checkout-windows
|
||||
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
|
||||
with:
|
||||
build-runs-on: electron-arc-centralus-windows-amd64-16core
|
||||
@@ -431,7 +422,7 @@ jobs:
|
||||
issues: read
|
||||
pull-requests: read
|
||||
uses: ./.github/workflows/pipeline-electron-build-and-test.yml
|
||||
needs: [checkout-windows, build-siso-windows]
|
||||
needs: checkout-windows
|
||||
if: ${{ needs.setup.outputs.src == 'true' && !inputs.skip-windows }}
|
||||
with:
|
||||
build-runs-on: electron-arc-centralus-windows-amd64-16core
|
||||
@@ -449,12 +440,36 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
needs: [docs-only, macos-x64, macos-arm64, linux-x64, linux-x64-asan, linux-arm, linux-arm64, build-siso-windows, windows-x64, windows-x86, windows-arm64]
|
||||
if: always() && github.repository == 'electron/electron'
|
||||
steps:
|
||||
- name: Fail if any needed job failed or was cancelled
|
||||
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
|
||||
run: exit 1
|
||||
needs: [docs-only, macos-x64, macos-arm64, linux-x64, linux-x64-asan, linux-arm, linux-arm64, windows-x64, windows-x86, windows-arm64]
|
||||
if: always() && github.repository == 'electron/electron' && !contains(needs.*.result, 'failure')
|
||||
steps:
|
||||
- name: GitHub Actions Jobs Done
|
||||
run: |
|
||||
echo "All GitHub Actions Jobs are done"
|
||||
|
||||
check-signed-commits:
|
||||
name: Check signed commits in green PR
|
||||
needs: gha-done
|
||||
if: ${{ contains(github.event.pull_request.labels.*.name, 'needs-signed-commits')}}
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check signed commits in PR
|
||||
uses: 1Password/check-signed-commits-action@ed2885f3ed2577a4f5d3c3fe895432a557d23d52 # v1
|
||||
with:
|
||||
comment: |
|
||||
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
|
||||
for all incoming PRs. To get your PR merged, please sign those commits
|
||||
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
|
||||
(`git push --force-with-lease`)
|
||||
|
||||
For more information on signing commits, see GitHub's documentation on [Telling Git about your signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key).
|
||||
|
||||
- name: Remove needs-signed-commits label
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
gh pr edit $PR_URL --remove-label needs-signed-commits
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
name: Clean Orphaned Cache Uploads
|
||||
|
||||
# Description:
|
||||
# Sweeps orphaned in-flight upload temp files left on the src-cache volumes
|
||||
# by checkout/action.yml when its cp-to-share step dies before the rename.
|
||||
# A successful upload finishes in minutes, so anything older than 4h is dead.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 */4 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
clean-orphaned-uploads:
|
||||
if: github.repository == 'electron/electron'
|
||||
runs-on: electron-arc-centralus-linux-amd64-32core
|
||||
permissions:
|
||||
contents: read
|
||||
container:
|
||||
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
|
||||
options: --user root
|
||||
volumes:
|
||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||
- /mnt/win-cache:/mnt/win-cache
|
||||
steps:
|
||||
- name: Remove Orphaned Upload Temp Files
|
||||
shell: bash
|
||||
run: |
|
||||
find /mnt/cross-instance-cache -maxdepth 1 -type f -name '*.tar.upload-*' -mmin +240 -print -delete
|
||||
find /mnt/win-cache -maxdepth 1 -type f -name '*.tar.upload-*' -mmin +240 -print -delete
|
||||
127
.github/workflows/clean-src-cache.yml
vendored
@@ -7,6 +7,7 @@ name: Clean Source Cache
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
@@ -16,6 +17,8 @@ jobs:
|
||||
runs-on: electron-arc-centralus-linux-amd64-32core
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
DD_API_KEY: ${{ secrets.DD_API_KEY }}
|
||||
container:
|
||||
image: ghcr.io/electron/build:bc2f48b2415a670de18d13605b1cf0eb5fdbaae1
|
||||
options: --user root
|
||||
@@ -23,12 +26,130 @@ jobs:
|
||||
- /mnt/cross-instance-cache:/mnt/cross-instance-cache
|
||||
- /mnt/win-cache:/mnt/win-cache
|
||||
steps:
|
||||
- name: Get Disk Space Before Cleanup
|
||||
id: disk-before
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Disk space before cleanup:"
|
||||
df -h /mnt/cross-instance-cache
|
||||
df -h /mnt/win-cache
|
||||
CROSS_FREE_BEFORE=$(df -k /mnt/cross-instance-cache | tail -1 | awk '{print $4}')
|
||||
CROSS_TOTAL=$(df -k /mnt/cross-instance-cache | tail -1 | awk '{print $2}')
|
||||
WIN_FREE_BEFORE=$(df -k /mnt/win-cache | tail -1 | awk '{print $4}')
|
||||
WIN_TOTAL=$(df -k /mnt/win-cache | tail -1 | awk '{print $2}')
|
||||
echo "cross_free_kb=$CROSS_FREE_BEFORE" >> $GITHUB_OUTPUT
|
||||
echo "cross_total_kb=$CROSS_TOTAL" >> $GITHUB_OUTPUT
|
||||
echo "win_free_kb=$WIN_FREE_BEFORE" >> $GITHUB_OUTPUT
|
||||
echo "win_total_kb=$WIN_TOTAL" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cleanup Source Cache
|
||||
shell: bash
|
||||
run: |
|
||||
df -h /mnt/cross-instance-cache
|
||||
find /mnt/cross-instance-cache -type f -mtime +15 -delete
|
||||
find /mnt/win-cache -type f -mtime +15 -delete
|
||||
|
||||
- name: Get Disk Space After Cleanup
|
||||
id: disk-after
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Disk space after cleanup:"
|
||||
df -h /mnt/cross-instance-cache
|
||||
df -h /mnt/win-cache
|
||||
find /mnt/win-cache -type f -mtime +15 -delete
|
||||
df -h /mnt/win-cache
|
||||
CROSS_FREE_AFTER=$(df -k /mnt/cross-instance-cache | tail -1 | awk '{print $4}')
|
||||
WIN_FREE_AFTER=$(df -k /mnt/win-cache | tail -1 | awk '{print $4}')
|
||||
echo "cross_free_kb=$CROSS_FREE_AFTER" >> $GITHUB_OUTPUT
|
||||
echo "win_free_kb=$WIN_FREE_AFTER" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Log Disk Space to Datadog
|
||||
if: ${{ env.DD_API_KEY != '' }}
|
||||
shell: bash
|
||||
env:
|
||||
CROSS_FREE_BEFORE: ${{ steps.disk-before.outputs.cross_free_kb }}
|
||||
CROSS_FREE_AFTER: ${{ steps.disk-after.outputs.cross_free_kb }}
|
||||
CROSS_TOTAL: ${{ steps.disk-before.outputs.cross_total_kb }}
|
||||
WIN_FREE_BEFORE: ${{ steps.disk-before.outputs.win_free_kb }}
|
||||
WIN_FREE_AFTER: ${{ steps.disk-after.outputs.win_free_kb }}
|
||||
WIN_TOTAL: ${{ steps.disk-before.outputs.win_total_kb }}
|
||||
run: |
|
||||
TIMESTAMP=$(date +%s)
|
||||
|
||||
CROSS_FREE_BEFORE_GB=$(awk "BEGIN {printf \"%.2f\", $CROSS_FREE_BEFORE / 1024 / 1024}")
|
||||
CROSS_FREE_AFTER_GB=$(awk "BEGIN {printf \"%.2f\", $CROSS_FREE_AFTER / 1024 / 1024}")
|
||||
CROSS_FREED_GB=$(awk "BEGIN {printf \"%.2f\", ($CROSS_FREE_AFTER - $CROSS_FREE_BEFORE) / 1024 / 1024}")
|
||||
CROSS_TOTAL_GB=$(awk "BEGIN {printf \"%.2f\", $CROSS_TOTAL / 1024 / 1024}")
|
||||
|
||||
WIN_FREE_BEFORE_GB=$(awk "BEGIN {printf \"%.2f\", $WIN_FREE_BEFORE / 1024 / 1024}")
|
||||
WIN_FREE_AFTER_GB=$(awk "BEGIN {printf \"%.2f\", $WIN_FREE_AFTER / 1024 / 1024}")
|
||||
WIN_FREED_GB=$(awk "BEGIN {printf \"%.2f\", ($WIN_FREE_AFTER - $WIN_FREE_BEFORE) / 1024 / 1024}")
|
||||
WIN_TOTAL_GB=$(awk "BEGIN {printf \"%.2f\", $WIN_TOTAL / 1024 / 1024}")
|
||||
|
||||
echo "cross-instance-cache: free before=${CROSS_FREE_BEFORE_GB}GB, after=${CROSS_FREE_AFTER_GB}GB, freed=${CROSS_FREED_GB}GB, total=${CROSS_TOTAL_GB}GB"
|
||||
echo "win-cache: free before=${WIN_FREE_BEFORE_GB}GB, after=${WIN_FREE_AFTER_GB}GB, freed=${WIN_FREED_GB}GB, total=${WIN_TOTAL_GB}GB"
|
||||
|
||||
curl -s -X POST "https://api.datadoghq.com/api/v2/series" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "DD-API-KEY: ${DD_API_KEY}" \
|
||||
-d @- << EOF
|
||||
{
|
||||
"series": [
|
||||
{
|
||||
"metric": "electron.src_cache.disk.free_space_before_cleanup_gb",
|
||||
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_FREE_BEFORE_GB}}],
|
||||
"type": 3,
|
||||
"unit": "gigabyte",
|
||||
"tags": ["volume:cross-instance-cache", "platform:linux"]
|
||||
},
|
||||
{
|
||||
"metric": "electron.src_cache.disk.free_space_after_cleanup_gb",
|
||||
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_FREE_AFTER_GB}}],
|
||||
"type": 3,
|
||||
"unit": "gigabyte",
|
||||
"tags": ["volume:cross-instance-cache", "platform:linux"]
|
||||
},
|
||||
{
|
||||
"metric": "electron.src_cache.disk.space_freed_gb",
|
||||
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_FREED_GB}}],
|
||||
"type": 3,
|
||||
"unit": "gigabyte",
|
||||
"tags": ["volume:cross-instance-cache", "platform:linux"]
|
||||
},
|
||||
{
|
||||
"metric": "electron.src_cache.disk.total_space_gb",
|
||||
"points": [{"timestamp": ${TIMESTAMP}, "value": ${CROSS_TOTAL_GB}}],
|
||||
"type": 3,
|
||||
"unit": "gigabyte",
|
||||
"tags": ["volume:cross-instance-cache", "platform:linux"]
|
||||
},
|
||||
{
|
||||
"metric": "electron.src_cache.disk.free_space_before_cleanup_gb",
|
||||
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_FREE_BEFORE_GB}}],
|
||||
"type": 3,
|
||||
"unit": "gigabyte",
|
||||
"tags": ["volume:win-cache", "platform:linux"]
|
||||
},
|
||||
{
|
||||
"metric": "electron.src_cache.disk.free_space_after_cleanup_gb",
|
||||
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_FREE_AFTER_GB}}],
|
||||
"type": 3,
|
||||
"unit": "gigabyte",
|
||||
"tags": ["volume:win-cache", "platform:linux"]
|
||||
},
|
||||
{
|
||||
"metric": "electron.src_cache.disk.space_freed_gb",
|
||||
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_FREED_GB}}],
|
||||
"type": 3,
|
||||
"unit": "gigabyte",
|
||||
"tags": ["volume:win-cache", "platform:linux"]
|
||||
},
|
||||
{
|
||||
"metric": "electron.src_cache.disk.total_space_gb",
|
||||
"points": [{"timestamp": ${TIMESTAMP}, "value": ${WIN_TOTAL_GB}}],
|
||||
"type": 3,
|
||||
"unit": "gigabyte",
|
||||
"tags": ["volume:win-cache", "platform:linux"]
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Disk space metrics logged to Datadog"
|
||||
|
||||
6
.github/workflows/issue-labeled.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
|
||||
org: electron
|
||||
- name: Set status
|
||||
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
|
||||
uses: dsanders11/project-actions/edit-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
project-number: 90
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
|
||||
org: electron
|
||||
- name: Set status
|
||||
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
|
||||
uses: dsanders11/project-actions/edit-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
project-number: 90
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
|
||||
- name: Create comment
|
||||
if: ${{ steps.check-for-comment.outputs.SHOULD_COMMENT }}
|
||||
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3.7.6
|
||||
uses: actions-cool/issues-helper@200c78641dbf33838311e5a1e0c31bbdb92d7cf0 # v3.8.0
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
4
.github/workflows/issue-opened.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
|
||||
org: electron
|
||||
- name: Add to Issue Triage
|
||||
uses: dsanders11/project-actions/add-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
|
||||
uses: dsanders11/project-actions/add-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
|
||||
with:
|
||||
field: Reporter
|
||||
field-value: ${{ github.event.issue.user.login }}
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
}
|
||||
- name: Create unsupported major comment
|
||||
if: ${{ steps.add-labels.outputs.unsupportedMajor }}
|
||||
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3.7.6
|
||||
uses: actions-cool/issues-helper@200c78641dbf33838311e5a1e0c31bbdb92d7cf0 # v3.8.0
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
2
.github/workflows/issue-transferred.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
|
||||
org: electron
|
||||
- name: Remove from issue triage
|
||||
uses: dsanders11/project-actions/delete-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
|
||||
uses: dsanders11/project-actions/delete-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
project-number: 90
|
||||
|
||||
2
.github/workflows/issue-unlabeled.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
org: electron
|
||||
- name: Set status
|
||||
if: ${{ steps.check-for-blocked-labels.outputs.NOT_BLOCKED }}
|
||||
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
|
||||
uses: dsanders11/project-actions/edit-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
project-number: 90
|
||||
|
||||
@@ -51,4 +51,21 @@ jobs:
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
run: |
|
||||
printf "<!-- disallowed-non-maintainer-change -->\n\nHello @${PR_AUTHOR}! 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=-
|
||||
cat <<'REVIEW_EOF' | sed "s/%AUTHOR%/$PR_AUTHOR/g" | gh pr review $PR_URL -r --body-file=-
|
||||
<!-- disallowed-non-maintainer-change -->
|
||||
|
||||
Hello @%AUTHOR%! 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.
|
||||
|
||||
To move this PR forward, please:
|
||||
|
||||
1. Revert the dependency/CI file changes from your branch. (e.g. `yarn.lock`, `.yarn/`, `.yarnrc.yml`, `.github/workflows/`, `.github/actions/`)
|
||||
2. Ensure your branch [allows maintainer commits](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so a maintainer can push the necessary dependency changes on your behalf.
|
||||
3. Leave a comment letting reviewers know the dependency change is still needed.
|
||||
|
||||
<details>
|
||||
<summary>For maintainers</summary>
|
||||
|
||||
To land this PR, push a verified commit to the contributor's branch with the required dependency/CI changes, then dismiss this review.
|
||||
|
||||
</details>
|
||||
REVIEW_EOF
|
||||
|
||||
4
.github/workflows/pipeline-electron-lint.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
echo "::error::Invalid chromium_revision: $chromium_revision"
|
||||
exit 1
|
||||
fi
|
||||
gn_version="$(curl -sL "https://raw.githubusercontent.com/chromium/chromium/refs/tags/${chromium_revision}/DEPS" | grep gn_version | head -n1 | cut -d\' -f4)"
|
||||
gn_version="$(curl -sL -b ~/.gitcookies "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/DEPS?format=TEXT" | base64 -d | grep gn_version | head -n1 | cut -d\' -f4)"
|
||||
|
||||
cipd ensure -ensure-file - -root . <<-CIPD
|
||||
\$ServiceURL https://chrome-infra-packages.appspot.com/
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
fi
|
||||
|
||||
mkdir -p src/buildtools
|
||||
curl -sL "https://raw.githubusercontent.com/chromium/chromium/refs/tags/${chromium_revision}/buildtools/DEPS" > src/buildtools/DEPS
|
||||
curl -sL -b ~/.gitcookies "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/buildtools/DEPS?format=TEXT" | base64 -d > src/buildtools/DEPS
|
||||
|
||||
gclient sync --spec="solutions=[{'name':'src/buildtools','url':None,'deps_file':'DEPS','custom_vars':{'process_deps':True},'managed':False}]"
|
||||
- name: Add problem matchers
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
name: Pipeline Segment - Build Siso (Windows)
|
||||
|
||||
# Builds a patched siso binary for Windows CI. Reads the siso revision from
|
||||
# the Chromium DEPS file at the pinned chromium_version, shallow-clones
|
||||
# chromium.googlesource.com/build at that revision, applies the patches under
|
||||
# .github/siso-patches/, cross-compiles siso.exe for windows/amd64, and
|
||||
# publishes it as the `siso-windows-amd64` artifact. The Windows build jobs
|
||||
# download it and use it via SISO_PATH. The built binary is cached keyed on
|
||||
# the siso revision + sha256 of the patch contents, so subsequent runs just
|
||||
# restore it.
|
||||
|
||||
on:
|
||||
workflow_call: {}
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout Electron
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
sparse-checkout: |
|
||||
DEPS
|
||||
.github/siso-patches
|
||||
- name: Resolve siso revision from Chromium DEPS
|
||||
id: resolve
|
||||
run: |
|
||||
set -euo pipefail
|
||||
CHROMIUM_VERSION=$(python3 -c "import re; print(re.search(r\"'chromium_version':\s*\n\s*'([^']+)'\", open('DEPS').read()).group(1))")
|
||||
if ! [[ "$CHROMIUM_VERSION" =~ ^[0-9]+(\.[0-9]+){1,3}$ ]]; then
|
||||
echo "error: unexpected chromium_version format: $CHROMIUM_VERSION" >&2
|
||||
exit 1
|
||||
fi
|
||||
curl -sfL "https://raw.githubusercontent.com/chromium/chromium/${CHROMIUM_VERSION}/DEPS" -o /tmp/chromium-DEPS
|
||||
SISO_SHA=$(python3 -c "import re; print(re.search(r\"'siso_version':\s*'git_revision:([0-9a-f]+)'\", open('/tmp/chromium-DEPS').read()).group(1))")
|
||||
if ! [[ "$SISO_SHA" =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "error: unexpected siso_version SHA: $SISO_SHA" >&2
|
||||
exit 1
|
||||
fi
|
||||
PATCHES_HASH=$(find .github/siso-patches -type f -name '*.patch' | sort | xargs sha256sum | sha256sum | awk '{print $1}')
|
||||
echo "siso-sha=${SISO_SHA}" >> "$GITHUB_OUTPUT"
|
||||
echo "patches-hash=${PATCHES_HASH}" >> "$GITHUB_OUTPUT"
|
||||
echo "Chromium ${CHROMIUM_VERSION} pins siso at ${SISO_SHA}"
|
||||
echo "Patches hash: ${PATCHES_HASH}"
|
||||
- name: Restore cached siso binary
|
||||
id: cache-siso
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: siso-out/siso.exe
|
||||
key: siso-windows-amd64-${{ steps.resolve.outputs.siso-sha }}-${{ steps.resolve.outputs.patches-hash }}
|
||||
- name: Shallow clone chromium build repo at pinned revision
|
||||
if: steps.cache-siso.outputs.cache-hit != 'true'
|
||||
env:
|
||||
SISO_SHA: ${{ steps.resolve.outputs.siso-sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir chromium-build
|
||||
cd chromium-build
|
||||
git init -q
|
||||
git remote add origin https://chromium.googlesource.com/build
|
||||
git -c protocol.version=2 fetch --depth=1 origin "$SISO_SHA"
|
||||
git checkout --detach FETCH_HEAD
|
||||
- name: Apply in-tree siso patches
|
||||
if: steps.cache-siso.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd chromium-build
|
||||
git -c user.name=electron-ci -c user.email=ci@electronjs.org \
|
||||
am --3way "${GITHUB_WORKSPACE}/.github/siso-patches"/*.patch
|
||||
- name: Set up Go
|
||||
if: steps.cache-siso.outputs.cache-hit != 'true'
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
with:
|
||||
go-version-file: chromium-build/siso/go.mod
|
||||
cache: false
|
||||
- name: Build siso (windows/amd64)
|
||||
if: steps.cache-siso.outputs.cache-hit != 'true'
|
||||
working-directory: chromium-build/siso
|
||||
env:
|
||||
CGO_ENABLED: '0'
|
||||
GOOS: windows
|
||||
GOARCH: amd64
|
||||
run: |
|
||||
mkdir -p "${GITHUB_WORKSPACE}/siso-out"
|
||||
go build -trimpath -o "${GITHUB_WORKSPACE}/siso-out/siso.exe" .
|
||||
- name: Upload siso artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: siso-windows-amd64
|
||||
path: siso-out/siso.exe
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
@@ -77,6 +77,7 @@ env:
|
||||
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 }}
|
||||
@@ -194,22 +195,6 @@ jobs:
|
||||
- name: Free up space (macOS)
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
uses: ./src/electron/.github/actions/free-space-macos
|
||||
- name: Download custom siso binary (Windows)
|
||||
if: ${{ inputs.target-platform == 'win' }}
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: siso-windows-amd64
|
||||
path: ${{ runner.temp }}/siso
|
||||
- name: Set SISO_PATH (Windows)
|
||||
if: ${{ inputs.target-platform == 'win' }}
|
||||
run: |
|
||||
SISO_BIN="${RUNNER_TEMP}/siso/siso.exe"
|
||||
if [ ! -f "$SISO_BIN" ]; then
|
||||
echo "error: expected siso binary at $SISO_BIN" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "SISO_PATH=$SISO_BIN" >> "$GITHUB_ENV"
|
||||
echo "Using custom siso binary at $SISO_BIN"
|
||||
- name: Build Electron
|
||||
if: ${{ inputs.target-platform != 'macos' || (inputs.target-variant == 'all' || inputs.target-variant == 'darwin') }}
|
||||
uses: ./src/electron/.github/actions/build-electron
|
||||
|
||||
@@ -126,7 +126,7 @@ jobs:
|
||||
cd src/electron
|
||||
git pack-refs
|
||||
- name: Download Out Gen Artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
||||
with:
|
||||
name: out_gen_artifacts_${{ env.ARTIFACT_KEY }}
|
||||
path: ./src/out/${{ env.ELECTRON_OUT_DIR }}/gen
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
run: echo "::add-matcher::src/electron/.github/problem-matchers/clang.json"
|
||||
- name: Run Clang-Tidy
|
||||
run: |
|
||||
e init -f --root=$(pwd) --out=${ELECTRON_OUT_DIR} testing --target-cpu ${TARGET_ARCH} --remote-build none
|
||||
e init -f --root=$(pwd) --out=${ELECTRON_OUT_DIR} testing --target-cpu ${TARGET_ARCH}
|
||||
|
||||
export GN_EXTRA_ARGS="target_cpu=\"${TARGET_ARCH}\""
|
||||
if [ "${{ inputs.target-platform }}" = "win" ]; then
|
||||
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
run: |
|
||||
for target_cpu in ${{ inputs.target-archs }}
|
||||
do
|
||||
e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu $target_cpu --remote-build none
|
||||
e init -f --root=$(pwd) --out=Default ${{ inputs.gn-build-type }} --import ${{ inputs.gn-build-type }} --target-cpu $target_cpu
|
||||
cd src
|
||||
export GN_EXTRA_ARGS="target_cpu=\"$target_cpu\""
|
||||
if [ "${{ inputs.target-platform }}" = "linux" ]; then
|
||||
|
||||
@@ -79,6 +79,7 @@ env:
|
||||
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' ||
|
||||
@@ -207,22 +208,6 @@ jobs:
|
||||
- name: Free up space (macOS)
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
uses: ./src/electron/.github/actions/free-space-macos
|
||||
- name: Download custom siso binary (Windows)
|
||||
if: ${{ inputs.target-platform == 'win' }}
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
||||
with:
|
||||
name: siso-windows-amd64
|
||||
path: ${{ runner.temp }}/siso
|
||||
- name: Set SISO_PATH (Windows)
|
||||
if: ${{ inputs.target-platform == 'win' }}
|
||||
run: |
|
||||
SISO_BIN="${RUNNER_TEMP}/siso/siso.exe"
|
||||
if [ ! -f "$SISO_BIN" ]; then
|
||||
echo "error: expected siso binary at $SISO_BIN" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "SISO_PATH=$SISO_BIN" >> "$GITHUB_ENV"
|
||||
echo "Using custom siso binary at $SISO_BIN"
|
||||
- name: Build Electron
|
||||
if: ${{ inputs.target-platform != 'macos' || (inputs.target-variant == 'all' ||
|
||||
inputs.target-variant == 'darwin') }}
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Download Generated Artifacts
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
||||
with:
|
||||
name: generated_artifacts_linux_arm64
|
||||
path: ./generated_artifacts_linux_arm64
|
||||
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build-type: ${{ inputs.target-platform == 'macos' && fromJSON('["darwin","mas"]') || (inputs.target-platform == 'win' && fromJSON('["win"]') || fromJSON('["linux"]')) }}
|
||||
shard: ${{ case(inputs.display-server == 'wayland', fromJSON('[1]'), inputs.target-platform == 'linux', fromJSON('[1, 2, 3]'), inputs.target-platform == 'macos' && inputs.target-arch == 'x64', fromJSON('[1, 2, 3]'), fromJSON('[1, 2]')) }}
|
||||
shard: ${{ case(inputs.display-server == 'wayland', fromJSON('[1]'), inputs.target-platform == 'linux', fromJSON('[1, 2, 3]'), fromJSON('[1, 2]')) }}
|
||||
env:
|
||||
BUILD_TYPE: ${{ matrix.build-type }}
|
||||
TARGET_ARCH: ${{ inputs.target-arch }}
|
||||
@@ -175,12 +175,12 @@ jobs:
|
||||
echo "DISABLE_CRASH_REPORTER_TESTS=true" >> $GITHUB_ENV
|
||||
echo "IS_ASAN=true" >> $GITHUB_ENV
|
||||
- name: Download Generated Artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
||||
with:
|
||||
name: generated_artifacts_${{ env.ARTIFACT_KEY }}
|
||||
path: ./generated_artifacts_${{ matrix.build-type }}_${{ inputs.target-arch }}
|
||||
- name: Download Src Artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
||||
with:
|
||||
name: src_artifacts_${{ env.ARTIFACT_KEY }}
|
||||
path: ./src_artifacts_${{ matrix.build-type }}_${{ inputs.target-arch }}
|
||||
@@ -227,7 +227,7 @@ jobs:
|
||||
cd src/electron
|
||||
export ELECTRON_TEST_RESULTS_DIR=`pwd`/junit
|
||||
# Get which tests are on this shard
|
||||
tests_files=$(node script/split-tests ${{ matrix.shard }} ${{ case(inputs.display-server == 'wayland', 1, inputs.target-platform == 'linux', 3, inputs.target-platform == 'macos' && inputs.target-arch == 'x64', 3, 2) }})
|
||||
tests_files=$(node script/split-tests ${{ matrix.shard }} ${{ case(inputs.display-server == 'wayland', 1, inputs.target-platform == 'linux', 3, 2) }})
|
||||
if [ "${{ inputs.display-server }}" = "wayland" ]; then
|
||||
allowlist_file=script/wayland-test-allowlist.txt
|
||||
filtered_tests=""
|
||||
|
||||
@@ -67,12 +67,12 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
uses: ./src/electron/.github/actions/install-dependencies
|
||||
- name: Download Generated Artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
||||
with:
|
||||
name: generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
|
||||
path: ./generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
|
||||
- name: Download Src Artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
||||
with:
|
||||
name: src_artifacts_linux_${{ env.TARGET_ARCH }}
|
||||
path: ./src_artifacts_linux_${{ env.TARGET_ARCH }}
|
||||
@@ -123,12 +123,12 @@ jobs:
|
||||
- name: Install Dependencies
|
||||
uses: ./src/electron/.github/actions/install-dependencies
|
||||
- name: Download Generated Artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
||||
with:
|
||||
name: generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
|
||||
path: ./generated_artifacts_${{ env.BUILD_TYPE }}_${{ env.TARGET_ARCH }}
|
||||
- name: Download Src Artifacts
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
|
||||
with:
|
||||
name: src_artifacts_linux_${{ env.TARGET_ARCH }}
|
||||
path: ./src_artifacts_linux_${{ env.TARGET_ARCH }}
|
||||
|
||||
58
.github/workflows/pr-template-check.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: PR Template Check
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, ready_for_review]
|
||||
|
||||
# SECURITY: This workflow uses pull_request_target and has access to secrets.
|
||||
# Do NOT checkout or run code from the PR head. All code execution must use
|
||||
# the base branch only. Adding a ref to PR head would expose secrets to
|
||||
# untrusted code.
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
check-pr-template:
|
||||
if: ${{ github.event.pull_request.head.repo.fork && !github.event.pull_request.draft && !startsWith(github.head_ref, 'roller/') }}
|
||||
name: Check PR Template
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
||||
with:
|
||||
sparse-checkout: .github/PULL_REQUEST_TEMPLATE.md
|
||||
sparse-checkout-cone-mode: false
|
||||
- name: Check for required sections
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const template = fs.readFileSync('.github/PULL_REQUEST_TEMPLATE.md', 'utf8');
|
||||
const requiredSections = [...template.matchAll(/^(#{1,4} .+)$/gm)].map(
|
||||
(m) => m[1],
|
||||
);
|
||||
if (requiredSections.length === 0) {
|
||||
console.log('No heading sections found in PR template');
|
||||
return;
|
||||
}
|
||||
const body = context.payload.pull_request.body || '';
|
||||
const missingSections = requiredSections.filter(
|
||||
(section) => !body.includes(section),
|
||||
);
|
||||
if (missingSections.length > 0) {
|
||||
const list = missingSections.map((s) => `- \`${s}\``).join('\n');
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body: `This PR was automatically closed because the PR template was not properly filled out. The following required sections are missing:\n\n${list}\n\nPlease update your PR description to include all required sections and reopen the PR.`,
|
||||
});
|
||||
await github.rest.pulls.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: context.payload.pull_request.number,
|
||||
state: 'closed',
|
||||
});
|
||||
}
|
||||
44
.github/workflows/pr-triage-automation.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: PR Triage Automation
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [synchronize, review_requested]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
# SECURITY: This workflow uses pull_request_target and has access to secrets.
|
||||
# Do NOT checkout or run code from the PR head. All code execution must use
|
||||
# the base branch only. Adding a ref to PR head would expose secrets to
|
||||
# untrusted code.
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
set-needs-review:
|
||||
name: Set status to Needs Review
|
||||
if: >-
|
||||
(github.event_name == 'pull_request_target'
|
||||
&& github.event.pull_request.draft != true
|
||||
&& !contains(github.event.pull_request.labels.*.name, 'wip ⚒')
|
||||
&& (github.event.action == 'synchronize' || github.event.action == 'review_requested'))
|
||||
|| (github.event_name == 'issue_comment'
|
||||
&& github.event.issue.pull_request
|
||||
&& !contains(github.event.issue.labels.*.name, 'wip ⚒')
|
||||
&& github.event.comment.user.login == github.event.issue.user.login)
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
uses: electron/github-app-auth-action@e14e47722ed120360649d0789e25b9baece12725 # v2.0.0
|
||||
id: generate-token
|
||||
with:
|
||||
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
|
||||
org: electron
|
||||
- name: Set status to Needs Review
|
||||
uses: dsanders11/project-actions/edit-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
project-number: 118
|
||||
field: Status
|
||||
field-value: 🌀 Needs Review
|
||||
fail-if-item-not-found: false
|
||||
8
.github/workflows/pull-request-labeled.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
permissions: {}
|
||||
steps:
|
||||
- name: Trigger Slack workflow
|
||||
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
|
||||
uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3.0.1
|
||||
with:
|
||||
webhook: ${{ secrets.BACKPORT_REQUESTED_SLACK_WEBHOOK_URL }}
|
||||
webhook-type: webhook-trigger
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
creds: ${{ secrets.RELEASE_BOARD_GH_APP_CREDS }}
|
||||
org: electron
|
||||
- name: Set status
|
||||
uses: dsanders11/project-actions/edit-item@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
|
||||
uses: dsanders11/project-actions/edit-item@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
|
||||
with:
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
project-number: 94
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
with:
|
||||
creds: ${{ secrets.ISSUE_TRIAGE_GH_APP_CREDS }}
|
||||
- name: Create comment
|
||||
uses: actions-cool/issues-helper@71b62d7da76e59ff7b193904feb6e77d4dbb2777 # v3.7.6
|
||||
uses: actions-cool/issues-helper@200c78641dbf33838311e5a1e0c31bbdb92d7cf0 # v3.8.0
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
|
||||
Hello @${{ github.event.pull_request.user.login }}. Due to the high amount of AI spam PRs we receive, if a PR is detected to be majority AI-generated without disclosure and untested, we will automatically close the PR.
|
||||
|
||||
We welcome the use of AI tools, as long as the PR meets our quality standards and has clearly been built and tested. If you believe your PR was closed in error, we welcome you to resubmit. However, please read our [CONTRIBUTING.md](http://contributing.md/) carefully before reopening. Thanks for your contribution.
|
||||
We welcome the use of AI tools, as long as the PR meets our quality standards and has clearly been built and tested. If you believe your PR was closed in error, we welcome you to resubmit. However, please read our [CONTRIBUTING.md](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) and [AI Tool Policy](https://github.com/electron/governance/blob/main/policy/ai.md) carefully before reopening. Thanks for your contribution.
|
||||
- name: Close the pull request
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
@@ -13,6 +13,7 @@ permissions: {}
|
||||
jobs:
|
||||
check-signed-commits:
|
||||
name: Check signed commits in PR
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'needs-signed-commits')}}
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -22,9 +23,9 @@ jobs:
|
||||
uses: 1Password/check-signed-commits-action@ed2885f3ed2577a4f5d3c3fe895432a557d23d52 # v1
|
||||
with:
|
||||
comment: |
|
||||
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
|
||||
for all incoming PRs. To get your PR merged, please sign those commits
|
||||
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
|
||||
⚠️ This PR contains unsigned commits. This repository enforces [commit signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification)
|
||||
for all incoming PRs. To get your PR merged, please sign those commits
|
||||
(`git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}`) and force push them to this branch
|
||||
(`git push --force-with-lease`)
|
||||
|
||||
For more information on signing commits, see GitHub's documentation on [Telling Git about your signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key).
|
||||
@@ -36,11 +37,3 @@ jobs:
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
gh pr edit $PR_URL --add-label needs-signed-commits
|
||||
|
||||
- name: Remove needs-signed-commits label
|
||||
if: ${{ success() && contains(github.event.pull_request.labels.*.name, 'needs-signed-commits') }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
run: |
|
||||
gh pr edit $PR_URL --remove-label needs-signed-commits
|
||||
|
||||
2
.github/workflows/scorecards.yml
vendored
@@ -51,6 +51,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v3.29.5
|
||||
uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
2
.github/workflows/stable-prep-items.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
PROJECT_NUMBER=$(gh project list --owner electron --format json | jq -r '.projects | map(select(.title | test("^[0-9]+-x-y$"))) | max_by(.number) | .number')
|
||||
echo "PROJECT_NUMBER=$PROJECT_NUMBER" >> "$GITHUB_OUTPUT"
|
||||
- name: Update Completed Stable Prep Items
|
||||
uses: dsanders11/project-actions/completed-by@2134fe7cc71c58b7ae259c82a8e63c6058255678 # v1.7.0
|
||||
uses: dsanders11/project-actions/completed-by@5767984408ccc6742f83acc8b8d8ea5e09f329af # v2.0.0
|
||||
with:
|
||||
field: Prep Status
|
||||
field-value: ✅ Complete
|
||||
|
||||
14
.github/workflows/windows-publish.yml
vendored
@@ -51,14 +51,6 @@ jobs:
|
||||
generate-sas-token: 'true'
|
||||
target-platform: win
|
||||
|
||||
# Build the patched siso binary in parallel with checkout-windows; the
|
||||
# publish-*-win jobs consume it via SISO_PATH.
|
||||
build-siso-windows:
|
||||
if: github.repository == 'electron/electron'
|
||||
uses: ./.github/workflows/pipeline-segment-build-siso-windows.yml
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
publish-x64-win:
|
||||
uses: ./.github/workflows/pipeline-segment-electron-publish.yml
|
||||
permissions:
|
||||
@@ -66,7 +58,7 @@ jobs:
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: [checkout-windows, build-siso-windows]
|
||||
needs: checkout-windows
|
||||
with:
|
||||
environment: production-release
|
||||
build-runs-on: electron-arc-centralus-windows-amd64-16core
|
||||
@@ -85,7 +77,7 @@ jobs:
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: [checkout-windows, build-siso-windows]
|
||||
needs: checkout-windows
|
||||
with:
|
||||
environment: production-release
|
||||
build-runs-on: electron-arc-centralus-windows-amd64-16core
|
||||
@@ -104,7 +96,7 @@ jobs:
|
||||
attestations: write
|
||||
contents: read
|
||||
id-token: write
|
||||
needs: [checkout-windows, build-siso-windows]
|
||||
needs: checkout-windows
|
||||
with:
|
||||
environment: production-release
|
||||
build-runs-on: electron-arc-centralus-windows-amd64-16core
|
||||
|
||||
19
BUILD.gn
@@ -105,25 +105,21 @@ electron_mac_bundle_id = branding.mac_bundle_id
|
||||
if (override_electron_version != "") {
|
||||
electron_version = override_electron_version
|
||||
} else {
|
||||
# When building from a source code tarball there is no git tag available and
|
||||
# When building from source code tarball there is no git tag available and
|
||||
# builders must explicitly pass override_electron_version in gn args.
|
||||
#
|
||||
# Resolve the real locations of packed-refs and HEAD via git so that this
|
||||
# also works when electron/ is a `git worktree` (where .git is a file, not a
|
||||
# directory, and GN's read_file cannot follow the gitdir indirection).
|
||||
electron_git_ref_paths =
|
||||
exec_script("script/get-git-ref-paths.py", [], "list lines")
|
||||
|
||||
# This read_file call will assert if there is no git information, without it
|
||||
# gn will generate a malformed build configuration and ninja will get into
|
||||
# infinite loop.
|
||||
read_file(electron_git_ref_paths[0], "string")
|
||||
read_file(".git/packed-refs", "string")
|
||||
|
||||
# Set electron version from git tag.
|
||||
electron_version = exec_script("script/get-git-version.py",
|
||||
[],
|
||||
"trim string",
|
||||
electron_git_ref_paths)
|
||||
[
|
||||
".git/packed-refs",
|
||||
".git/HEAD",
|
||||
])
|
||||
}
|
||||
|
||||
if (is_mas_build) {
|
||||
@@ -494,7 +490,6 @@ source_set("electron_lib") {
|
||||
"//components/pref_registry",
|
||||
"//components/prefs",
|
||||
"//components/security_state/content",
|
||||
"//components/tracing:tracing_metrics",
|
||||
"//components/upload_list",
|
||||
"//components/user_prefs",
|
||||
"//components/viz/host",
|
||||
@@ -780,7 +775,6 @@ source_set("electron_lib") {
|
||||
"//components/zoom",
|
||||
"//extensions/browser",
|
||||
"//extensions/browser/api:api_provider",
|
||||
"//extensions/browser/mime_handler:stream_info",
|
||||
"//extensions/browser/updater",
|
||||
"//extensions/common",
|
||||
"//extensions/common:core_api_provider",
|
||||
@@ -1649,7 +1643,6 @@ action("node_version_header") {
|
||||
action("generate_node_headers") {
|
||||
deps = [ ":generate_config_gypi" ]
|
||||
script = "script/node/generate_node_headers.py"
|
||||
args = [ rebase_path("$root_gen_dir") ]
|
||||
outputs = [ "$root_gen_dir/node_headers.json" ]
|
||||
}
|
||||
|
||||
|
||||
@@ -171,10 +171,6 @@ e test # Run full test suite
|
||||
|
||||
When working on the `roller/chromium/main` branch to upgrade Chromium activate the "Electron Chromium Upgrade" skill.
|
||||
|
||||
## Node.js Upgrade Workflow
|
||||
|
||||
When working on the `roller/node/main` branch to upgrade Node.js activate the "Electron Node.js Upgrade" skill.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
PR bodies must always include a `Notes:` section as the **last line** of the body. This is a consumer-facing release note for Electron app developers — describe the user-visible fix or change, not internal implementation details. Use `Notes: none` if there is no user-facing change.
|
||||
|
||||
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'148.0.7778.5',
|
||||
'148.0.7763.0',
|
||||
'node_version':
|
||||
'v24.14.1',
|
||||
'nan_version':
|
||||
|
||||
@@ -144,8 +144,6 @@ static_library("chrome") {
|
||||
"//chrome/browser/ui/views/overlay/toggle_camera_button.h",
|
||||
"//chrome/browser/ui/views/overlay/toggle_microphone_button.cc",
|
||||
"//chrome/browser/ui/views/overlay/toggle_microphone_button.h",
|
||||
"//chrome/browser/ui/views/overlay/toggle_mute_button.cc",
|
||||
"//chrome/browser/ui/views/overlay/toggle_mute_button.h",
|
||||
"//chrome/browser/ui/views/overlay/video_overlay_window_views.cc",
|
||||
"//chrome/browser/ui/views/overlay/video_overlay_window_views.h",
|
||||
"//chrome/browser/ui/views/picture_in_picture/picture_in_picture_bounds_change_animation.cc",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { shell } from 'electron/common';
|
||||
import { app, dialog, BrowserWindow, ipcMain, Menu } from 'electron/main';
|
||||
import { app, dialog, BrowserWindow, ipcMain } from 'electron/main';
|
||||
|
||||
import * as path from 'node:path';
|
||||
import * as url from 'node:url';
|
||||
@@ -11,52 +11,12 @@ app.on('window-all-closed', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const helpMenu: Electron.MenuItemConstructorOptions = {
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Learn More',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://electronjs.org');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Documentation',
|
||||
click: async () => {
|
||||
const version = process.versions.electron;
|
||||
await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Community Discussions',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://discord.gg/electronjs');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Search Issues',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://github.com/electron/electron/issues');
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' };
|
||||
const template: Electron.MenuItemConstructorOptions[] = [
|
||||
...(isMac ? [macAppMenu] : []),
|
||||
{ role: 'fileMenu' },
|
||||
{ role: 'editMenu' },
|
||||
{ role: 'viewMenu' },
|
||||
{ role: 'windowMenu' },
|
||||
helpMenu
|
||||
];
|
||||
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
|
||||
});
|
||||
function decorateURL (url: string) {
|
||||
// safely add `?utm_source=default_app
|
||||
const parsedUrl = new URL(url);
|
||||
parsedUrl.searchParams.append('utm_source', 'default_app');
|
||||
return parsedUrl.toString();
|
||||
}
|
||||
|
||||
// Find the shortest path to the electron binary
|
||||
const absoluteElectronPath = process.execPath;
|
||||
@@ -109,7 +69,7 @@ async function createWindow (backgroundColor?: string) {
|
||||
mainWindow.on('ready-to-show', () => mainWindow!.show());
|
||||
|
||||
mainWindow.webContents.setWindowOpenHandler(details => {
|
||||
shell.openExternal(details.url);
|
||||
shell.openExternal(decorateURL(details.url));
|
||||
return { action: 'deny' };
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,10 @@ added:
|
||||
* `window` [BaseWindow](base-window.md) (optional)
|
||||
* `options` Object
|
||||
* `title` string (optional)
|
||||
* `defaultPath` string (optional)
|
||||
* `defaultPath` string (optional) - Absolute directory path, absolute file
|
||||
path, or file name to use by default. If not provided, the dialog will
|
||||
default to the user's Downloads folder, or their home directory if Downloads
|
||||
doesn't exist.
|
||||
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
|
||||
left empty the default label will be used.
|
||||
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
|
||||
@@ -109,7 +112,10 @@ changes:
|
||||
* `window` [BaseWindow](base-window.md) (optional)
|
||||
* `options` Object
|
||||
* `title` string (optional)
|
||||
* `defaultPath` string (optional)
|
||||
* `defaultPath` string (optional) - Absolute directory path, absolute file
|
||||
path, or file name to use by default. If not provided, the dialog will
|
||||
default to the user's Downloads folder, or their home directory if Downloads
|
||||
doesn't exist.
|
||||
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
|
||||
left empty the default label will be used.
|
||||
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
|
||||
@@ -198,7 +204,9 @@ added:
|
||||
* `options` Object
|
||||
* `title` string (optional) - The dialog title. Cannot be displayed on some _Linux_ desktop environments.
|
||||
* `defaultPath` string (optional) - Absolute directory path, absolute file
|
||||
path, or file name to use by default.
|
||||
path, or file name to use by default. If not provided, the dialog will
|
||||
default to the user's Downloads folder, or their home directory if Downloads
|
||||
doesn't exist.
|
||||
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
|
||||
left empty the default label will be used.
|
||||
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
|
||||
@@ -238,7 +246,9 @@ changes:
|
||||
* `options` Object
|
||||
* `title` string (optional) - The dialog title. Cannot be displayed on some _Linux_ desktop environments.
|
||||
* `defaultPath` string (optional) - Absolute directory path, absolute file
|
||||
path, or file name to use by default.
|
||||
path, or file name to use by default. If not provided, the dialog will
|
||||
default to the user's Downloads folder, or their home directory if Downloads
|
||||
doesn't exist.
|
||||
* `buttonLabel` string (optional) - Custom label for the confirmation button, when
|
||||
left empty the default label will be used.
|
||||
* `filters` [FileFilter[]](structures/file-filter.md) (optional)
|
||||
|
||||
@@ -56,6 +56,9 @@ Returns `string` - The badge string of the dock.
|
||||
|
||||
Hides the dock icon.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Known issue:** Calling `dock.hide()` within one second of a previous call will have no effect. As a workaround, ensure at least one second has elapsed between calls — for example, by deferring with a `setTimeout` of 1100ms or more after a previous call.
|
||||
|
||||
#### `dock.show()` _macOS_
|
||||
|
||||
Returns `Promise<void>` - Resolves when the dock icon is shown.
|
||||
|
||||
@@ -148,34 +148,3 @@ added:
|
||||
-->
|
||||
|
||||
Unregisters all of the global shortcuts.
|
||||
|
||||
### `globalShortcut.setSuspended(suspended)`
|
||||
|
||||
<!--
|
||||
```YAML history
|
||||
added:
|
||||
- pr-url: https://github.com/electron/electron/pull/50425
|
||||
```
|
||||
-->
|
||||
|
||||
* `suspended` boolean - Whether global shortcut handling should be suspended.
|
||||
|
||||
Suspends or resumes global shortcut handling. When suspended, all registered
|
||||
global shortcuts will stop listening for key presses. When resumed, all
|
||||
previously registered shortcuts will begin listening again. New shortcut
|
||||
registrations will fail while handling is suspended.
|
||||
|
||||
This can be useful when you want to temporarily allow the user to press key
|
||||
combinations without your application intercepting them, for example while
|
||||
displaying a UI to rebind shortcuts.
|
||||
|
||||
### `globalShortcut.isSuspended()`
|
||||
|
||||
<!--
|
||||
```YAML history
|
||||
added:
|
||||
- pr-url: https://github.com/electron/electron/pull/50425
|
||||
```
|
||||
-->
|
||||
|
||||
Returns `boolean` - Whether global shortcut handling is currently suspended.
|
||||
|
||||
@@ -44,8 +44,8 @@ See [`Menu`](menu.md) for examples.
|
||||
menu items.
|
||||
* `registerAccelerator` boolean (optional) _Linux_ _Windows_ - If false, the accelerator won't be registered
|
||||
with the system, but it will still be displayed. Defaults to true.
|
||||
* `sharingItem` [SharingItem](structures/sharing-item.md) (optional) _macOS_ - The item to share when the `role` is `shareMenu`.
|
||||
* `submenu` ([MenuItemConstructorOptions](#new-menuitemoptions)[] | [Menu](menu.md)) (optional) - Should be specified
|
||||
* `sharingItem` SharingItem (optional) _macOS_ - The item to share when the `role` is `shareMenu`.
|
||||
* `submenu` (MenuItemConstructorOptions[] | [Menu](menu.md)) (optional) - Should be specified
|
||||
for `submenu` type menu items. If `submenu` is specified, the `type: 'submenu'` can be omitted.
|
||||
If the value is not a [`Menu`](menu.md) then it will be automatically converted to one using
|
||||
`Menu.buildFromTemplate`.
|
||||
@@ -89,7 +89,7 @@ A `Function` that is fired when the MenuItem receives a click event.
|
||||
It can be called with `menuItem.click(event, focusedWindow, focusedWebContents)`.
|
||||
|
||||
* `event` [KeyboardEvent](structures/keyboard-event.md)
|
||||
* `focusedWindow` [BaseWindow](base-window.md)
|
||||
* `focusedWindow` [BaseWindow](browser-window.md)
|
||||
* `focusedWebContents` [WebContents](web-contents.md)
|
||||
|
||||
#### `menuItem.submenu`
|
||||
@@ -110,11 +110,11 @@ A `string` (optional) indicating the item's role, if set. Can be `undo`, `redo`,
|
||||
|
||||
#### `menuItem.accelerator`
|
||||
|
||||
An [`Accelerator | null`](../tutorial/keyboard-shortcuts.md#accelerators) indicating the item's accelerator, if set.
|
||||
An `Accelerator | null` indicating the item's accelerator, if set.
|
||||
|
||||
#### `menuItem.userAccelerator` _Readonly_ _macOS_
|
||||
|
||||
An [`Accelerator | null`](../tutorial/keyboard-shortcuts.md#accelerators) indicating the item's [user-assigned accelerator](https://developer.apple.com/documentation/appkit/nsmenuitem/1514850-userkeyequivalent?language=objc) for the menu item.
|
||||
An `Accelerator | null` indicating the item's [user-assigned accelerator](https://developer.apple.com/documentation/appkit/nsmenuitem/1514850-userkeyequivalent?language=objc) for the menu item.
|
||||
|
||||
> [!NOTE]
|
||||
> This property is only initialized after the `MenuItem` has been added to a `Menu`. Either via `Menu.buildFromTemplate` or via `Menu.append()/insert()`. Accessing before initialization will just return `null`.
|
||||
@@ -170,7 +170,7 @@ This property can be dynamically changed.
|
||||
|
||||
#### `menuItem.sharingItem` _macOS_
|
||||
|
||||
A [`SharingItem`](structures/sharing-item.md) indicating the item to share when the `role` is `shareMenu`.
|
||||
A `SharingItem` indicating the item to share when the `role` is `shareMenu`.
|
||||
|
||||
This property can be dynamically changed.
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ this has the additional effect of removing the menu bar from the window.
|
||||
|
||||
> [!NOTE]
|
||||
> The default menu will be created automatically if the app does not set one.
|
||||
> It contains standard items such as `File`, `Edit`, `View`, and `Window`.
|
||||
> It contains standard items such as `File`, `Edit`, `View`, `Window` and `Help`.
|
||||
|
||||
#### `Menu.getApplicationMenu()`
|
||||
|
||||
@@ -70,7 +70,7 @@ for more information on macOS' native actions.
|
||||
|
||||
#### `Menu.buildFromTemplate(template)`
|
||||
|
||||
- `template` ([MenuItemConstructorOptions](menu-item.md#new-menuitemoptions) | [MenuItem](menu-item.md))[]
|
||||
- `template` (MenuItemConstructorOptions | [MenuItem](menu-item.md))[]
|
||||
|
||||
Returns [`Menu`](menu.md)
|
||||
|
||||
@@ -162,7 +162,7 @@ Emitted when a popup is closed either manually or with `menu.closePopup()`.
|
||||
|
||||
#### `menu.items`
|
||||
|
||||
A [`MenuItem[]`](menu-item.md) array containing the menu's items.
|
||||
A `MenuItem[]` array containing the menu's items.
|
||||
|
||||
Each `Menu` consists of multiple [`MenuItem`](menu-item.md) instances and each `MenuItem`
|
||||
can nest a `Menu` into its `submenu` property.
|
||||
|
||||
@@ -79,9 +79,8 @@ app.whenReady().then(() => {
|
||||
### `new Notification([options])`
|
||||
|
||||
* `options` Object (optional)
|
||||
* `id` string (optional) _macOS_ _Windows_ - A unique identifier for the notification. On macOS, maps to `UNNotificationRequest`'s [`identifier`](https://developer.apple.com/documentation/usernotifications/unnotificationrequest/identifier) property. On Windows, maps to the toast notification's [`Tag`](https://learn.microsoft.com/en-us/uwp/api/windows.ui.notifications.toastnotification.tag) property. Defaults to a random UUID if not provided or if an empty string is passed. This can be used to remove or update previously delivered notifications.
|
||||
* `groupId` string (optional) _macOS_ _Windows_ - A string identifier used to visually group notifications together in Notification Center / Action Center. On macOS, maps to `UNNotificationContent`'s [`threadIdentifier`](https://developer.apple.com/documentation/usernotifications/unnotificationcontent/threadidentifier) property. On Windows, maps to the toast notification's [`Group`](https://learn.microsoft.com/en-us/uwp/api/windows.ui.notifications.toastnotification.group) property.
|
||||
* `groupTitle` string (optional) _Windows_ - A title for the notification group header. When both `groupId` and `groupTitle` are specified, Windows will display a header above the notification that groups related notifications together. Maps to the toast notification's [`header`](https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/toast-headers) element.
|
||||
* `id` string (optional) _macOS_ - A unique identifier for the notification, mapping to `UNNotificationRequest`'s [`identifier`](https://developer.apple.com/documentation/usernotifications/unnotificationrequest/identifier) property. Defaults to a random UUID if not provided or if an empty string is passed. This can be used to remove or update previously delivered notifications.
|
||||
* `groupId` string (optional) _macOS_ - A string identifier used to visually group notifications together in Notification Center. Maps to `UNNotificationContent`'s [`threadIdentifier`](https://developer.apple.com/documentation/usernotifications/unnotificationcontent/threadidentifier) property.
|
||||
* `title` string (optional) - A title for the notification, which will be displayed at the top of the notification window when it is shown.
|
||||
* `subtitle` string (optional) _macOS_ - A subtitle for the notification, which will be displayed below the title.
|
||||
* `body` string (optional) - The body text of the notification, which will be displayed below the title or subtitle.
|
||||
@@ -330,17 +329,13 @@ app.whenReady().then(() => {
|
||||
|
||||
### Instance Properties
|
||||
|
||||
#### `notification.id` _macOS_ _Windows_ _Readonly_
|
||||
#### `notification.id` _macOS_ _Readonly_
|
||||
|
||||
A `string` property representing the unique identifier of the notification. This is set at construction time — either from the `id` option or as a generated UUID if none was provided.
|
||||
|
||||
#### `notification.groupId` _macOS_ _Windows_ _Readonly_
|
||||
#### `notification.groupId` _macOS_ _Readonly_
|
||||
|
||||
A `string` property representing the group identifier of the notification. Notifications with the same `groupId` will be visually grouped together in Notification Center (macOS) or Action Center (Windows).
|
||||
|
||||
#### `notification.groupTitle` _Windows_ _Readonly_
|
||||
|
||||
A `string` property representing the title of the notification group header.
|
||||
A `string` property representing the group identifier of the notification. Notifications with the same `groupId` will be visually grouped together in Notification Center.
|
||||
|
||||
#### `notification.title`
|
||||
|
||||
|
||||
@@ -59,7 +59,12 @@ On Windows, returns true once the app has emitted the `ready` event.
|
||||
|
||||
### `safeStorage.isAsyncEncryptionAvailable()`
|
||||
|
||||
Returns `Promise<Boolean>` - Whether encryption is available for asynchronous safeStorage operations.
|
||||
Returns `Promise<boolean>` - Resolves with whether encryption is available for
|
||||
asynchronous safeStorage operations.
|
||||
|
||||
The asynchronous encryptor is initialized lazily the first time this method,
|
||||
`encryptStringAsync`, or `decryptStringAsync` is called after the app is ready.
|
||||
The returned promise resolves once initialization completes.
|
||||
|
||||
### `safeStorage.encryptString(plainText)`
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
The actual output pixel format and color space of the texture should refer to [`OffscreenSharedTexture`](../structures/offscreen-shared-texture.md) object in the `paint` event.
|
||||
* `argb` - The requested output texture format is 8-bit unorm RGBA, with SRGB SDR color space.
|
||||
* `rgbaf16` - The requested output texture format is 16-bit float RGBA, with scRGB HDR color space.
|
||||
* `nv12` - The requested output texture format is 12bpp with Y plane followed by a 2x2 interleaved UV plane, with REC709 color space.
|
||||
* `deviceScaleFactor` number (optional) _Experimental_ - The device scale factor of the offscreen rendering output. If not set, will use `1` as default.
|
||||
* `contextIsolation` boolean (optional) - Whether to run Electron APIs and
|
||||
the specified `preload` script in a separate JavaScript context. Defaults
|
||||
|
||||
@@ -1585,6 +1585,20 @@ Centers the current text selection in web page.
|
||||
|
||||
Copy the image at the given position to the clipboard.
|
||||
|
||||
#### `contents.copyVideoFrameAt(x, y)`
|
||||
|
||||
* `x` Integer
|
||||
* `y` Integer
|
||||
|
||||
When executed on a video media element, copies the frame at (x, y) to the clipboard.
|
||||
|
||||
#### `contents.saveVideoFrameAs(x, y)`
|
||||
|
||||
* `x` Integer
|
||||
* `y` Integer
|
||||
|
||||
When executed on a video media element, shows a save dialog and saves the frame at (x, y) to disk.
|
||||
|
||||
#### `contents.paste()`
|
||||
|
||||
Executes the editing command `paste` in web page.
|
||||
|
||||
@@ -175,6 +175,20 @@ app.on('web-contents-created', (_, webContents) => {
|
||||
})
|
||||
```
|
||||
|
||||
#### `frame.copyVideoFrameAt(x, y)`
|
||||
|
||||
* `x` Integer
|
||||
* `y` Integer
|
||||
|
||||
When executed on a video media element, copies the frame at (x, y) to the clipboard.
|
||||
|
||||
#### `frame.saveVideoFrameAs(x, y)`
|
||||
|
||||
* `x` Integer
|
||||
* `y` Integer
|
||||
|
||||
When executed on a video media element, shows a save dialog and saves the frame at (x, y) to disk.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
#### `frame.ipc` _Readonly_
|
||||
|
||||
@@ -33,14 +33,10 @@ because it is invoked in the main process.
|
||||
Returns [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) | null
|
||||
|
||||
`features` is a comma-separated key-value list, following the standard format of
|
||||
the browser. For convenience, Electron will parse a subset of presentational
|
||||
[`BrowserWindowConstructorOptions`](structures/browser-window-options.md) out of
|
||||
this list (such as `width`, `height`, `x`, `y`, `show`, `frame`, `title`,
|
||||
`backgroundColor`). Because the renderer is untrusted, options that cause the
|
||||
main process to access the filesystem or that are otherwise privileged (such as
|
||||
`icon`) are ignored. For full control and better ergonomics, use
|
||||
`webContents.setWindowOpenHandler` to customize the BrowserWindow creation from
|
||||
the main process.
|
||||
the browser. Electron will parse [`BrowserWindowConstructorOptions`](structures/browser-window-options.md) out of this
|
||||
list where possible, for convenience. For full control and better ergonomics,
|
||||
consider using `webContents.setWindowOpenHandler` to customize the
|
||||
BrowserWindow creation.
|
||||
|
||||
A subset of [`WebPreferences`](structures/web-preferences.md) can be set directly,
|
||||
unnested, from the features string: `zoomFactor`, `nodeIntegration`, `javascript`,
|
||||
@@ -60,10 +56,9 @@ window.open('https://github.com', '_blank', 'top=500,left=200,frame=false,nodeIn
|
||||
enabled on the parent window.
|
||||
* JavaScript will always be disabled in the opened `window` if it is disabled on
|
||||
the parent window.
|
||||
* Features that are not handled by Chromium and not in Electron's allowlist of
|
||||
presentational `BrowserWindowConstructorOptions` are ignored. The raw
|
||||
`features` string is still available to the main process via
|
||||
`setWindowOpenHandler`.
|
||||
* Non-standard features (that are not handled by Chromium or Electron) given in
|
||||
`features` will be passed to any registered `webContents`'s
|
||||
`did-create-window` event handler in the `options` argument.
|
||||
* `frameName` follows the specification of `target` located in the [native documentation](https://developer.mozilla.org/en-US/docs/Web/API/Window/open#parameters).
|
||||
* When opening `about:blank`, the child window's [`WebPreferences`](structures/web-preferences.md) will be copied
|
||||
from the parent window, and there is no way to override it because Chromium
|
||||
|
||||
@@ -12,6 +12,34 @@ This document uses the following convention to categorize breaking changes:
|
||||
* **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
|
||||
* **Removed:** An API or feature was removed, and is no longer supported by Electron.
|
||||
|
||||
## Planned Breaking API Changes (43.0)
|
||||
|
||||
### Behavior Changed: Dialog methods default to Downloads directory
|
||||
|
||||
The `defaultPath` option for the following methods now defaults to the user's Downloads folder (or their home directory if Downloads doesn't exist) when not explicitly provided:
|
||||
|
||||
* `dialog.showOpenDialog`
|
||||
* `dialog.showOpenDialogSync`
|
||||
* `dialog.showSaveDialog`
|
||||
* `dialog.showSaveDialogSync`
|
||||
|
||||
Previously, when no `defaultPath` was provided, the underlying OS file dialog would determine the initial directory — typically remembering the last directory the user navigated to, or falling back to an OS-specific default. Now, Electron explicitly sets the initial directory to Downloads, which also means the OS will no longer track and restore the last-used directory between dialog invocations.
|
||||
|
||||
To preserve the old behavior, you can track the last-used directory yourself and pass it as `defaultPath`:
|
||||
|
||||
```js
|
||||
const path = require('node:path')
|
||||
|
||||
let lastUsedPath
|
||||
const result = await dialog.showOpenDialog({
|
||||
defaultPath: lastUsedPath
|
||||
})
|
||||
|
||||
if (!result.canceled && result.filePaths.length > 0) {
|
||||
lastUsedPath = path.dirname(result.filePaths[0])
|
||||
}
|
||||
```
|
||||
|
||||
## Planned Breaking API Changes (42.0)
|
||||
|
||||
### Behavior Changed: macOS notifications now use `UNNotification` API
|
||||
|
||||
@@ -79,7 +79,7 @@ $ ../../electron/script/git-import-patches ../../electron/patches/node
|
||||
$ ../../electron/script/git-export-patches -o ../../electron/patches/node
|
||||
```
|
||||
|
||||
Note that `git-import-patches` will mark the commit that was `HEAD` when it was run as `refs/patches/upstream-head` (and a checkout-specific `refs/patches/upstream-head-<hash>` so that gclient worktrees sharing a `.git/refs` directory don't clobber each other). This lets you keep track of which commits are from Electron patches (those that come after `refs/patches/upstream-head`) and which commits are in upstream (those before `refs/patches/upstream-head`).
|
||||
Note that `git-import-patches` will mark the commit that was `HEAD` when it was run as `refs/patches/upstream-head`. This lets you keep track of which commits are from Electron patches (those that come after `refs/patches/upstream-head`) and which commits are in upstream (those before `refs/patches/upstream-head`).
|
||||
|
||||
#### Resolving conflicts
|
||||
|
||||
|
||||
BIN
docs/images/versioning-sketch-0.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
docs/images/versioning-sketch-1.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
docs/images/versioning-sketch-2.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/images/versioning-sketch-3.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/images/versioning-sketch-4.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
docs/images/versioning-sketch-5.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/images/versioning-sketch-6.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
docs/images/versioning-sketch-7.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
@@ -2,53 +2,28 @@
|
||||
|
||||
Electron frequently releases major versions alongside every other Chromium release.
|
||||
This document focuses on the release cadence and version support policy.
|
||||
|
||||
> [!TIP]
|
||||
> See the [Electron Versioning](./electron-versioning.md) document for more details
|
||||
> on how Electron is versioned.
|
||||
For a more in-depth guide on our git branches and how Electron uses semantic versions,
|
||||
check out our [Electron Versioning](./electron-versioning.md) doc.
|
||||
|
||||
## Timeline
|
||||
|
||||
[Electron's Release Schedule](https://releases.electronjs.org/schedule) lists a schedule of Electron major releases showing key milestones including alpha, beta, and stable release dates, as well as end-of-life dates and dependency versions.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Electron's official support policy is the latest 3 stable releases. Our stable
|
||||
> release and end-of-life dates are determined by Chromium, and may be subject to
|
||||
> change. While we try to keep our planned release and end-of-life dates frequently
|
||||
> updated here, future dates may change if affected by upstream scheduling changes,
|
||||
> and may not always be accurately reflected.
|
||||
>
|
||||
> See [Chromium's public release schedule](https://chromiumdash.appspot.com/schedule) for
|
||||
> definitive information about Chromium's scheduled release dates.
|
||||
:::info Official support dates may change
|
||||
|
||||
Electron's cadence between major version releases is 8 weeks long. Before each major
|
||||
version hits stable, it goes through a four-week **alpha** phase and a four-week
|
||||
**beta** phase.
|
||||
Electron's official support policy is the latest 3 stable releases. Our stable
|
||||
release and end-of-life dates are determined by Chromium, and may be subject to
|
||||
change. While we try to keep our planned release and end-of-life dates frequently
|
||||
updated here, future dates may change if affected by upstream scheduling changes,
|
||||
and may not always be accurately reflected.
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title Electron release cycle
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat Week %W
|
||||
todayMarker off
|
||||
section v41
|
||||
Alpha phase :a1, 2026-01-19, 4w
|
||||
M146 enters Chrome beta :milestone, bm1, after a1, 0d
|
||||
Beta phase :b1, after a1, 4w
|
||||
M146 enters Chrome stable :milestone, s1, after b1, 0d
|
||||
Supported until v44 release :active, after b1, 12w
|
||||
section v42
|
||||
Alpha phase :a2, after b1, 4w
|
||||
M148 enters Chrome beta :milestone, bm2, after a2, 0d
|
||||
Beta phase :b2, after a2, 4w
|
||||
M148 enters Chrome stable :milestone, s2, after b2, 0d
|
||||
Supported until v45 release :active, after b2, 4w
|
||||
```
|
||||
See [Chromium's public release schedule](https://chromiumdash.appspot.com/schedule) for
|
||||
definitive information about Chromium's scheduled release dates.
|
||||
|
||||
:::
|
||||
|
||||
**Notes:**
|
||||
|
||||
* Alphas are generally less stable than beta releases. The cutoff between the two
|
||||
corresponds to when the underlying Chromium version enters Chrome's Beta channel.
|
||||
* The `-alpha.1`, `-beta.1`, and `stable` dates are our solid release dates.
|
||||
* We strive for weekly alpha/beta releases, but we often release more than scheduled.
|
||||
* All dates are our goals but there may be reasons for adjusting the stable deadline, such as security bugs.
|
||||
@@ -63,11 +38,10 @@ gantt
|
||||
## Version support policy
|
||||
|
||||
The latest three _stable_ major versions are supported by the Electron team.
|
||||
|
||||
For example, if the latest release is 42.1.x, then the 41.0.x as well
|
||||
as the 40.2.x series are supported. We only support the latest minor release
|
||||
For example, if the latest release is 6.1.x, then the 5.0.x as well
|
||||
as the 4.2.x series are supported. We only support the latest minor release
|
||||
for each stable release series. This means that in the case of a security fix,
|
||||
42.1.x will receive the fix, but we will not release a new version of 42.0.x.
|
||||
6.1.x will receive the fix, but we will not release a new version of 6.0.x.
|
||||
|
||||
The latest stable release unilaterally receives all fixes from `main`,
|
||||
and the version prior to that receives the vast majority of those fixes
|
||||
@@ -76,8 +50,11 @@ only security fixes directly.
|
||||
|
||||
### Chromium version support
|
||||
|
||||
> [!TIP]
|
||||
> Chromium's public release schedule is [here](https://chromiumdash.appspot.com/schedule).
|
||||
:::info Chromium release schedule
|
||||
|
||||
Chromium's public release schedule is [here](https://chromiumdash.appspot.com/schedule).
|
||||
|
||||
:::
|
||||
|
||||
Electron targets Chromium even-number versions, releasing every 8 weeks in concert
|
||||
with Chromium's 4-week release schedule. For example, Electron 26 uses Chromium 116, while Electron 27 uses Chromium 118.
|
||||
@@ -105,7 +82,3 @@ and that number is reduced to two in major version 10, the three-argument versio
|
||||
continue to work until, at minimum, major version 12. Past the minimum two-version
|
||||
threshold, we will attempt to support backwards compatibility beyond two versions
|
||||
until the maintainers feel the maintenance burden is too high to continue doing so.
|
||||
|
||||
> [!TIP]
|
||||
> For a canonical list of breaking changes, see the [Breaking Changes](../breaking-changes.md)
|
||||
> document.
|
||||
|
||||
@@ -14,6 +14,18 @@ To update an existing project to use the latest stable version:
|
||||
npm install --save-dev electron@latest
|
||||
```
|
||||
|
||||
## Versioning scheme
|
||||
|
||||
There are several major changes from our 1.x strategy outlined below. Each change is intended to satisfy the needs and priorities of developers/maintainers and app developers.
|
||||
|
||||
1. Strict use of the [SemVer](#semver) spec
|
||||
2. Introduction of semver-compliant `-beta` tags
|
||||
3. Introduction of [conventional commit messages](https://conventionalcommits.org/)
|
||||
4. Well-defined stabilization branches
|
||||
5. The `main` branch is versionless; only stabilization branches contain version information
|
||||
|
||||
We will cover in detail how git branching works, how npm tagging works, what developers should expect to see, and how one can backport changes.
|
||||
|
||||
## SemVer
|
||||
|
||||
Below is a table explicitly mapping types of changes to their corresponding category of SemVer (e.g. Major, Minor, Patch).
|
||||
@@ -22,7 +34,7 @@ Below is a table explicitly mapping types of changes to their corresponding cate
|
||||
| ------------------------------- | ---------------------------------- | ----------------------------- |
|
||||
| Electron breaking API changes | Electron non-breaking API changes | Electron bug fixes |
|
||||
| Node.js major version updates | Node.js minor version updates | Node.js patch version updates |
|
||||
| Chromium version updates | | fix-related Chromium patches |
|
||||
| Chromium version updates | | fix-related chromium patches |
|
||||
|
||||
For more information, see the [Semantic Versioning 2.0.0](https://semver.org/) spec.
|
||||
|
||||
@@ -32,189 +44,68 @@ Note that most Chromium updates will be considered breaking. Fixes that can be b
|
||||
|
||||
Stabilization branches are branches that run parallel to `main`, taking in only cherry-picked commits that are related to security or stability. These branches are never merged back to `main`.
|
||||
|
||||
```mermaid
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
branch N-x-y
|
||||
checkout main
|
||||
commit id:"fix-1"
|
||||
checkout N-x-y
|
||||
cherry-pick id:"fix-1"
|
||||
checkout main
|
||||
commit id:"fix-2"
|
||||
checkout N-x-y
|
||||
cherry-pick id:"fix-2"
|
||||
checkout main
|
||||
commit
|
||||
commit
|
||||

|
||||
|
||||
Since Electron 8, stabilization branches are always **major** version lines, and named against the following template `$MAJOR-x-y` e.g. `8-x-y`. Prior to that we used **minor** version lines and named them as `$MAJOR-$MINOR-x` e.g. `2-0-x`.
|
||||
|
||||
We allow for multiple stabilization branches to exist simultaneously, one for each supported version. For more details on which versions are supported, see our [Electron Releases](./electron-timelines.md) doc.
|
||||
|
||||

|
||||
|
||||
Older lines will not be supported by the Electron project, but other groups can take ownership and backport stability and security fixes on their own. We discourage this, but recognize that it makes life easier for many app developers.
|
||||
|
||||
## Beta releases and bug fixes
|
||||
|
||||
Developers want to know which releases are _safe_ to use. Even seemingly innocent features can introduce regressions in complex applications. At the same time, locking to a fixed version is dangerous because you’re ignoring security patches and bug fixes that may have come out since your version. Our goal is to allow the following standard semver ranges in `package.json` :
|
||||
|
||||
* Use `~2.0.0` to admit only stability or security related fixes to your `2.0.0` release.
|
||||
* Use `^2.0.0` to admit non-breaking _reasonably stable_ feature work as well as security and bug fixes.
|
||||
|
||||
What’s important about the second point is that apps using `^` should still be able to expect a reasonable level of stability. To accomplish this, SemVer allows for a _pre-release identifier_ to indicate a particular version is not yet _safe_ or _stable_.
|
||||
|
||||
Whatever you choose, you will periodically have to bump the version in your `package.json` as breaking changes are a fact of Chromium life.
|
||||
|
||||
The process is as follows:
|
||||
|
||||
1. All new major and minor releases lines begin with a beta series indicated by SemVer prerelease tags of `beta.N`, e.g. `2.0.0-beta.1`. After the first beta, subsequent beta releases must meet all of the following conditions:
|
||||
1. The change is backwards API-compatible (deprecations are allowed)
|
||||
2. The risk to meeting our stability timeline must be low.
|
||||
2. If allowed changes need to be made once a release is beta, they are applied and the prerelease tag is incremented, e.g. `2.0.0-beta.2`.
|
||||
3. If a particular beta release is _generally regarded_ as stable, it will be re-released as a stable build, changing only the version information. e.g. `2.0.0`. After the first stable, all changes must be backwards-compatible bug or security fixes.
|
||||
4. If future bug fixes or security patches need to be made once a release is stable, they are applied and the _patch_ version is incremented
|
||||
e.g. `2.0.1`.
|
||||
|
||||
Specifically, the above means:
|
||||
|
||||
1. Admitting non-breaking-API changes before Week 3 in the beta cycle is okay, even if those changes have the potential to cause moderate side-effects.
|
||||
2. Admitting feature-flagged changes, that do not otherwise alter existing code paths, at most points in the beta cycle is okay. Users can explicitly enable those flags in their apps.
|
||||
3. Admitting features of any sort after Week 3 in the beta cycle is 👎 without a very good reason.
|
||||
|
||||
For each major and minor bump, you should expect to see something like the following:
|
||||
|
||||
```plaintext
|
||||
2.0.0-beta.1
|
||||
2.0.0-beta.2
|
||||
2.0.0-beta.3
|
||||
2.0.0
|
||||
2.0.1
|
||||
2.0.2
|
||||
```
|
||||
|
||||
Since Electron 8, stabilization branches are always **major** version lines, and named against the following template `$MAJOR-x-y` e.g. `8-x-y`. (Prior to that, we used **minor** version lines and named them as `$MAJOR-$MINOR-x` e.g. `2-0-x`.)
|
||||
An example lifecycle in pictures:
|
||||
|
||||
We allow for multiple stabilization branches to exist simultaneously, one for each supported version.
|
||||
* A new release branch is created that includes the latest set of features. It is published as `2.0.0-beta.1`.
|
||||

|
||||
* A bug fix comes into master that can be backported to the release branch. The patch is applied, and a new beta is published as `2.0.0-beta.2`.
|
||||

|
||||
* The beta is considered _generally stable_ and it is published again as a non-beta under `2.0.0`.
|
||||

|
||||
* Later, a zero-day exploit is revealed and a fix is applied to master. We backport the fix to the `2-0-x` line and release `2.0.1`.
|
||||

|
||||
|
||||
> [!TIP]
|
||||
> For more details on which versions are supported, see our [Electron Releases](./electron-timelines.md) doc.
|
||||
A few examples of how various SemVer ranges will pick up new releases:
|
||||
|
||||
```mermaid
|
||||
gitGraph
|
||||
commit
|
||||
branch "41-x-y"
|
||||
checkout main
|
||||
commit
|
||||
commit
|
||||
commit id:"fix-a"
|
||||
checkout "41-x-y"
|
||||
cherry-pick id:"fix-a"
|
||||
checkout main
|
||||
commit
|
||||
commit id:"fix-b"
|
||||
checkout "41-x-y"
|
||||
cherry-pick id:"fix-b"
|
||||
checkout main
|
||||
commit
|
||||
branch "42-x-y"
|
||||
checkout main
|
||||
commit
|
||||
commit id:"fix-c"
|
||||
checkout "41-x-y"
|
||||
cherry-pick id:"fix-c"
|
||||
checkout "42-x-y"
|
||||
cherry-pick id:"fix-c"
|
||||
checkout main
|
||||
commit
|
||||
commit id:"fix-d"
|
||||
checkout "41-x-y"
|
||||
cherry-pick id:"fix-d"
|
||||
checkout "42-x-y"
|
||||
cherry-pick id:"fix-d"
|
||||
checkout main
|
||||
commit
|
||||
```
|
||||
|
||||
Older lines will not be supported by the Electron project.
|
||||
|
||||
## Release cycle
|
||||
|
||||
Electron follows an **8-week regular release cycle** where key milestones correspond to
|
||||
matching dates in the Chromium release cycle.
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title Electron release cycle
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat Week %W
|
||||
todayMarker off
|
||||
section v41
|
||||
Alpha phase :a1, 2026-01-19, 4w
|
||||
M146 enters Chrome beta :milestone, bm1, after a1, 0d
|
||||
Beta phase :b1, after a1, 4w
|
||||
M146 enters Chrome stable :milestone, s1, after b1, 0d
|
||||
Supported until v44 release :active, after b1, 12w
|
||||
section v42
|
||||
Alpha phase :a2, after b1, 4w
|
||||
M148 enters Chrome beta :milestone, bm2, after a2, 0d
|
||||
Beta phase :b2, after a2, 4w
|
||||
M148 enters Chrome stable :milestone, s2, after b2, 0d
|
||||
Supported until v45 release :active, after b2, 4w
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
When Electron 41 hits its stable release, the release line for Electron 42 is branched off of `main`.
|
||||
Its first alpha release is created with all the changes contained on `main`:
|
||||
|
||||
```mermaid
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
commit
|
||||
branch "42-x-y"
|
||||
checkout "42-x-y"
|
||||
commit tag:"v42.0.0-alpha.1"
|
||||
```
|
||||
|
||||
A bug fix comes into `main` that can be backported to the release branch. The patch is applied,
|
||||
and it is published in the next `v42.0.0-alpha.2` release.
|
||||
|
||||
```mermaid
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
commit
|
||||
branch "42-x-y"
|
||||
checkout "42-x-y"
|
||||
commit id:"42.0.0-alpha.1" tag:"v42.0.0-alpha.1"
|
||||
checkout "main"
|
||||
commit
|
||||
commit id:"fix-1"
|
||||
checkout "42-x-y"
|
||||
cherry-pick id:"fix-1" tag:"v42.0.0-alpha.2"
|
||||
```
|
||||
|
||||
The version of Chromium that powers Electron 42 hits Chrome's beta channel. The `alpha` line is
|
||||
promoted to `beta`.
|
||||
|
||||
```mermaid
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
commit
|
||||
branch "42-x-y"
|
||||
checkout "42-x-y"
|
||||
commit id:"42.0.0-alpha.1" tag:"v42.0.0-alpha.1"
|
||||
checkout "main"
|
||||
commit
|
||||
commit id:"fix-1"
|
||||
checkout "42-x-y"
|
||||
cherry-pick id:"fix-1" tag:"v42.0.0-alpha.2"
|
||||
checkout "main"
|
||||
commit
|
||||
commit
|
||||
commit id:"fix-2"
|
||||
checkout "42-x-y"
|
||||
cherry-pick id:"fix-2" tag:"v42.0.0-beta.1"
|
||||
```
|
||||
|
||||
Beta releases continue weekly until Electron 42 is promoted to stable and the same cycle starts again
|
||||
with `43-x-y`. Later, a zero-day exploit is revealed and a fix is applied to `main`. We backport the
|
||||
fix to the `42-x-y` line and release `42.0.1`.
|
||||
|
||||
```mermaid
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
commit
|
||||
branch "42-x-y"
|
||||
checkout "42-x-y"
|
||||
commit id:"42.0.0-alpha.1" tag:"v42.0.0-alpha.1"
|
||||
checkout "main"
|
||||
commit
|
||||
commit id:"fix-1"
|
||||
checkout "42-x-y"
|
||||
cherry-pick id:"fix-1" tag:"v42.0.0-alpha.2"
|
||||
checkout "main"
|
||||
commit
|
||||
commit
|
||||
commit id:"fix-2"
|
||||
checkout "42-x-y"
|
||||
cherry-pick id:"fix-2" tag:"v42.0.0-beta.1"
|
||||
checkout "main"
|
||||
commit id:"fix-3"
|
||||
checkout "42-x-y"
|
||||
cherry-pick id:"fix-3" tag:"v42.0.0"
|
||||
checkout "main"
|
||||
branch "43-x-y"
|
||||
checkout "43-x-y"
|
||||
commit id:"43.0.0-alpha.1" tag:"v43.0.0-alpha.1"
|
||||
checkout "main"
|
||||
commit id:"security-fix"
|
||||
checkout "42-x-y"
|
||||
cherry-pick id:"security-fix" tag:"v42.0.1"
|
||||
checkout "43-x-y"
|
||||
cherry-pick id:"security-fix" tag:"v43.0.0-alpha.2"
|
||||
```
|
||||

|
||||
|
||||
### Backport request process
|
||||
|
||||
@@ -245,11 +136,10 @@ The `electron/electron` repository also enforces squash merging, so you only nee
|
||||
|
||||
## Versioned `main` branch
|
||||
|
||||
* The `main` branch always corresponds to the major version above the current pre-release line.
|
||||
* Unstable nightly releases of `main` are released under the [`electron-nightly`](https://www.npmjs.com/package/electron-nightly)
|
||||
package on npm.
|
||||
* The `main` branch will always contain the next major version `X.0.0-nightly.DATE` in its `package.json`.
|
||||
* Release branches are never merged back to `main`.
|
||||
* All `package.json` values are fixed at `0.0.0-development`.
|
||||
* Release branches _do_ contain the correct version in their `package.json`.
|
||||
* As soon as a release branch is cut for a major, `main` must be bumped to the next major (i.e. `main` is always versioned as the next theoretical release branch).
|
||||
|
||||
## Historical versioning (Electron 1.X)
|
||||
|
||||
@@ -257,29 +147,6 @@ Electron versions _< 2.0_ did not conform to the [SemVer](https://semver.org) sp
|
||||
|
||||
Here is an example of the 1.x strategy:
|
||||
|
||||
```mermaid
|
||||
---
|
||||
config:
|
||||
gitGraph:
|
||||
mainBranchName: 'master'
|
||||
---
|
||||
gitGraph
|
||||
commit
|
||||
branch "bugfix-1"
|
||||
checkout "bugfix-1"
|
||||
commit
|
||||
checkout master
|
||||
merge "bugfix-1" tag:"1.8.1"
|
||||
branch "feature"
|
||||
checkout "feature"
|
||||
commit
|
||||
checkout master
|
||||
merge "feature" tag:"1.8.2"
|
||||
branch "bugfix-2"
|
||||
checkout "bugfix-2"
|
||||
commit
|
||||
checkout master
|
||||
merge "bugfix-2" tag:"1.8.3"
|
||||
```
|
||||

|
||||
|
||||
An app developed with `1.8.1` cannot take the `1.8.3` bug fix without either absorbing the `1.8.2` feature, or by backporting the fix and maintaining a new release line.
|
||||
|
||||
@@ -25,27 +25,6 @@ included in the `electron` package:
|
||||
npx install-electron --no
|
||||
```
|
||||
|
||||
## Installing prereleases
|
||||
|
||||
Electron [distributes experimental releases of future major versions](./electron-timelines.md)
|
||||
via npm as well.
|
||||
|
||||
Nightly builds contain the latest changes from the `main` branch:
|
||||
|
||||
```sh
|
||||
npm install electron-nightly --save-dev
|
||||
```
|
||||
|
||||
Alpha and beta builds contain changes slated for the next major version:
|
||||
|
||||
```sh
|
||||
npm install electron@alpha --save-dev
|
||||
npm install electron@beta --save-dev
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> For more information on available Electron releases, see the [Release Status dashboard](https://releases.electronjs.org).
|
||||
|
||||
## Running Electron ad-hoc
|
||||
|
||||
If you're in a pinch and would prefer to not use `npm install` in your local
|
||||
|
||||
@@ -87,6 +87,13 @@ if (!gotTheLock) {
|
||||
// Create mainWindow, load the rest of the app, etc...
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
// Check for deep link on cold start
|
||||
if (process.argv.length >= 2) {
|
||||
const lastArg = process.argv[process.argv.length - 1]
|
||||
if (lastArg.startsWith('electron-fiddle://')) {
|
||||
dialog.showErrorBox('Welcome Back', `You arrived from: ${lastArg}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
@@ -179,6 +179,7 @@ auto_filenames = {
|
||||
"lib/common/define-properties.ts",
|
||||
"lib/common/deprecate.ts",
|
||||
"lib/common/ipc-messages.ts",
|
||||
"lib/common/timers-shim.ts",
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/common/webpack-globals-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
|
||||
@@ -432,10 +432,6 @@ filenames = {
|
||||
"shell/browser/media/media_capture_devices_dispatcher.h",
|
||||
"shell/browser/media/media_device_id_salt.cc",
|
||||
"shell/browser/media/media_device_id_salt.h",
|
||||
"shell/browser/metrics/electron_metrics_log_uploader.cc",
|
||||
"shell/browser/metrics/electron_metrics_log_uploader.h",
|
||||
"shell/browser/metrics/electron_metrics_service_client.cc",
|
||||
"shell/browser/metrics/electron_metrics_service_client.h",
|
||||
"shell/browser/microtasks_runner.cc",
|
||||
"shell/browser/microtasks_runner.h",
|
||||
"shell/browser/native_window.cc",
|
||||
@@ -512,10 +508,6 @@ filenames = {
|
||||
"shell/browser/session_preferences.h",
|
||||
"shell/browser/special_storage_policy.cc",
|
||||
"shell/browser/special_storage_policy.h",
|
||||
"shell/browser/tracing/electron_background_tracing_metrics_provider.cc",
|
||||
"shell/browser/tracing/electron_background_tracing_metrics_provider.h",
|
||||
"shell/browser/tracing/electron_tracing_delegate.cc",
|
||||
"shell/browser/tracing/electron_tracing_delegate.h",
|
||||
"shell/browser/ui/accelerator_util.cc",
|
||||
"shell/browser/ui/accelerator_util.h",
|
||||
"shell/browser/ui/autofill_popup.cc",
|
||||
@@ -789,8 +781,6 @@ filenames = {
|
||||
"shell/browser/extensions/electron_extension_system_factory.h",
|
||||
"shell/browser/extensions/electron_extension_system.cc",
|
||||
"shell/browser/extensions/electron_extension_system.h",
|
||||
"shell/browser/extensions/electron_extension_tab_util.cc",
|
||||
"shell/browser/extensions/electron_extension_tab_util.h",
|
||||
"shell/browser/extensions/electron_extension_web_contents_observer.cc",
|
||||
"shell/browser/extensions/electron_extension_web_contents_observer.h",
|
||||
"shell/browser/extensions/electron_extensions_api_client.cc",
|
||||
@@ -803,8 +793,6 @@ filenames = {
|
||||
"shell/browser/extensions/electron_kiosk_delegate.h",
|
||||
"shell/browser/extensions/electron_messaging_delegate.cc",
|
||||
"shell/browser/extensions/electron_messaging_delegate.h",
|
||||
"shell/browser/extensions/electron_navigation_ui_data.cc",
|
||||
"shell/browser/extensions/electron_navigation_ui_data.h",
|
||||
"shell/browser/extensions/electron_process_manager_delegate.cc",
|
||||
"shell/browser/extensions/electron_process_manager_delegate.h",
|
||||
"shell/common/extensions/electron_extensions_api_provider.cc",
|
||||
|
||||
@@ -437,6 +437,14 @@ WebContents.prototype.loadURL = function (url, options) {
|
||||
return p;
|
||||
};
|
||||
|
||||
WebContents.prototype.copyVideoFrameAt = function (x: number, y: number) {
|
||||
this.mainFrame.copyVideoFrameAt(x, y);
|
||||
};
|
||||
|
||||
WebContents.prototype.saveVideoFrameAs = function (x: number, y: number) {
|
||||
this.mainFrame.saveVideoFrameAs(x, y);
|
||||
};
|
||||
|
||||
WebContents.prototype.setWindowOpenHandler = function (handler: (details: Electron.HandlerDetails) => Electron.WindowOpenHandlerResponse) {
|
||||
this._windowOpenHandler = handler;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Menu } from 'electron/main';
|
||||
import { shell } from 'electron/common';
|
||||
import { app, Menu } from 'electron/main';
|
||||
|
||||
const isMac = process.platform === 'darwin';
|
||||
|
||||
@@ -11,13 +12,47 @@ export const setApplicationMenuWasSet = () => {
|
||||
export const setDefaultApplicationMenu = () => {
|
||||
if (applicationMenuWasSet) return;
|
||||
|
||||
const helpMenu: Electron.MenuItemConstructorOptions = {
|
||||
role: 'help',
|
||||
submenu: app.isPackaged
|
||||
? []
|
||||
: [
|
||||
{
|
||||
label: 'Learn More',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://electronjs.org');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Documentation',
|
||||
click: async () => {
|
||||
const version = process.versions.electron;
|
||||
await shell.openExternal(`https://github.com/electron/electron/tree/v${version}/docs#readme`);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Community Discussions',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://discord.gg/electronjs');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Search Issues',
|
||||
click: async () => {
|
||||
await shell.openExternal('https://github.com/electron/electron/issues');
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const macAppMenu: Electron.MenuItemConstructorOptions = { role: 'appMenu' };
|
||||
const template: Electron.MenuItemConstructorOptions[] = [
|
||||
...(isMac ? [macAppMenu] : []),
|
||||
{ role: 'fileMenu' },
|
||||
{ role: 'editMenu' },
|
||||
{ role: 'viewMenu' },
|
||||
{ role: 'windowMenu' }
|
||||
{ role: 'windowMenu' },
|
||||
helpMenu
|
||||
];
|
||||
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
|
||||
@@ -78,27 +78,6 @@ export function parseWebViewWebPreferences (preferences: string) {
|
||||
const allowedWebPreferences = ['zoomFactor', 'nodeIntegration', 'javascript', 'contextIsolation', 'webviewTag'] as const;
|
||||
type AllowedWebPreference = (typeof allowedWebPreferences)[number];
|
||||
|
||||
// Top-level BrowserWindow options that may be set via the window.open()
|
||||
// features string. Options not listed here are silently dropped; apps that
|
||||
// need to pass other options should use setWindowOpenHandler in the main
|
||||
// process.
|
||||
const allowedWindowOptions = new Set<string>([
|
||||
// standard window.open() position/size features
|
||||
'top', 'left', 'innerWidth', 'innerHeight',
|
||||
// numeric
|
||||
'x', 'y', 'width', 'height',
|
||||
'minWidth', 'minHeight', 'maxWidth', 'maxHeight', 'opacity',
|
||||
// presentational booleans
|
||||
'show', 'center', 'useContentSize', 'frame', 'transparent', 'hasShadow',
|
||||
'movable', 'closable', 'focusable', 'minimizable', 'maximizable',
|
||||
'fullscreenable', 'alwaysOnTop', 'skipTaskbar', 'modal', 'acceptFirstMouse',
|
||||
'autoHideMenuBar', 'enableLargerThanScreen', 'paintWhenInitiallyHidden',
|
||||
'roundedCorners', 'thickFrame', 'disableAutoHideCursor', 'hiddenInMissionControl',
|
||||
// presentational strings (no filesystem/network side effects)
|
||||
'title', 'backgroundColor', 'tabbingIdentifier', 'titleBarStyle', 'vibrancy',
|
||||
'visualEffectState', 'backgroundMaterial'
|
||||
]);
|
||||
|
||||
/**
|
||||
* Parses a feature string that has the format used in window.open().
|
||||
*/
|
||||
@@ -121,15 +100,8 @@ export function parseFeatures (features: string) {
|
||||
if (parsed.left !== undefined) parsed.x = parsed.left;
|
||||
if (parsed.top !== undefined) parsed.y = parsed.top;
|
||||
|
||||
const options: { [key: string]: CoercedValue } = {};
|
||||
for (const key of Object.keys(parsed)) {
|
||||
if (allowedWindowOptions.has(key)) {
|
||||
options[key] = parsed[key];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
options: options as Omit<BrowserWindowConstructorOptions, 'webPreferences'>,
|
||||
options: parsed as Omit<BrowserWindowConstructorOptions, 'webPreferences'>,
|
||||
webPreferences
|
||||
};
|
||||
}
|
||||
|
||||
@@ -135,10 +135,10 @@ const asarStatsToFsStats = function (stats: NodeJS.AsarFileStat) {
|
||||
uid,
|
||||
gid,
|
||||
0, // rdev
|
||||
4096, // blksize
|
||||
undefined, // blksize
|
||||
++nextInode, // ino
|
||||
stats.size,
|
||||
Math.ceil(stats.size / 512), // blocks (512-byte units)
|
||||
undefined, // blocks,
|
||||
fakeTime.getTime(), // atim_msec
|
||||
fakeTime.getTime(), // mtim_msec
|
||||
fakeTime.getTime(), // ctim_msec
|
||||
|
||||
@@ -124,7 +124,9 @@ if (nodeIntegration) {
|
||||
delete (global as any).setImmediate;
|
||||
delete (global as any).clearImmediate;
|
||||
delete (global as any).global;
|
||||
// eslint-disable-next-line n/no-deprecated-api
|
||||
delete (global as any).root;
|
||||
// eslint-disable-next-line n/no-deprecated-api
|
||||
delete (global as any).GLOBAL;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
"@azure/storage-blob": "^12.28.0",
|
||||
"@datadog/datadog-ci": "^4.1.2",
|
||||
"@datadog/datadog-ci": "^5.9.1",
|
||||
"@electron/asar": "^4.0.1",
|
||||
"@electron/docs-parser": "^2.0.0",
|
||||
"@electron/fiddle-core": "^1.3.4",
|
||||
@@ -32,11 +32,11 @@
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-markdown": "^5.1.0",
|
||||
"eslint-plugin-mocha": "^10.5.0",
|
||||
"eslint-plugin-n": "^16.6.2",
|
||||
"eslint-plugin-n": "^17.24.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^6.6.0",
|
||||
"events": "^3.2.0",
|
||||
"folder-hash": "^4.1.1",
|
||||
"folder-hash": "^4.1.2",
|
||||
"got": "^11.8.5",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.1.0",
|
||||
|
||||
@@ -10,10 +10,10 @@ this patch is required to provide ripemd160 support in the nodejs crypto
|
||||
module.
|
||||
|
||||
diff --git a/crypto/digest/digest_extra.cc b/crypto/digest/digest_extra.cc
|
||||
index d38e0c1132da60ec96c3a5c2416ff07589f03b80..cd60baaf22a8d5dc20544d861d36b7d74d986e7b 100644
|
||||
index 17961ba6bd9de78b5b1b1008eb1f73babd49d0e7..6a870dce37df8f49106c24b183308a2c7a03fd7d 100644
|
||||
--- a/crypto/digest/digest_extra.cc
|
||||
+++ b/crypto/digest/digest_extra.cc
|
||||
@@ -48,6 +48,7 @@ static const struct nid_to_digest nid_to_digest_mapping[] = {
|
||||
@@ -47,6 +47,7 @@ static const struct nid_to_digest nid_to_digest_mapping[] = {
|
||||
{NID_sha512, EVP_sha512, SN_sha512, LN_sha512},
|
||||
{NID_sha512_256, EVP_sha512_256, SN_sha512_256, LN_sha512_256},
|
||||
{NID_md5_sha1, EVP_md5_sha1, SN_md5_sha1, LN_md5_sha1},
|
||||
@@ -62,10 +62,10 @@ index a246a51103701e0ac8a0722324350a462f95bcc9..ddf0a90337d4e40de09bc345cf959dff
|
||||
+
|
||||
#undef CHECK
|
||||
diff --git a/decrepit/evp/evp_do_all.cc b/decrepit/evp/evp_do_all.cc
|
||||
index 584b1390a841cc1b1dcb69e16d8242a88e4bb9cb..637aeccb8de8d793eabc38e32bef6834ac0e6ad3 100644
|
||||
index feaf17c72cecb8099bc11ac10747fbad719ddca9..891a73f229e3f0838cb2fa99b8fb24fdeac1962b 100644
|
||||
--- a/decrepit/evp/evp_do_all.cc
|
||||
+++ b/decrepit/evp/evp_do_all.cc
|
||||
@@ -82,6 +82,7 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *md,
|
||||
@@ -79,6 +79,7 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher,
|
||||
callback(EVP_sha384(), "SHA384", nullptr, arg);
|
||||
callback(EVP_sha512(), "SHA512", nullptr, arg);
|
||||
callback(EVP_sha512_256(), "SHA512-256", nullptr, arg);
|
||||
@@ -73,16 +73,16 @@ index 584b1390a841cc1b1dcb69e16d8242a88e4bb9cb..637aeccb8de8d793eabc38e32bef6834
|
||||
|
||||
callback(EVP_md4(), "md4", nullptr, arg);
|
||||
callback(EVP_md5(), "md5", nullptr, arg);
|
||||
@@ -91,6 +92,7 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *md,
|
||||
@@ -88,6 +89,7 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher,
|
||||
callback(EVP_sha384(), "sha384", nullptr, arg);
|
||||
callback(EVP_sha512(), "sha512", nullptr, arg);
|
||||
callback(EVP_sha512_256(), "sha512-256", nullptr, arg);
|
||||
+ callback(EVP_ripemd160(), "ripemd160", nullptr, arg);
|
||||
}
|
||||
|
||||
void EVP_MD_do_all(void (*callback)(const EVP_MD *md, const char *name,
|
||||
void EVP_MD_do_all(void (*callback)(const EVP_MD *cipher, const char *name,
|
||||
diff --git a/include/openssl/digest.h b/include/openssl/digest.h
|
||||
index 62ad57368cb3059ee25df08bb07876fef499de2e..322daef194b3c7b73011419bb74bccb311eb03a5 100644
|
||||
index 40670234682ac00dec268dea43f0ee1e39e8684f..293fbc9faf01ea0ca4e58b0a65b14597fe4916a6 100644
|
||||
--- a/include/openssl/digest.h
|
||||
+++ b/include/openssl/digest.h
|
||||
@@ -48,6 +48,9 @@ OPENSSL_EXPORT const EVP_MD *EVP_blake2b256(void);
|
||||
|
||||
@@ -64,10 +64,10 @@ index dabc54aa13745600a62e57ecbb427e48a4565282..ce213e00573102ce9405a794d3c140d9
|
||||
|
||||
const EVP_CIPHER *EVP_get_cipherbynid(int nid) {
|
||||
diff --git a/decrepit/evp/evp_do_all.cc b/decrepit/evp/evp_do_all.cc
|
||||
index 637aeccb8de8d793eabc38e32bef6834ac0e6ad3..c5dd0b18d7338457e47ae47088d9822472b24212 100644
|
||||
index 891a73f229e3f0838cb2fa99b8fb24fdeac1962b..f7d0c5dc66f016eb9338c15e7f5ef59e6de2969d 100644
|
||||
--- a/decrepit/evp/evp_do_all.cc
|
||||
+++ b/decrepit/evp/evp_do_all.cc
|
||||
@@ -23,8 +23,10 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
@@ -20,8 +20,10 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
const char *unused, void *arg),
|
||||
void *arg) {
|
||||
callback(EVP_aes_128_cbc(), "AES-128-CBC", nullptr, arg);
|
||||
@@ -78,7 +78,7 @@ index 637aeccb8de8d793eabc38e32bef6834ac0e6ad3..c5dd0b18d7338457e47ae47088d98224
|
||||
callback(EVP_aes_128_ctr(), "AES-128-CTR", nullptr, arg);
|
||||
callback(EVP_aes_192_ctr(), "AES-192-CTR", nullptr, arg);
|
||||
callback(EVP_aes_256_ctr(), "AES-256-CTR", nullptr, arg);
|
||||
@@ -37,9 +39,13 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
@@ -34,9 +36,13 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
callback(EVP_aes_128_gcm(), "AES-128-GCM", nullptr, arg);
|
||||
callback(EVP_aes_192_gcm(), "AES-192-GCM", nullptr, arg);
|
||||
callback(EVP_aes_256_gcm(), "AES-256-GCM", nullptr, arg);
|
||||
@@ -92,7 +92,7 @@ index 637aeccb8de8d793eabc38e32bef6834ac0e6ad3..c5dd0b18d7338457e47ae47088d98224
|
||||
callback(EVP_des_ede_cbc(), "DES-EDE-CBC", nullptr, arg);
|
||||
callback(EVP_des_ede3_cbc(), "DES-EDE3-CBC", nullptr, arg);
|
||||
callback(EVP_rc2_cbc(), "RC2-CBC", nullptr, arg);
|
||||
@@ -47,8 +53,10 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
@@ -44,8 +50,10 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
|
||||
// OpenSSL returns everything twice, the second time in lower case.
|
||||
callback(EVP_aes_128_cbc(), "aes-128-cbc", nullptr, arg);
|
||||
@@ -103,7 +103,7 @@ index 637aeccb8de8d793eabc38e32bef6834ac0e6ad3..c5dd0b18d7338457e47ae47088d98224
|
||||
callback(EVP_aes_128_ctr(), "aes-128-ctr", nullptr, arg);
|
||||
callback(EVP_aes_192_ctr(), "aes-192-ctr", nullptr, arg);
|
||||
callback(EVP_aes_256_ctr(), "aes-256-ctr", nullptr, arg);
|
||||
@@ -61,9 +69,13 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
@@ -58,9 +66,13 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
|
||||
callback(EVP_aes_128_gcm(), "aes-128-gcm", nullptr, arg);
|
||||
callback(EVP_aes_192_gcm(), "aes-192-gcm", nullptr, arg);
|
||||
callback(EVP_aes_256_gcm(), "aes-256-gcm", nullptr, arg);
|
||||
|
||||
@@ -125,6 +125,7 @@ feat_separate_content_settings_callback_for_sync_and_async_clipboard.patch
|
||||
fix_win32_synchronous_spellcheck.patch
|
||||
chore_grandfather_in_electron_views_and_delegates.patch
|
||||
refactor_patch_electron_permissiontypes_into_blink.patch
|
||||
revert_views_remove_desktopwindowtreehostwin_window_enlargement.patch
|
||||
fix_add_macos_memory_query_fallback_to_avoid_crash.patch
|
||||
fix_resolve_dynamic_background_material_update_issue_on_windows_11.patch
|
||||
feat_add_support_for_embedder_snapshot_validation.patch
|
||||
@@ -145,15 +146,7 @@ fix_set_correct_app_id_on_linux.patch
|
||||
fix_pass_trigger_for_global_shortcuts_on_wayland.patch
|
||||
feat_plumb_node_integration_in_worker_through_workersettings.patch
|
||||
fix_restore_sdk_inputs_cross-toolchain_deps_for_macos.patch
|
||||
fix_use_fresh_lazynow_for_onendworkitemimpl_after_didruntask.patch
|
||||
fix_pulseaudio_stream_and_icon_names.patch
|
||||
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
|
||||
feat_allow_enabling_extensions_on_custom_protocols.patch
|
||||
fix_initialize_com_on_desktopmedialistcapturethread_on_windows.patch
|
||||
fix_use_fresh_lazynow_for_onendworkitemimpl_after_didruntask.patch
|
||||
cherry-pick-b173791bf402.patch
|
||||
cherry-pick-be87466afecb.patch
|
||||
cherry-pick-c0390bcd64ba.patch
|
||||
cherry-pick-1b69067db7d2.patch
|
||||
cherry-pick-d513cd2fe668.patch
|
||||
cherry-pick-dc5e20c4c055.patch
|
||||
cherry-pick-847b11ad2fa3.patch
|
||||
cherry-pick-fc79e8cc2dfc.patch
|
||||
|
||||
@@ -33,7 +33,7 @@ index 26619daf25f3cc455d2dba7b5f16c9449e6103c1..387fca1b54b818a5af435e96bf8f435e
|
||||
client->PostSandboxInitialized();
|
||||
}
|
||||
diff --git a/content/public/gpu/content_gpu_client.h b/content/public/gpu/content_gpu_client.h
|
||||
index ad511f0966c29e46a1e4c07e09c3172b38c7c906..ca3a35d213147c6fcb9fbbbe118c15a3075875fa 100644
|
||||
index e5389b44df98ab1a5c976524a66a26c763e5c436..4a183b4959fae18e6875440e6570b8ada6823d81 100644
|
||||
--- a/content/public/gpu/content_gpu_client.h
|
||||
+++ b/content/public/gpu/content_gpu_client.h
|
||||
@@ -36,6 +36,10 @@ class CONTENT_EXPORT ContentGpuClient {
|
||||
|
||||
@@ -23,10 +23,10 @@ index 3f8cf4edc7448e6b584adae8fcbb872d27377126..1d03dc809d4c18f24314d94811e0bf52
|
||||
int32_t world_id) {}
|
||||
virtual void DidClearWindowObject() {}
|
||||
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
|
||||
index ab959e66f8841d7367863bb13d6c7a0854d0df23..5279ba15f45bd7634b5f24553ad64c0069318cc0 100644
|
||||
index 0e64b0a0ac8387ab15b201a9fc0f0fd36cd5ab29..f28214d369138eb854a556165f0a946c07cfdb9c 100644
|
||||
--- a/content/renderer/render_frame_impl.cc
|
||||
+++ b/content/renderer/render_frame_impl.cc
|
||||
@@ -4733,6 +4733,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context,
|
||||
@@ -4731,6 +4731,12 @@ void RenderFrameImpl::DidCreateScriptContext(v8::Local<v8::Context> context,
|
||||
observer.DidCreateScriptContext(context, world_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Subject: allow disabling blink scheduler throttling per RenderView
|
||||
This allows us to disable throttling for hidden windows.
|
||||
|
||||
diff --git a/content/browser/renderer_host/navigation_controller_impl_unittest.cc b/content/browser/renderer_host/navigation_controller_impl_unittest.cc
|
||||
index f5a6ffc61f6cdff3897a97003b74838aac27e2a1..9b10aeb457a010db0ab89211610ea97b1a364453 100644
|
||||
index 29d5b174e122cbd140554687548106ead8f8e8d9..da74da96c3fe35a0f3838f04bca08846f7b41abe 100644
|
||||
--- a/content/browser/renderer_host/navigation_controller_impl_unittest.cc
|
||||
+++ b/content/browser/renderer_host/navigation_controller_impl_unittest.cc
|
||||
@@ -168,6 +168,12 @@ class MockPageBroadcast : public blink::mojom::PageBroadcast {
|
||||
@@ -51,7 +51,7 @@ index 89fed16c112d55c13a9f23695e2898d630f7d815..b7f486337f46daac015644525c9870f5
|
||||
void SendRendererPreferencesToRenderer(
|
||||
const blink::RendererPreferences& preferences);
|
||||
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
|
||||
index 53ec5cd693539d74424c683f78e953e85c13c098..ccfe78580c2acb9a3afa43d246e1a83cc0e28598 100644
|
||||
index 79bd8d43a71731e5076196877448462656a04d48..b5d7f50817f503956f19fcea687b5b0751268b44 100644
|
||||
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
|
||||
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
|
||||
@@ -655,8 +655,8 @@ void RenderWidgetHostViewAura::ShowImpl(PageVisibilityState page_visibility) {
|
||||
@@ -116,7 +116,7 @@ index 932658273154ef2e022358e493a8e7c00c86e732..57bbfb5cde62c9496c351c861880a189
|
||||
// Visibility -----------------------------------------------------------
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
|
||||
index b5a7e1b177f031837f670c26bff7394315eb6ea5..ed63aa041733e2fb09d77a219c93c322985cc81e 100644
|
||||
index 611ecffa47703196dc40550b1e920afc4c1be716..be26284387dcfa4e72592862f313a2c7e9a81d1b 100644
|
||||
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
|
||||
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
|
||||
@@ -2471,6 +2471,10 @@ void WebViewImpl::SetPageLifecycleStateInternal(
|
||||
@@ -130,7 +130,7 @@ index b5a7e1b177f031837f670c26bff7394315eb6ea5..ed63aa041733e2fb09d77a219c93c322
|
||||
bool storing_in_bfcache = new_state->is_in_back_forward_cache &&
|
||||
!old_state->is_in_back_forward_cache;
|
||||
bool restoring_from_bfcache = !new_state->is_in_back_forward_cache &&
|
||||
@@ -4170,10 +4174,23 @@ PageScheduler* WebViewImpl::Scheduler() const {
|
||||
@@ -4163,10 +4167,23 @@ PageScheduler* WebViewImpl::Scheduler() const {
|
||||
return GetPage()->GetPageScheduler();
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ index b5a7e1b177f031837f670c26bff7394315eb6ea5..ed63aa041733e2fb09d77a219c93c322
|
||||
// Do not throttle if the page should be painting.
|
||||
bool is_visible =
|
||||
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
|
||||
index b2ad789e53146b06e0e416f2dcf384cf7e9c17ae..838c67ac5b02c427858febbfbddf25fb03632b37 100644
|
||||
index 8d5c7349c360726778e37976fc54d660d7424f1f..96ee25c8ae4b50ab265bd698517efe15e2f1f44d 100644
|
||||
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
|
||||
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
|
||||
@@ -446,6 +446,7 @@ class CORE_EXPORT WebViewImpl final : public WebView,
|
||||
|
||||
@@ -8,10 +8,10 @@ WebPreferences of in-process child windows, rather than relying on
|
||||
process-level command line switches, as before.
|
||||
|
||||
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
index 9ab1b47509c8b72b7844e83f1d69499d13e26837..8fe07713a01123cc21d2649f8a3e9347a49a2bb8 100644
|
||||
index c6552b25ffba3bf8d806d8bf2410a89533c9bef1..9920c3146c6cf700414a679e80087c469395eee9 100644
|
||||
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
|
||||
@@ -149,6 +149,19 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
@@ -150,6 +150,19 @@ bool StructTraits<blink::mojom::WebPreferencesDataView,
|
||||
out->v8_cache_options = data.v8_cache_options();
|
||||
out->record_whole_document = data.record_whole_document();
|
||||
out->stylus_handwriting_enabled = data.stylus_handwriting_enabled();
|
||||
@@ -32,7 +32,7 @@ index 9ab1b47509c8b72b7844e83f1d69499d13e26837..8fe07713a01123cc21d2649f8a3e9347
|
||||
out->accelerated_video_decode_enabled =
|
||||
data.accelerated_video_decode_enabled();
|
||||
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
index efcb7d9457045c2d58ecec4b68d7c4547cb5d08a..e37fa2e8cb0896e61ef11259df13d97b8fbff548 100644
|
||||
index 1188e60da33c292febf45be4cd6055671c21b4aa..43a1e777536ce2079d81deb5c7f440a1ba9b43d9 100644
|
||||
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
|
||||
@@ -10,6 +10,7 @@
|
||||
@@ -64,7 +64,7 @@ index efcb7d9457045c2d58ecec4b68d7c4547cb5d08a..e37fa2e8cb0896e61ef11259df13d97b
|
||||
// chrome, except for the cases where it would require lots of extra work for
|
||||
// the embedder to use the same default value.
|
||||
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
index fade1dd1310d8339fff45b9ae74ebff4673eec37..ea3f8f3e30f76ebf71ed470f43e4f61995829932 100644
|
||||
index ac91e8bad952dad5fc6ff673ffd19b0edd30bdb2..0f1715711056c83bb53e03dd8b675cb40a0c42cc 100644
|
||||
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
|
||||
@@ -8,6 +8,7 @@
|
||||
@@ -129,7 +129,7 @@ index fade1dd1310d8339fff45b9ae74ebff4673eec37..ea3f8f3e30f76ebf71ed470f43e4f619
|
||||
return r.cookie_enabled;
|
||||
}
|
||||
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
index c637783517d250b7aa6f34af11fd3ca804a2a705..0268d33da3150a37ca8206695a5f324d8fde22e6 100644
|
||||
index 0ed21e64a0b43580feb99166953babfb633d5af6..f06db564760e8f7e785bb3d6d4b80270a8783a23 100644
|
||||
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
@@ -15,7 +15,7 @@ Refs changes in:
|
||||
This patch reverts the changes to fix associated crashes in Electron.
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/frame/frame.cc b/third_party/blink/renderer/core/frame/frame.cc
|
||||
index 9827a89c56141596fde57b78f9c9894f273db83e..cedb4bd8217a0ad3ab07d85421e1850bc4d910f5 100644
|
||||
index 901b727ed898cdd840df5ff7e2380fbee5d7fde2..1caacaeed9ddf1162cfa393fe4a7c86ac27f674a 100644
|
||||
--- a/third_party/blink/renderer/core/frame/frame.cc
|
||||
+++ b/third_party/blink/renderer/core/frame/frame.cc
|
||||
@@ -135,14 +135,6 @@ bool Frame::Detach(FrameDetachType type) {
|
||||
@@ -49,7 +49,7 @@ index 9827a89c56141596fde57b78f9c9894f273db83e..cedb4bd8217a0ad3ab07d85421e1850b
|
||||
// its owning reference back to our owning LocalFrame.
|
||||
client_->Detached(type);
|
||||
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
|
||||
index 4c38cd881b5a81b7939f61688f05949be799f008..8970537416e171d513bc9c015706fb18a574eab6 100644
|
||||
index 802c876dd85d8100fc3d6e634ad4e390fd48747f..abe5b3c6e5eadf30f3e00013fceddaa0ead36cb1 100644
|
||||
--- a/third_party/blink/renderer/core/frame/local_frame.cc
|
||||
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
|
||||
@@ -758,10 +758,6 @@ bool LocalFrame::DetachImpl(FrameDetachType type) {
|
||||
|
||||
@@ -8,7 +8,7 @@ categories in use are known / declared. This patch is required for us
|
||||
to introduce a new Electron category for Electron-specific tracing.
|
||||
|
||||
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
|
||||
index 440349df6c5767fe3f93b51f78b33bf9d3bb5c1a..85c6f973788938b6a48a7a89e9fa803dc1030580 100644
|
||||
index 39168e90fd6ea68e562f0a2c6d8ba1162bc29e71..1b2b58497541b06857bc8f0d79172e8106003845 100644
|
||||
--- a/base/trace_event/builtin_categories.h
|
||||
+++ b/base/trace_event/builtin_categories.h
|
||||
@@ -133,6 +133,7 @@ PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS(
|
||||
|
||||
@@ -11,10 +11,10 @@ if we ever align our .pak file generation with Chrome we can remove this
|
||||
patch.
|
||||
|
||||
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
|
||||
index 0af4d4b75d0519fabcb5d48bd9d5bd465bc80e92..eb6b23655afaa268f25d99301a0853aaecd23652 100644
|
||||
index 9a5b03af14d68c0c64380f84901aaeef11757ccb..933c03f197a8510c168775d5f8d19ebf375389d2 100644
|
||||
--- a/chrome/BUILD.gn
|
||||
+++ b/chrome/BUILD.gn
|
||||
@@ -201,6 +201,12 @@ if (!is_android && !is_mac) {
|
||||
@@ -201,11 +201,16 @@ if (!is_android && !is_mac) {
|
||||
"common/crash_keys.h",
|
||||
]
|
||||
|
||||
@@ -27,11 +27,29 @@ index 0af4d4b75d0519fabcb5d48bd9d5bd465bc80e92..eb6b23655afaa268f25d99301a0853aa
|
||||
deps += [
|
||||
":chrome_dll",
|
||||
":chrome_exe_version",
|
||||
":copy_first_run",
|
||||
- ":packed_resources_integrity_header",
|
||||
":visual_elements_resources",
|
||||
"//base",
|
||||
"//build:branding_buildflags",
|
||||
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
|
||||
index 5768066ed65810d14d8ad4b6839c6c632af6bb57..d8d4e66f1c96f630e60001425d16fc4d6e22212b 100644
|
||||
--- a/chrome/browser/BUILD.gn
|
||||
+++ b/chrome/browser/BUILD.gn
|
||||
@@ -4455,7 +4455,7 @@ static_library("browser") {
|
||||
]
|
||||
}
|
||||
|
||||
- if (!is_win) {
|
||||
+ if (!is_win && !is_electron_build) {
|
||||
# On Windows, the hashes are embedded in //chrome:chrome_initial rather
|
||||
# than here in :chrome_dll.
|
||||
deps += [ "//chrome:packed_resources_integrity_header" ]
|
||||
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
|
||||
index e91f97276866bd500720962c74acaca2c22fff7c..22867153821d2b1e83feb1a2a7a6b8c26ba776eb 100644
|
||||
index 98ce26437751543c5c93574bc9561409e214d8a8..853eda4503954de04d50caba63f55fd74f390069 100644
|
||||
--- a/chrome/test/BUILD.gn
|
||||
+++ b/chrome/test/BUILD.gn
|
||||
@@ -7737,6 +7737,10 @@ test("unit_tests") {
|
||||
@@ -7718,9 +7718,12 @@ test("unit_tests") {
|
||||
"//chrome/notification_helper",
|
||||
]
|
||||
|
||||
@@ -41,8 +59,11 @@ index e91f97276866bd500720962c74acaca2c22fff7c..22867153821d2b1e83feb1a2a7a6b8c2
|
||||
+
|
||||
deps += [
|
||||
"//chrome:other_version",
|
||||
- "//chrome:packed_resources_integrity_header",
|
||||
"//chrome//services/util_win:unit_tests",
|
||||
@@ -8711,6 +8715,10 @@ test("unit_tests") {
|
||||
"//chrome/app:chrome_dll_resources",
|
||||
"//chrome/app:win_unit_tests",
|
||||
@@ -8723,6 +8726,10 @@ test("unit_tests") {
|
||||
"../browser/performance_manager/policies/background_tab_loading_policy_unittest.cc",
|
||||
]
|
||||
|
||||
@@ -53,12 +74,11 @@ index e91f97276866bd500720962c74acaca2c22fff7c..22867153821d2b1e83feb1a2a7a6b8c2
|
||||
sources += [
|
||||
# The importer code is not used on Android.
|
||||
"../common/importer/firefox_importer_utils_unittest.cc",
|
||||
@@ -8767,7 +8775,7 @@ test("unit_tests") {
|
||||
@@ -8779,7 +8786,6 @@ test("unit_tests") {
|
||||
# TODO(crbug.com/417513088): Maybe merge with the non-android `deps` declaration above?
|
||||
deps += [
|
||||
"../browser/screen_ai:screen_ai_install_state",
|
||||
- "//chrome:packed_resources",
|
||||
+
|
||||
- "//chrome:packed_resources_integrity_header",
|
||||
"//chrome/browser/apps:icon_standardizer",
|
||||
"//chrome/browser/apps/app_service",
|
||||
"//chrome/browser/apps/app_service:app_registry_cache_waiter",
|
||||
|
||||
@@ -9,10 +9,10 @@ potentially prevent a window from being created.
|
||||
TODO(loc): this patch is currently broken.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
index ac474e220d411dec278c40448f038b25e6788d2a..e4ff8f11bed9e53f3134068492ac94b4c9bb4df2 100644
|
||||
index 914b06175825f79c03d34e0bdb1c3749a934bfdb..89f4af078c151adc1d9d471056bacab5dace0833 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -10228,6 +10228,7 @@ void RenderFrameHostImpl::CreateNewWindow(
|
||||
@@ -10170,6 +10170,7 @@ void RenderFrameHostImpl::CreateNewWindow(
|
||||
last_committed_origin_, params->window_container_type,
|
||||
params->target_url, params->referrer.To<Referrer>(),
|
||||
params->frame_name, params->disposition, *params->features,
|
||||
@@ -21,10 +21,10 @@ index ac474e220d411dec278c40448f038b25e6788d2a..e4ff8f11bed9e53f3134068492ac94b4
|
||||
&no_javascript_access);
|
||||
|
||||
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
|
||||
index 3e0c8bd308d8a947a2bd295a2d83e385e53853fb..4e91b3aeb5630476c660e8814e2fd9d92c5a9ca1 100644
|
||||
index 299324868f7bf88c7105015c1925881b06ec40a6..d40eb3c0670a9b3053db7773cef229adae8ecbec 100644
|
||||
--- a/content/browser/web_contents/web_contents_impl.cc
|
||||
+++ b/content/browser/web_contents/web_contents_impl.cc
|
||||
@@ -5501,6 +5501,10 @@ FrameTree* WebContentsImpl::CreateNewWindow(
|
||||
@@ -5497,6 +5497,10 @@ FrameTree* WebContentsImpl::CreateNewWindow(
|
||||
create_params.initially_hidden = renderer_started_hidden;
|
||||
create_params.initial_popup_url = params.target_url;
|
||||
|
||||
@@ -35,7 +35,7 @@ index 3e0c8bd308d8a947a2bd295a2d83e385e53853fb..4e91b3aeb5630476c660e8814e2fd9d9
|
||||
// Even though all codepaths leading here are in response to a renderer
|
||||
// trying to open a new window, if the new window ends up in a different
|
||||
// browsing instance, then the RenderViewHost, RenderWidgetHost,
|
||||
@@ -5555,6 +5559,12 @@ FrameTree* WebContentsImpl::CreateNewWindow(
|
||||
@@ -5551,6 +5555,12 @@ FrameTree* WebContentsImpl::CreateNewWindow(
|
||||
// Sets the newly created WebContents WindowOpenDisposition.
|
||||
new_contents_impl->original_window_open_disposition_ = params.disposition;
|
||||
|
||||
@@ -48,7 +48,7 @@ index 3e0c8bd308d8a947a2bd295a2d83e385e53853fb..4e91b3aeb5630476c660e8814e2fd9d9
|
||||
// If the new frame has a name, make sure any SiteInstances that can find
|
||||
// this named frame have proxies for it. Must be called after
|
||||
// SetSessionStorageNamespace, since this calls CreateRenderView, which uses
|
||||
@@ -5596,12 +5606,6 @@ FrameTree* WebContentsImpl::CreateNewWindow(
|
||||
@@ -5592,12 +5602,6 @@ FrameTree* WebContentsImpl::CreateNewWindow(
|
||||
AddWebContentsDestructionObserver(new_contents_impl);
|
||||
}
|
||||
|
||||
@@ -77,10 +77,10 @@ index 444fa7009d0db33470cac9ab9cfdc23ceacec942..ab9aeb852e5ea89583284386d9a78a3e
|
||||
|
||||
// Operation result when the renderer asks the browser to create a new window.
|
||||
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
|
||||
index e806de04ca92cb8351e9a242a5241c0d4286da97..d0b3e4bc348921df7e6446dbc1f14860b8a84d87 100644
|
||||
index 836d27b82b0798be4a17484903284810d86e4ff9..b06cd802b7e9bedf038a0b84fd1f242c1664a6ed 100644
|
||||
--- a/content/public/browser/content_browser_client.cc
|
||||
+++ b/content/public/browser/content_browser_client.cc
|
||||
@@ -854,6 +854,8 @@ bool ContentBrowserClient::CanCreateWindow(
|
||||
@@ -874,6 +874,8 @@ bool ContentBrowserClient::CanCreateWindow(
|
||||
const std::string& frame_name,
|
||||
WindowOpenDisposition disposition,
|
||||
const blink::mojom::WindowFeatures& features,
|
||||
@@ -90,7 +90,7 @@ index e806de04ca92cb8351e9a242a5241c0d4286da97..d0b3e4bc348921df7e6446dbc1f14860
|
||||
bool opener_suppressed,
|
||||
bool* no_javascript_access) {
|
||||
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
|
||||
index 70588ccd619ac7969918771bccf5c054320e4f6f..eb684232648424fab4ba73b1fc813b0b3f8b809b 100644
|
||||
index 7032139f91aadab0e854182d95eb97422a4182b3..f6ceaf652707d355780d8009339a42bbc271bd07 100644
|
||||
--- a/content/public/browser/content_browser_client.h
|
||||
+++ b/content/public/browser/content_browser_client.h
|
||||
@@ -205,6 +205,7 @@ class NetworkService;
|
||||
@@ -101,7 +101,7 @@ index 70588ccd619ac7969918771bccf5c054320e4f6f..eb684232648424fab4ba73b1fc813b0b
|
||||
} // namespace network
|
||||
|
||||
namespace sandbox {
|
||||
@@ -1406,6 +1407,8 @@ class CONTENT_EXPORT ContentBrowserClient {
|
||||
@@ -1457,6 +1458,8 @@ class CONTENT_EXPORT ContentBrowserClient {
|
||||
const std::string& frame_name,
|
||||
WindowOpenDisposition disposition,
|
||||
const blink::mojom::WindowFeatures& features,
|
||||
@@ -170,10 +170,10 @@ index 0650197909d484b8a0f48ab61b22471c71bce0e8..29c380d7845aab1a7b3417e0d3940ea0
|
||||
// typically happens when popups are created.
|
||||
virtual void WebContentsCreated(WebContents* source_contents,
|
||||
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
|
||||
index 5936c5eaa081abde7f7c26cc990a122622e46908..ab959e66f8841d7367863bb13d6c7a0854d0df23 100644
|
||||
index 017007ee611e3cbb718085096b38c60677c0863c..0e64b0a0ac8387ab15b201a9fc0f0fd36cd5ab29 100644
|
||||
--- a/content/renderer/render_frame_impl.cc
|
||||
+++ b/content/renderer/render_frame_impl.cc
|
||||
@@ -6845,6 +6845,10 @@ WebView* RenderFrameImpl::CreateNewWindow(
|
||||
@@ -6842,6 +6842,10 @@ WebView* RenderFrameImpl::CreateNewWindow(
|
||||
params->started_by_ad =
|
||||
GetWebFrame()->IsAdFrame() || GetWebFrame()->IsAdScriptInStack();
|
||||
|
||||
@@ -185,10 +185,10 @@ index 5936c5eaa081abde7f7c26cc990a122622e46908..ab959e66f8841d7367863bb13d6c7a08
|
||||
// moved on send.
|
||||
bool is_background_tab =
|
||||
diff --git a/content/web_test/browser/web_test_content_browser_client.cc b/content/web_test/browser/web_test_content_browser_client.cc
|
||||
index 9453418ab164904cb9d75930d649abe21b94bb03..b2acd05882e8dfb04e5a75b249705c1a15209056 100644
|
||||
index 7a57cb3a1414a77704c42ae01a9dc89fae4ad4a3..769601c2749f0781317f668cf806042db626c348 100644
|
||||
--- a/content/web_test/browser/web_test_content_browser_client.cc
|
||||
+++ b/content/web_test/browser/web_test_content_browser_client.cc
|
||||
@@ -540,6 +540,8 @@ bool WebTestContentBrowserClient::CanCreateWindow(
|
||||
@@ -539,6 +539,8 @@ bool WebTestContentBrowserClient::CanCreateWindow(
|
||||
const std::string& frame_name,
|
||||
WindowOpenDisposition disposition,
|
||||
const blink::mojom::WindowFeatures& features,
|
||||
@@ -224,10 +224,10 @@ index d92bab531c12c62a5321a23f4a0cb89691668127..2060e04795ba8e7a923fd0fe3485b8c5
|
||||
|
||||
} // namespace blink
|
||||
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
|
||||
index 51f03729c2d40a225dbcfc42091d44f78f77d971..780ee21199701b01a97932cd4a59aeb5db98017b 100644
|
||||
index 87856b74d5e0a323b8527d783316d1aab1cf9b1e..9f0d95954ed3d7c7e3ac4825f31ee55255e0c46f 100644
|
||||
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
|
||||
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
|
||||
@@ -2342,6 +2342,8 @@ DOMWindow* LocalDOMWindow::open(v8::Isolate* isolate,
|
||||
@@ -2366,6 +2366,8 @@ DOMWindow* LocalDOMWindow::open(v8::Isolate* isolate,
|
||||
WebWindowFeatures window_features =
|
||||
GetWindowFeaturesFromString(features, entered_window);
|
||||
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Vasilii Sukhanov <vasilii@chromium.org>
|
||||
Date: Wed, 8 Apr 2026 07:48:21 -0700
|
||||
Subject: Fix cross-domain password leak via manual-fallback preview
|
||||
|
||||
In PasswordManualFallbackFlow::DidSelectSuggestion, when a user selects
|
||||
a password suggestion, the browser process sends the cleartext password
|
||||
to the renderer for previewing. If the suggestion is cross-domain, this
|
||||
leak happens without consent or auth.
|
||||
|
||||
This CL fixes this by omitting the password in the preview message for
|
||||
all the cases by sending the fake string.
|
||||
|
||||
Fixed: 498269651
|
||||
Change-Id: Ic9546114c453f05de1030f05c7a9830b39d73038
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7735152
|
||||
Commit-Queue: Vasilii Sukhanov <vasilii@chromium.org>
|
||||
Reviewed-by: Anna Tsvirchkova <atsvirchkova@google.com>
|
||||
Cr-Commit-Position: refs/heads/main@{#1611490}
|
||||
|
||||
diff --git a/components/password_manager/core/browser/password_manual_fallback_flow.cc b/components/password_manager/core/browser/password_manual_fallback_flow.cc
|
||||
index 6fd5468b061949c7f4a45a29b07e1325bde629e3..47bd86d5fd70a849a13b6837a98f3009fbc10ea6 100644
|
||||
--- a/components/password_manager/core/browser/password_manual_fallback_flow.cc
|
||||
+++ b/components/password_manager/core/browser/password_manual_fallback_flow.cc
|
||||
@@ -213,12 +213,13 @@ void PasswordManualFallbackFlow::DidSelectSuggestion(
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
+ const auto payload =
|
||||
+ suggestion.GetPayload<Suggestion::PasswordSuggestionDetails>();
|
||||
password_manager_driver_->PreviewSuggestionById(
|
||||
form->username_element_renderer_id,
|
||||
form->password_element_renderer_id,
|
||||
GetUsernameFromLabel(suggestion.labels[0][0].value),
|
||||
- suggestion.GetPayload<Suggestion::PasswordSuggestionDetails>()
|
||||
- .password);
|
||||
+ std::u16string(payload.password.length(), '*'));
|
||||
break;
|
||||
}
|
||||
case autofill::SuggestionType::kPasswordFieldByFieldFilling:
|
||||
diff --git a/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc b/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc
|
||||
index 8b51bbcab5ec65562eed443ea9ba80dbaf8cba63..b99c6531aeb2d4c46e0a88cc0478e18c117c2bb6 100644
|
||||
--- a/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc
|
||||
+++ b/components/password_manager/core/browser/password_manual_fallback_flow_unittest.cc
|
||||
@@ -656,7 +656,7 @@ TEST_F(PasswordManualFallbackFlowTest,
|
||||
EXPECT_CALL(driver(), PreviewSuggestionById(form.username_element_renderer_id,
|
||||
form.password_element_renderer_id,
|
||||
std::u16string(u"username"),
|
||||
- std::u16string(u"password")));
|
||||
+ std::u16string(u"********")));
|
||||
Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
|
||||
SuggestionType::kPasswordEntry, u"google.com",
|
||||
CreateTestPasswordDetails());
|
||||
@@ -667,6 +667,40 @@ TEST_F(PasswordManualFallbackFlowTest,
|
||||
flow().DidSelectSuggestion(suggestion);
|
||||
}
|
||||
|
||||
+// Test that password manual fallback suggestion is previewed without password
|
||||
+// if the suggestion is cross-domain.
|
||||
+TEST_F(PasswordManualFallbackFlowTest,
|
||||
+ SelectFillFullFormSuggestion_CrossDomain_TriggeredOnAPasswordForm) {
|
||||
+ InitializeFlow();
|
||||
+ ProcessPasswordStoreUpdates();
|
||||
+
|
||||
+ PasswordForm form;
|
||||
+ form.username_element_renderer_id = MakeFieldRendererId();
|
||||
+ form.password_element_renderer_id = MakeFieldRendererId();
|
||||
+ // Simulate that the field is/isn't classified as target filling password.
|
||||
+ EXPECT_CALL(password_form_cache(),
|
||||
+ GetPasswordForm(_, form.username_element_renderer_id))
|
||||
+ .WillRepeatedly(Return(&form));
|
||||
+
|
||||
+ flow().RunFlow(form.username_element_renderer_id, gfx::RectF{},
|
||||
+ TextDirection::LEFT_TO_RIGHT);
|
||||
+
|
||||
+ // Expect that the password is empty in the preview call.
|
||||
+ EXPECT_CALL(driver(), PreviewSuggestionById(form.username_element_renderer_id,
|
||||
+ form.password_element_renderer_id,
|
||||
+ std::u16string(u"username"),
|
||||
+ std::u16string(u"********")));
|
||||
+ Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
|
||||
+ SuggestionType::kPasswordEntry, u"google.com",
|
||||
+ Suggestion::PasswordSuggestionDetails(u"username", u"password",
|
||||
+ "https://cross-domain.com/",
|
||||
+ u"cross-domain.com",
|
||||
+ /*is_cross_domain=*/true));
|
||||
+ suggestion.labels = {{Suggestion::Text(u"username")}};
|
||||
+ suggestion.acceptability = Suggestion::Acceptability::kAcceptable;
|
||||
+ flow().DidSelectSuggestion(suggestion);
|
||||
+}
|
||||
+
|
||||
// Test that only password field is previewed if the credential doesn't have
|
||||
// a username saved for it.
|
||||
TEST_F(PasswordManualFallbackFlowTest,
|
||||
@@ -687,7 +721,7 @@ TEST_F(PasswordManualFallbackFlowTest,
|
||||
EXPECT_CALL(driver(), PreviewSuggestionById(FieldRendererId(),
|
||||
form.password_element_renderer_id,
|
||||
std::u16string(),
|
||||
- std::u16string(u"password")));
|
||||
+ std::u16string(u"********")));
|
||||
Suggestion suggestion = autofill::test::CreateAutofillSuggestion(
|
||||
SuggestionType::kPasswordEntry, u"google.com",
|
||||
CreateTestPasswordDetails());
|
||||
@@ -1,216 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Joey Arhar <jarhar@chromium.org>
|
||||
Date: Fri, 10 Apr 2026 12:19:11 -0700
|
||||
Subject: Fix crashes when restoring <selectedcontent> with <input>
|
||||
|
||||
When restoring form control state, the DOM could be modified to add or
|
||||
remove more listed elements to the form if a select element is being
|
||||
restored which has a selectedcontent element.
|
||||
|
||||
Fixed: 499384399
|
||||
Change-Id: I18f69c30ae25396c53625f7a3172626b79de3ae3
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7732030
|
||||
Reviewed-by: Joey Arhar <jarhar@chromium.org>
|
||||
Commit-Queue: Joey Arhar <jarhar@chromium.org>
|
||||
Reviewed-by: Dominic Farolino <dom@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1613032}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/html/forms/form_controller.cc b/third_party/blink/renderer/core/html/forms/form_controller.cc
|
||||
index abacf373d30debc17498eaeb7cd940972aed2a3a..c4cb59678eeacc2d3fb9f69a30df8674154dbf37 100644
|
||||
--- a/third_party/blink/renderer/core/html/forms/form_controller.cc
|
||||
+++ b/third_party/blink/renderer/core/html/forms/form_controller.cc
|
||||
@@ -492,8 +492,10 @@ void FormController::RestoreControlStateIn(HTMLFormElement& form) {
|
||||
if (!document_->HasFinishedParsing())
|
||||
return;
|
||||
EventQueueScope scope;
|
||||
- const ListedElement::List& elements = form.ListedElements();
|
||||
- for (const auto& control : elements) {
|
||||
+ // Make a copy of the list because the DOM could be modified during
|
||||
+ // restoration of a <select> with a <selectedcontent> element.
|
||||
+ ListedElement::List elements_copy(form.ListedElements());
|
||||
+ for (const auto& control : elements_copy) {
|
||||
if (!control->ClassSupportsStateRestore())
|
||||
continue;
|
||||
if (OwnerFormForState(*control) != &form)
|
||||
@@ -550,7 +552,11 @@ void FormController::RestoreAllControlsInDocumentOrder() {
|
||||
return;
|
||||
HeapHashSet<Member<HTMLFormElement>> finished_forms;
|
||||
EventQueueScope scope;
|
||||
- for (auto& control : document_state_->GetControlList()) {
|
||||
+ // Make a copy of the list because the DOM could be modified during
|
||||
+ // restoration of a <select> with a <selectedcontent> element.
|
||||
+ DocumentState::ControlList control_list_copy(
|
||||
+ document_state_->GetControlList());
|
||||
+ for (auto& control : control_list_copy) {
|
||||
auto* owner = OwnerFormForState(*control);
|
||||
if (!owner)
|
||||
RestoreControlStateFor(*control);
|
||||
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
|
||||
index ef47961c57cd15f8c59a4554165568c7f13c0d0a..54331107dd22c6b534327ffd5c282ca3220345de 100644
|
||||
--- a/third_party/blink/web_tests/VirtualTestSuites
|
||||
+++ b/third_party/blink/web_tests/VirtualTestSuites
|
||||
@@ -3067,7 +3067,7 @@
|
||||
],
|
||||
"bases": [
|
||||
"external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-in-option-crash.html",
|
||||
- "external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.html",
|
||||
+ "external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.optional.html",
|
||||
"external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-color.html",
|
||||
"external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-nested.html",
|
||||
"external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontentelement-attr.tentative.html"
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/resources/selectedcontent-input.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/resources/selectedcontent-input.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..847f42ac304835c2049cf434a4dec68814ad533c
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/resources/selectedcontent-input.html
|
||||
@@ -0,0 +1,27 @@
|
||||
+<!DOCTYPE html>
|
||||
+<style>
|
||||
+select,::picker(select) {
|
||||
+ appearance: base-select;
|
||||
+}
|
||||
+</style>
|
||||
+<form action="blank.html">
|
||||
+ <select>
|
||||
+ <button>
|
||||
+ <selectedcontent></selectedcontent>
|
||||
+ </button>
|
||||
+ <option id=one>one</option>
|
||||
+ <option id=two>two</option>
|
||||
+ </select>
|
||||
+</form>
|
||||
+
|
||||
+<script>
|
||||
+window.createInput = () => {
|
||||
+ const selectedcontent = document.querySelector('selectedcontent');
|
||||
+ const input = document.createElement('input');
|
||||
+ window.input = input;
|
||||
+ input.name = 'input';
|
||||
+ selectedcontent.innerHTML = '';
|
||||
+ selectedcontent.appendChild(input);
|
||||
+};
|
||||
+window.createInput();
|
||||
+</script>
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.html
|
||||
deleted file mode 100644
|
||||
index da5fe450abbae0d19826021f114cc6388f97bc57..0000000000000000000000000000000000000000
|
||||
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.html
|
||||
+++ /dev/null
|
||||
@@ -1,35 +0,0 @@
|
||||
-<!DOCTYPE html>
|
||||
-<link rel=author href="mailto:jarhar@chromium.org">
|
||||
-<link rel=help href="https://github.com/whatwg/html/issues/9799">
|
||||
-<script src="/resources/testharness.js"></script>
|
||||
-<script src="/resources/testharnessreport.js"></script>
|
||||
-<script src="/resources/testdriver.js"></script>
|
||||
-<script src="/resources/testdriver-vendor.js"></script>
|
||||
-
|
||||
-<iframe src="resources/selectedcontent-restore-iframe.html"></iframe>
|
||||
-
|
||||
-<script>
|
||||
-const iframe = document.querySelector('iframe');
|
||||
-promise_test(async () => {
|
||||
- await new Promise(resolve => iframe.onload = resolve);
|
||||
- await test_driver.bless();
|
||||
-
|
||||
- iframe.contentDocument.querySelector('select').value = 'two';
|
||||
- assert_equals(iframe.contentDocument.querySelector('select').value, 'two',
|
||||
- 'Assining two to select.value should work.');
|
||||
- iframe.contentDocument.querySelector('form').submit();
|
||||
- await new Promise(resolve => iframe.onload = resolve);
|
||||
-
|
||||
- await test_driver.bless();
|
||||
- iframe.contentWindow.history.back();
|
||||
- await new Promise(resolve => iframe.onload = resolve);
|
||||
- await new Promise(requestAnimationFrame);
|
||||
- await new Promise(requestAnimationFrame);
|
||||
-
|
||||
- assert_equals(iframe.contentDocument.querySelector('select').value, 'two',
|
||||
- 'The selects value should be restored after navigating back.');
|
||||
- assert_equals(iframe.contentDocument.querySelector('selectedcontent').innerHTML,
|
||||
- iframe.contentDocument.querySelector('option[value=two]').innerHTML,
|
||||
- 'selectedcontent.innerHTML should match the selected <option>');
|
||||
-}, '<selectedcontent> should be up to date after form restoration.');
|
||||
-</script>
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.optional.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.optional.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..1d0064659cd9df06d6267261bf0b39b3fb29aeef
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/selectedcontent-restore.optional.html
|
||||
@@ -0,0 +1,76 @@
|
||||
+<!DOCTYPE html>
|
||||
+<link rel=author href="mailto:jarhar@chromium.org">
|
||||
+<link rel=help href="https://github.com/whatwg/html/issues/9799">
|
||||
+<script src="/resources/testharness.js"></script>
|
||||
+<script src="/resources/testharnessreport.js"></script>
|
||||
+<script src="/resources/testdriver.js"></script>
|
||||
+<script src="/resources/testdriver-vendor.js"></script>
|
||||
+
|
||||
+<!-- This test is marked optional because form control restoration is not explicitly specified. -->
|
||||
+
|
||||
+<iframe id=iframe1 src="resources/selectedcontent-restore-iframe.html"></iframe>
|
||||
+<iframe id=iframe2 src="resources/selectedcontent-input.html"></iframe>
|
||||
+
|
||||
+<script>
|
||||
+const iframe1 = document.getElementById('iframe1');
|
||||
+const iframe2 = document.getElementById('iframe2');
|
||||
+const iframe1load = new Promise(resolve => iframe1.onload = resolve);
|
||||
+const iframe2load = new Promise(resolve => iframe2.onload = resolve);
|
||||
+
|
||||
+promise_test(async () => {
|
||||
+ await iframe1load;
|
||||
+ await test_driver.bless();
|
||||
+
|
||||
+ iframe1.contentDocument.querySelector('select').value = 'two';
|
||||
+ assert_equals(iframe1.contentDocument.querySelector('select').value, 'two',
|
||||
+ 'Assigning two to select.value should work.');
|
||||
+ iframe1.contentDocument.querySelector('form').submit();
|
||||
+ await new Promise(resolve => iframe1.onload = resolve);
|
||||
+
|
||||
+ await test_driver.bless();
|
||||
+ iframe1.contentWindow.history.back();
|
||||
+ // Form controls are restored immediately after the load event is fired, so
|
||||
+ // one rAF is added after awaiting the load event. See
|
||||
+ // LocalDOMWindow::DispatchLoadAndPageshowEvents.
|
||||
+ await new Promise(resolve => iframe1.onload = resolve);
|
||||
+ await new Promise(requestAnimationFrame);
|
||||
+
|
||||
+ assert_equals(iframe1.contentDocument.querySelector('select').value, 'two',
|
||||
+ 'The selects value should be restored after navigating back.');
|
||||
+ assert_equals(iframe1.contentDocument.querySelector('selectedcontent').innerHTML,
|
||||
+ iframe1.contentDocument.querySelector('option[value=two]').innerHTML,
|
||||
+ 'selectedcontent.innerHTML should match the selected <option>');
|
||||
+}, '<selectedcontent> should be up to date after form restoration.');
|
||||
+
|
||||
+promise_test(async () => {
|
||||
+ await iframe2load;
|
||||
+ await test_driver.bless();
|
||||
+
|
||||
+ iframe2.contentDocument.querySelector('select').value = 'two';
|
||||
+ iframe2.contentWindow.createInput();
|
||||
+ iframe2.contentDocument.querySelector('input').value = 'value';
|
||||
+ iframe2.contentDocument.querySelector('form').submit();
|
||||
+ await new Promise(resolve => iframe2.onload = resolve);
|
||||
+
|
||||
+ await test_driver.bless();
|
||||
+ iframe2.contentWindow.history.back();
|
||||
+ // Form controls are restored immediately after the load event is fired, so
|
||||
+ // one rAF is added after awaiting the load event. See
|
||||
+ // LocalDOMWindow::DispatchLoadAndPageshowEvents.
|
||||
+ await new Promise(resolve => iframe2.onload = resolve);
|
||||
+ await new Promise(requestAnimationFrame);
|
||||
+
|
||||
+ // A crash would happen here because the form restoration code would iterate
|
||||
+ // over all of the form controls and remove an input element to restore during
|
||||
+ // restoration of the selectedcontent element, then try to restore the
|
||||
+ // disconnected input.
|
||||
+
|
||||
+ assert_equals(iframe2.contentDocument.querySelector('select').value, 'two',
|
||||
+ 'The selects value should be restored after navigating back.');
|
||||
+ assert_equals(iframe2.contentDocument.querySelector('selectedcontent').innerHTML,
|
||||
+ iframe2.contentDocument.getElementById('two').innerHTML,
|
||||
+ 'selectedcontent.innerHTML should match the selected <option>');
|
||||
+ assert_equals(iframe2.contentWindow.input.value, '',
|
||||
+ 'The text inputs value should not be restored because it was removed before restoring.');
|
||||
+}, '<input> inside <selectedcontent> should be restored after form submission.');
|
||||
+</script>
|
||||
@@ -1,53 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: p0-tato <smartphonewithbear@gmail.com>
|
||||
Date: Mon, 13 Apr 2026 14:50:07 -0700
|
||||
Subject: Fix dangling pointers in OpenXrSpatialFrameworkManager
|
||||
|
||||
Pointers to vector elements were collected during emplace_back,
|
||||
which invalidates them on reallocation. Split into two loops
|
||||
and reserve the correct capacity.
|
||||
|
||||
Bug: 497724498
|
||||
Change-Id: I204534bc1bd1522fe03db86f03c2c3e0d285631c
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7735242
|
||||
Commit-Queue: Brian Sheedy <bsheedy@chromium.org>
|
||||
Reviewed-by: Brian Sheedy <bsheedy@chromium.org>
|
||||
Reviewed-by: Brandon Jones <bajones@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1613990}
|
||||
|
||||
diff --git a/AUTHORS b/AUTHORS
|
||||
index 700ad1f495549eac39242e5d3b489245eb1b633d..5ef10597084e19b283209ee9833c56a7a0346187 100644
|
||||
--- a/AUTHORS
|
||||
+++ b/AUTHORS
|
||||
@@ -736,6 +736,7 @@ Jihoon Chung <jihoon@gmail.com>
|
||||
Jihun Brent Kim <devgrapher@gmail.com>
|
||||
Jihwan Marc Kim <bluewhale.marc@gmail.com>
|
||||
Jihye Hyun <jijinny26@gmail.com>
|
||||
+Jihyeon Jeong <smartphonewithbear@gmail.com>
|
||||
Jihyeon Lee <wlgus7464@gmail.com>
|
||||
Jim Wu <lofoz.tw@gmail.com>
|
||||
Jin Yang <jin.a.yang@intel.com>
|
||||
diff --git a/device/vr/openxr/openxr_spatial_framework_manager.cc b/device/vr/openxr/openxr_spatial_framework_manager.cc
|
||||
index 2fd3609f277dc425d38a9acf9895b1ad02d64c72..b6f82c5c2999073aad9c43811fe9561c670992f9 100644
|
||||
--- a/device/vr/openxr/openxr_spatial_framework_manager.cc
|
||||
+++ b/device/vr/openxr/openxr_spatial_framework_manager.cc
|
||||
@@ -74,12 +74,15 @@ OpenXrSpatialFrameworkManager::OpenXrSpatialFrameworkManager(
|
||||
// to help abstract some of the details of creating the child structs, even
|
||||
// though at present we only have a configuration base.
|
||||
std::vector<OpenXrSpatialCapabilityConfigurationBase> capability_configs;
|
||||
- std::vector<XrSpatialCapabilityConfigurationBaseHeaderEXT*>
|
||||
- capability_config_ptrs;
|
||||
+ capability_configs.reserve(capability_configuration.size());
|
||||
for (auto& [capability, components] : capability_configuration) {
|
||||
capability_configs.emplace_back(capability, components);
|
||||
- capability_config_ptrs.push_back(
|
||||
- capability_configs.back().GetAsBaseHeader());
|
||||
+ }
|
||||
+
|
||||
+ std::vector<XrSpatialCapabilityConfigurationBaseHeaderEXT*>
|
||||
+ capability_config_ptrs;
|
||||
+ for (auto& config : capability_configs) {
|
||||
+ capability_config_ptrs.push_back(config.GetAsBaseHeader());
|
||||
}
|
||||
|
||||
XrSpatialContextCreateInfoEXT create_info = {
|
||||
@@ -1,224 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jonathan Ross <jonross@chromium.org>
|
||||
Date: Wed, 8 Apr 2026 17:15:45 -0700
|
||||
Subject: gl: Make DCOMPSurfaceRegistry thread-safe
|
||||
|
||||
DCOMPSurfaceRegistry is accessed from both the GPU IO thread (via
|
||||
GpuServiceImpl) and the GPU main scheduler thread (via DCOMPTexture).
|
||||
The underlying base::flat_map is not thread-safe, leading to potential
|
||||
container corruption and crashes (UAF, BOf) during concurrent access.
|
||||
|
||||
This CL adds a base::Lock to protect all accesses to the map and
|
||||
includes a new multi-threaded stress test to verify the fix.
|
||||
|
||||
Bug: 493315759
|
||||
Change-Id: Ibb7ef5e602f222410fde06a61fb3f5e571e7a70f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7737061
|
||||
Reviewed-by: Sunny Sachanandani <sunnyps@chromium.org>
|
||||
Commit-Queue: Jonathan Ross <jonross@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1611867}
|
||||
|
||||
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
|
||||
index 3584b693370b5199456608a26ceb763f6e9c3446..1cb66199a0b8adf2035a05fecc411c67180f7e80 100644
|
||||
--- a/ui/gl/BUILD.gn
|
||||
+++ b/ui/gl/BUILD.gn
|
||||
@@ -552,6 +552,7 @@ test("gl_unittests") {
|
||||
if (is_win) {
|
||||
sources += [
|
||||
"dcomp_presenter_unittest.cc",
|
||||
+ "dcomp_surface_registry_unittest.cc",
|
||||
"delegated_ink_point_renderer_gpu_unittest.cc",
|
||||
"gl_fence_win_unittest.cc",
|
||||
"hdr_metadata_helper_win_unittest.cc",
|
||||
diff --git a/ui/gl/dcomp_surface_registry.cc b/ui/gl/dcomp_surface_registry.cc
|
||||
index 352cc298b9ea97361ae2a7d668b7d7e9eb455cd5..410f76f8980438abae32b6c89e7083ae48cf1699 100644
|
||||
--- a/ui/gl/dcomp_surface_registry.cc
|
||||
+++ b/ui/gl/dcomp_surface_registry.cc
|
||||
@@ -3,8 +3,11 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ui/gl/dcomp_surface_registry.h"
|
||||
+
|
||||
+#include "base/check.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/no_destructor.h"
|
||||
+#include "base/synchronization/lock.h"
|
||||
|
||||
namespace gl {
|
||||
|
||||
@@ -20,8 +23,11 @@ base::UnguessableToken DCOMPSurfaceRegistry::RegisterDCOMPSurfaceHandle(
|
||||
base::win::ScopedHandle surface) {
|
||||
DVLOG(1) << __func__;
|
||||
base::UnguessableToken token = base::UnguessableToken::Create();
|
||||
- DCHECK(surface_handle_map_.find(token) == surface_handle_map_.end());
|
||||
- surface_handle_map_[token] = std::move(surface);
|
||||
+ {
|
||||
+ base::AutoLock lock(lock_);
|
||||
+ DCHECK(surface_handle_map_.find(token) == surface_handle_map_.end());
|
||||
+ surface_handle_map_[token] = std::move(surface);
|
||||
+ }
|
||||
DVLOG(1) << __func__ << ": Surface handle registered with token " << token;
|
||||
return token;
|
||||
}
|
||||
@@ -29,12 +35,14 @@ base::UnguessableToken DCOMPSurfaceRegistry::RegisterDCOMPSurfaceHandle(
|
||||
void DCOMPSurfaceRegistry::UnregisterDCOMPSurfaceHandle(
|
||||
const base::UnguessableToken& token) {
|
||||
DVLOG(1) << __func__;
|
||||
+ base::AutoLock lock(lock_);
|
||||
surface_handle_map_.erase(token);
|
||||
}
|
||||
|
||||
base::win::ScopedHandle DCOMPSurfaceRegistry::TakeDCOMPSurfaceHandle(
|
||||
const base::UnguessableToken& token) {
|
||||
DVLOG(1) << __func__;
|
||||
+ base::AutoLock lock(lock_);
|
||||
auto surface_iter = surface_handle_map_.find(token);
|
||||
if (surface_iter != surface_handle_map_.end()) {
|
||||
// Take ownership.
|
||||
diff --git a/ui/gl/dcomp_surface_registry.h b/ui/gl/dcomp_surface_registry.h
|
||||
index 803a3cc6398f0777504063118920998869086d7f..7cd9fdbfe8669bc97d4b664fdb29573ec2ea26de 100644
|
||||
--- a/ui/gl/dcomp_surface_registry.h
|
||||
+++ b/ui/gl/dcomp_surface_registry.h
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "base/no_destructor.h"
|
||||
+#include "base/synchronization/lock.h"
|
||||
#include "base/unguessable_token.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "ui/gl/gl_export.h"
|
||||
@@ -44,7 +45,9 @@ class GL_EXPORT DCOMPSurfaceRegistry {
|
||||
~DCOMPSurfaceRegistry();
|
||||
|
||||
base::flat_map<base::UnguessableToken, base::win::ScopedHandle>
|
||||
- surface_handle_map_;
|
||||
+ surface_handle_map_ GUARDED_BY(lock_);
|
||||
+
|
||||
+ base::Lock lock_;
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
diff --git a/ui/gl/dcomp_surface_registry_unittest.cc b/ui/gl/dcomp_surface_registry_unittest.cc
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..595e2388e9f50df33214359ecef0c135d94610b8
|
||||
--- /dev/null
|
||||
+++ b/ui/gl/dcomp_surface_registry_unittest.cc
|
||||
@@ -0,0 +1,118 @@
|
||||
+// Copyright 2026 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#include "ui/gl/dcomp_surface_registry.h"
|
||||
+
|
||||
+#include <windows.h>
|
||||
+
|
||||
+#include <atomic>
|
||||
+#include <thread>
|
||||
+#include <vector>
|
||||
+
|
||||
+#include "base/memory/raw_ptr.h"
|
||||
+#include "base/synchronization/lock.h"
|
||||
+#include "base/unguessable_token.h"
|
||||
+#include "base/win/scoped_handle.h"
|
||||
+#include "testing/gtest/include/gtest/gtest.h"
|
||||
+
|
||||
+namespace gl {
|
||||
+
|
||||
+namespace {
|
||||
+
|
||||
+class DCOMPSurfaceRegistryTest : public testing::Test {
|
||||
+ public:
|
||||
+ void SetUp() override { registry_ = DCOMPSurfaceRegistry::GetInstance(); }
|
||||
+
|
||||
+ protected:
|
||||
+ raw_ptr<DCOMPSurfaceRegistry> registry_;
|
||||
+};
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
+// Stress test for concurrent access to DCOMPSurfaceRegistry using the
|
||||
+// barrier pattern to ensure TSAN consistently catches data races.
|
||||
+//
|
||||
+// Without proper synchronization (e.g., base::Lock), this test would likely
|
||||
+// fail in the following ways:
|
||||
+// 1. Memory Corruption (UAF/HeapBOf): base::flat_map uses a contiguous
|
||||
+// std::vector. If one thread triggers a reallocation during an insertion
|
||||
+// while another thread is searching or erasing, the latter will hold an
|
||||
+// invalidated iterator or pointer.
|
||||
+// 2. Container Inconsistency: Concurrent insertions and erasures can leave
|
||||
+// the map in an unsorted or corrupted state, leading to failed lookups
|
||||
+// for valid tokens.
|
||||
+// 3. Sanitizer Triggers: ASan would detect container-overflow or
|
||||
+// heap-use-after-free, and TSan would flag a data race.
|
||||
+TEST_F(DCOMPSurfaceRegistryTest, ConcurrentRegisterAndTake) {
|
||||
+ const int kOpsPerThread = 100;
|
||||
+
|
||||
+ std::vector<base::UnguessableToken> tokens;
|
||||
+ base::Lock tokens_lock;
|
||||
+
|
||||
+ std::atomic<bool> start_flag{false};
|
||||
+ std::atomic<int> threads_ready{0};
|
||||
+
|
||||
+ auto register_worker = [&]() {
|
||||
+ threads_ready++;
|
||||
+ while (!start_flag.load(std::memory_order_acquire)) {
|
||||
+ std::this_thread::yield();
|
||||
+ }
|
||||
+
|
||||
+ for (int i = 0; i < kOpsPerThread; ++i) {
|
||||
+ base::win::ScopedHandle handle(
|
||||
+ ::CreateEvent(nullptr, FALSE, FALSE, nullptr));
|
||||
+ base::UnguessableToken token =
|
||||
+ registry_->RegisterDCOMPSurfaceHandle(std::move(handle));
|
||||
+ {
|
||||
+ base::AutoLock lock(tokens_lock);
|
||||
+ tokens.push_back(token);
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ auto take_worker = [&]() {
|
||||
+ threads_ready++;
|
||||
+ while (!start_flag.load(std::memory_order_acquire)) {
|
||||
+ std::this_thread::yield();
|
||||
+ }
|
||||
+
|
||||
+ int taken = 0;
|
||||
+ while (taken < kOpsPerThread) {
|
||||
+ base::UnguessableToken token;
|
||||
+ {
|
||||
+ base::AutoLock lock(tokens_lock);
|
||||
+ if (!tokens.empty()) {
|
||||
+ token = tokens.back();
|
||||
+ tokens.pop_back();
|
||||
+ }
|
||||
+ }
|
||||
+ if (!token.is_empty()) {
|
||||
+ base::win::ScopedHandle handle =
|
||||
+ registry_->TakeDCOMPSurfaceHandle(token);
|
||||
+ taken++;
|
||||
+ } else {
|
||||
+ std::this_thread::yield();
|
||||
+ }
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ // With the barrier pattern, two threads are sufficient to trigger
|
||||
+ // the race condition for TSAN.
|
||||
+ std::thread t1(register_worker);
|
||||
+ std::thread t2(take_worker);
|
||||
+
|
||||
+ // Wait until both threads are ready at the starting line.
|
||||
+ while (threads_ready.load(std::memory_order_relaxed) < 2) {
|
||||
+ std::this_thread::yield();
|
||||
+ }
|
||||
+
|
||||
+ // Signal the staring flag to allow both threads to race from the initialized
|
||||
+ // state.
|
||||
+ start_flag.store(true, std::memory_order_release);
|
||||
+
|
||||
+ t1.join();
|
||||
+ t2.join();
|
||||
+}
|
||||
+
|
||||
+} // namespace gl
|
||||
@@ -1,210 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Fergal Daly <fergal@chromium.org>
|
||||
Date: Mon, 6 Apr 2026 19:49:06 -0700
|
||||
Subject: Fix UAF in FileSystemAccessChangeSource.
|
||||
|
||||
`DidInitialize` calls any outstanding initialization callbacks but a
|
||||
callback can delete this. The code guards against this in its access
|
||||
of `initialization_callbacks_` but not `initialization_result_`.
|
||||
|
||||
This fix keeps a copy of the result on the stack.
|
||||
|
||||
This also adds a test which fails with ASAN before the fix is applied
|
||||
and passes after.
|
||||
|
||||
The basic test code was written by Gemini.
|
||||
|
||||
Fixed: 497880137
|
||||
Change-Id: I046831db23cb4b8e41964910e2aede9b1be0db7f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7728464
|
||||
Auto-Submit: Fergal Daly <fergal@chromium.org>
|
||||
Reviewed-by: Ming-Ying Chung <mych@chromium.org>
|
||||
Commit-Queue: Ming-Ying Chung <mych@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1610499}
|
||||
|
||||
diff --git a/content/browser/file_system_access/file_system_access_change_source.cc b/content/browser/file_system_access/file_system_access_change_source.cc
|
||||
index 566dc1ea40b43a54b33d70e82a20ff5695b57b5e..48bd867a9d3d140eaf515ea7bc1613231f7e79e9 100644
|
||||
--- a/content/browser/file_system_access/file_system_access_change_source.cc
|
||||
+++ b/content/browser/file_system_access/file_system_access_change_source.cc
|
||||
@@ -71,13 +71,14 @@ void FileSystemAccessChangeSource::DidInitialize(
|
||||
CHECK(!initialization_result_.has_value());
|
||||
CHECK(!initialization_callbacks_.empty());
|
||||
|
||||
- initialization_result_ = std::move(result);
|
||||
+ // The callbacks may cause |this| to be deleted, so we should only use
|
||||
+ // stack-based objects below.
|
||||
+ initialization_result_ = result->Clone();
|
||||
|
||||
- // Move the callbacks to the stack since they may cause |this| to be deleted.
|
||||
auto initialization_callbacks = std::move(initialization_callbacks_);
|
||||
initialization_callbacks_.clear();
|
||||
for (auto& callback : initialization_callbacks) {
|
||||
- std::move(callback).Run(initialization_result_->Clone());
|
||||
+ std::move(callback).Run(result->Clone());
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/content/browser/file_system_access/file_system_access_change_source_unittest.cc b/content/browser/file_system_access/file_system_access_change_source_unittest.cc
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..b0f15909bebda29fc2ec689a6d3b15d797dcc722
|
||||
--- /dev/null
|
||||
+++ b/content/browser/file_system_access/file_system_access_change_source_unittest.cc
|
||||
@@ -0,0 +1,146 @@
|
||||
+// Copyright 2026 The Chromium Authors
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#include "content/browser/file_system_access/file_system_access_change_source.h"
|
||||
+
|
||||
+#include "base/files/scoped_temp_dir.h"
|
||||
+#include "base/functional/bind.h"
|
||||
+#include "base/memory/scoped_refptr.h"
|
||||
+#include "base/task/sequenced_task_runner.h"
|
||||
+#include "base/test/task_environment.h"
|
||||
+#include "base/test/test_future.h"
|
||||
+#include "content/browser/file_system_access/file_system_access_watch_scope.h"
|
||||
+#include "storage/browser/file_system/file_system_context.h"
|
||||
+#include "storage/browser/file_system/file_system_url.h"
|
||||
+#include "storage/browser/quota/quota_manager_proxy.h"
|
||||
+#include "storage/browser/test/test_file_system_context.h"
|
||||
+#include "testing/gmock/include/gmock/gmock.h"
|
||||
+#include "testing/gtest/include/gtest/gtest.h"
|
||||
+#include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom.h"
|
||||
+
|
||||
+namespace content {
|
||||
+
|
||||
+namespace {
|
||||
+
|
||||
+class MockRawChangeObserver
|
||||
+ : public FileSystemAccessChangeSource::RawChangeObserver {
|
||||
+ public:
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnRawChange,
|
||||
+ (const storage::FileSystemURL& changed_url,
|
||||
+ bool error,
|
||||
+ const FileSystemAccessChangeSource::ChangeInfo& change_info,
|
||||
+ const FileSystemAccessWatchScope& scope),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnUsageChange,
|
||||
+ (size_t old_usage,
|
||||
+ size_t new_usage,
|
||||
+ const FileSystemAccessWatchScope& scope),
|
||||
+ (override));
|
||||
+ MOCK_METHOD(void,
|
||||
+ OnSourceBeingDestroyed,
|
||||
+ (FileSystemAccessChangeSource * source),
|
||||
+ (override));
|
||||
+};
|
||||
+
|
||||
+class FakeChangeSource : public FileSystemAccessChangeSource {
|
||||
+ public:
|
||||
+ FakeChangeSource(
|
||||
+ FileSystemAccessWatchScope scope,
|
||||
+ scoped_refptr<storage::FileSystemContext> file_system_context)
|
||||
+ : FileSystemAccessChangeSource(std::move(scope),
|
||||
+ std::move(file_system_context)) {}
|
||||
+ ~FakeChangeSource() override = default;
|
||||
+
|
||||
+ // FileSystemAccessChangeSource:
|
||||
+ void Initialize(
|
||||
+ base::OnceCallback<void(blink::mojom::FileSystemAccessErrorPtr)>
|
||||
+ on_source_initialized) override {
|
||||
+ base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
|
||||
+ FROM_HERE, base::BindOnce(std::move(on_source_initialized),
|
||||
+ blink::mojom::FileSystemAccessError::New(
|
||||
+ blink::mojom::FileSystemAccessStatus::kOk,
|
||||
+ base::File::FILE_OK, "")));
|
||||
+ }
|
||||
+
|
||||
+ void Signal(const storage::FileSystemURL& changed_url,
|
||||
+ bool error = false,
|
||||
+ ChangeInfo change_info = ChangeInfo()) {
|
||||
+ NotifyOfChange(changed_url, error, change_info);
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
+class FileSystemAccessChangeSourceTest : public testing::Test {
|
||||
+ public:
|
||||
+ FileSystemAccessChangeSourceTest()
|
||||
+ : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) {}
|
||||
+
|
||||
+ void SetUp() override {
|
||||
+ ASSERT_TRUE(dir_.CreateUniqueTempDir());
|
||||
+ file_system_context_ = storage::CreateFileSystemContextForTesting(
|
||||
+ /*quota_manager_proxy=*/nullptr, dir_.GetPath());
|
||||
+ }
|
||||
+
|
||||
+ protected:
|
||||
+ base::test::TaskEnvironment task_environment_;
|
||||
+ base::ScopedTempDir dir_;
|
||||
+ scoped_refptr<storage::FileSystemContext> file_system_context_;
|
||||
+};
|
||||
+
|
||||
+TEST_F(FileSystemAccessChangeSourceTest, CreateAndInitialize) {
|
||||
+ auto file_path = dir_.GetPath().AppendASCII("file");
|
||||
+ auto file_url = file_system_context_->CreateCrackedFileSystemURL(
|
||||
+ blink::StorageKey(), storage::kFileSystemTypeLocal, file_path);
|
||||
+
|
||||
+ auto scope = FileSystemAccessWatchScope::GetScopeForFileWatch(file_url);
|
||||
+ FakeChangeSource source(scope, file_system_context_);
|
||||
+
|
||||
+ base::test::TestFuture<blink::mojom::FileSystemAccessErrorPtr> future;
|
||||
+ source.EnsureInitialized(future.GetCallback());
|
||||
+ EXPECT_EQ(future.Get()->status, blink::mojom::FileSystemAccessStatus::kOk);
|
||||
+}
|
||||
+
|
||||
+TEST_F(FileSystemAccessChangeSourceTest, NotifyOfChange) {
|
||||
+ auto file_path = dir_.GetPath().AppendASCII("file");
|
||||
+ auto file_url = file_system_context_->CreateCrackedFileSystemURL(
|
||||
+ blink::StorageKey(), storage::kFileSystemTypeLocal, file_path);
|
||||
+
|
||||
+ auto scope = FileSystemAccessWatchScope::GetScopeForFileWatch(file_url);
|
||||
+ FakeChangeSource source(scope, file_system_context_);
|
||||
+
|
||||
+ MockRawChangeObserver observer;
|
||||
+ source.AddObserver(&observer);
|
||||
+
|
||||
+ EXPECT_CALL(observer, OnRawChange(testing::Eq(file_url), testing::IsFalse(),
|
||||
+ testing::_, testing::Eq(scope)));
|
||||
+ source.Signal(file_url);
|
||||
+
|
||||
+ source.RemoveObserver(&observer);
|
||||
+}
|
||||
+
|
||||
+// A callback passed to `EnsureInitialized` may result in `this` being
|
||||
+// destroyed. This tests that `DidInitialize` (which calls the callbacks) is
|
||||
+// robust to that situation. See https://crbug.com/497880137.
|
||||
+TEST_F(FileSystemAccessChangeSourceTest, TestDestroyFromInitializeCallback) {
|
||||
+ auto file_path = dir_.GetPath().AppendASCII("file");
|
||||
+ auto file_url = file_system_context_->CreateCrackedFileSystemURL(
|
||||
+ blink::StorageKey(), storage::kFileSystemTypeLocal, file_path);
|
||||
+
|
||||
+ auto scope = FileSystemAccessWatchScope::GetScopeForFileWatch(file_url);
|
||||
+ FakeChangeSource* source = new FakeChangeSource(scope, file_system_context_);
|
||||
+
|
||||
+ source->EnsureInitialized(base::BindOnce(
|
||||
+ [](FakeChangeSource* source, blink::mojom::FileSystemAccessErrorPtr) {
|
||||
+ delete source;
|
||||
+ },
|
||||
+ base::Unretained(source)));
|
||||
+ base::test::TestFuture<blink::mojom::FileSystemAccessErrorPtr> future;
|
||||
+ source->EnsureInitialized(future.GetCallback());
|
||||
+ EXPECT_EQ(future.Get()->status, blink::mojom::FileSystemAccessStatus::kOk);
|
||||
+}
|
||||
+
|
||||
+} // namespace content
|
||||
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
|
||||
index 4521cc9e247c44248627c12b9eda0961f837d744..6ea61eb47fcde420261a9dd7e7e3feefa31f87d2 100644
|
||||
--- a/content/test/BUILD.gn
|
||||
+++ b/content/test/BUILD.gn
|
||||
@@ -2680,6 +2680,7 @@ test("content_unittests") {
|
||||
"../browser/fenced_frame/redacted_fenced_frame_config_mojom_traits_unittest.cc",
|
||||
"../browser/file_system/browser_file_system_helper_unittest.cc",
|
||||
"../browser/file_system/file_system_operation_runner_unittest.cc",
|
||||
+ "../browser/file_system_access/file_system_access_change_source_unittest.cc",
|
||||
"../browser/file_system_access/file_system_access_directory_handle_impl_unittest.cc",
|
||||
"../browser/file_system_access/file_system_access_file_handle_impl_unittest.cc",
|
||||
"../browser/file_system_access/file_system_access_file_modification_host_impl_unittest.cc",
|
||||
@@ -1,59 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Kenichi Ishibashi <bashi@chromium.org>
|
||||
Date: Fri, 10 Apr 2026 17:14:24 -0700
|
||||
Subject: [CORS] Block forbidden methods for no-cors requests
|
||||
|
||||
Previously, forbidden methods like TRACE and TRACK were allowed when
|
||||
the request mode was no-cors, and only CONNECT was unconditionally
|
||||
blocked.
|
||||
|
||||
This CL updates CorsURLLoaderFactory::IsValidRequest to block all
|
||||
forbidden methods regardless of the request mode. The unit test is
|
||||
also updated to reflect this new restriction.
|
||||
|
||||
Bug: 498765210
|
||||
Change-Id: Ie451a3c2b8fa7aafdebade8b3ba517be3ce255f8
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7743444
|
||||
Reviewed-by: mmenke <mmenke@chromium.org>
|
||||
Commit-Queue: Kenichi Ishibashi <bashi@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1613186}
|
||||
|
||||
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc
|
||||
index 6a1eb075b0ed581bf81c43a0439da12eff20664c..bf02d6663b47413e47ebfe9ae4ba5799787f69ae 100644
|
||||
--- a/services/network/cors/cors_url_loader_factory.cc
|
||||
+++ b/services/network/cors/cors_url_loader_factory.cc
|
||||
@@ -910,13 +910,8 @@ bool CorsURLLoaderFactory::IsValidRequest(
|
||||
return false;
|
||||
}
|
||||
|
||||
- // Don't allow forbidden methods for any requests except RequestMode::kNoCors.
|
||||
- // Don't allow CONNECT method for any request.
|
||||
- if ((request.mode != mojom::RequestMode::kNoCors &&
|
||||
- cors::IsForbiddenMethod(request.method)) ||
|
||||
- (request.mode == mojom::RequestMode::kNoCors &&
|
||||
- base::EqualsCaseInsensitiveASCII(
|
||||
- request.method, net::HttpRequestHeaders::kConnectMethod))) {
|
||||
+ // Don't allow forbidden methods.
|
||||
+ if (cors::IsForbiddenMethod(request.method)) {
|
||||
mojo::ReportBadMessage("CorsURLLoaderFactory: Forbidden method");
|
||||
return false;
|
||||
}
|
||||
diff --git a/services/network/cors/cors_url_loader_unittest.cc b/services/network/cors/cors_url_loader_unittest.cc
|
||||
index e9bbbc2013e6fb9498bec0982c045ea8b937a207..23a9e8093aa8d6cafb9a949d4a1dae86bd52a99d 100644
|
||||
--- a/services/network/cors/cors_url_loader_unittest.cc
|
||||
+++ b/services/network/cors/cors_url_loader_unittest.cc
|
||||
@@ -109,11 +109,10 @@ TEST_F(CorsURLLoaderTest, ForbiddenMethods) {
|
||||
std::string forbidden_method;
|
||||
bool expect_allowed_for_no_cors;
|
||||
} kTestCases[] = {
|
||||
- // CONNECT is never allowed, while TRACE and TRACK are allowed only with
|
||||
- // RequestMode::kNoCors.
|
||||
+ // CONNECT, TRACE and TRACK are not allowed for any mode.
|
||||
{"CONNECT", false},
|
||||
- {"TRACE", true},
|
||||
- {"TRACK", true},
|
||||
+ {"TRACE", false},
|
||||
+ {"TRACK", false},
|
||||
};
|
||||
for (const auto& test_case : kTestCases) {
|
||||
SCOPED_TRACE(test_case.forbidden_method);
|
||||
@@ -1,78 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Sunny Sachanandani <sunnyps@chromium.org>
|
||||
Date: Wed, 8 Apr 2026 16:29:57 -0700
|
||||
Subject: [gpu] Fix OOB write due to unvalidated get_offset
|
||||
|
||||
A compromised GPU process can provide an invalid get_offset to the
|
||||
CommandBufferHelper (e.g., via shared memory). This offset is used to
|
||||
calculate available space and could lead to out-of-bounds writes in the
|
||||
Browser process if not validated.
|
||||
|
||||
This change adds a bounds check in
|
||||
CommandBufferHelper::UpdateCachedState to ensure that the cached
|
||||
get_offset is within the valid range [0, total_entry_count_]. If an
|
||||
invalid offset is detected, it forces a context loss, frees the ring
|
||||
buffer, and marks the helper as unusable, preventing further operations.
|
||||
|
||||
Bug: 498782145
|
||||
Test: CommandBufferHelperTest.*
|
||||
Change-Id: I8c64e546ecdc90a5a22d15e57ff762a86a6a6964
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7739951
|
||||
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
|
||||
Auto-Submit: Sunny Sachanandani <sunnyps@chromium.org>
|
||||
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1611853}
|
||||
|
||||
diff --git a/gpu/command_buffer/client/cmd_buffer_helper.cc b/gpu/command_buffer/client/cmd_buffer_helper.cc
|
||||
index ccda45b133c6a9f2ee60ccc8900bd4a4ce328394..5aea0c81b29b3507099f399c374f3cb372a3100e 100644
|
||||
--- a/gpu/command_buffer/client/cmd_buffer_helper.cc
|
||||
+++ b/gpu/command_buffer/client/cmd_buffer_helper.cc
|
||||
@@ -158,6 +158,17 @@ void CommandBufferHelper::UpdateCachedState(const CommandBuffer::State& state) {
|
||||
service_on_old_buffer_ =
|
||||
(state.set_get_buffer_count != set_get_buffer_count_);
|
||||
cached_get_offset_ = service_on_old_buffer_ ? 0 : state.get_offset;
|
||||
+
|
||||
+ if (!service_on_old_buffer_ &&
|
||||
+ (cached_get_offset_ < 0 || cached_get_offset_ > total_entry_count_)) {
|
||||
+ command_buffer_->ForceLostContext(error::kGuilty);
|
||||
+ FreeRingBuffer();
|
||||
+ usable_ = false;
|
||||
+ context_lost_ = true;
|
||||
+ cached_get_offset_ = 0; // Safe fallback
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
cached_last_token_read_ = state.token;
|
||||
// Don't transition from a lost context to a working context.
|
||||
context_lost_ |= error::IsError(state.error);
|
||||
diff --git a/gpu/command_buffer/client/cmd_buffer_helper_test.cc b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
|
||||
index 5b1e5fae133ef75a99dab4ba1f8d2beddef68138..31e46714756ee30bf2fc3353693b6d49be8f6076 100644
|
||||
--- a/gpu/command_buffer/client/cmd_buffer_helper_test.cc
|
||||
+++ b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
|
||||
@@ -70,6 +70,8 @@ class CommandBufferHelperTest : public testing::Test {
|
||||
return helper_->immediate_entry_count_;
|
||||
}
|
||||
|
||||
+ int32_t TotalEntryCount() const { return helper_->total_entry_count_; }
|
||||
+
|
||||
// Adds a command to the buffer through the helper, while adding it as an
|
||||
// expected call on the API mock.
|
||||
void AddCommandWithExpect(error::Error _return,
|
||||
@@ -655,6 +657,17 @@ TEST_F(CommandBufferHelperTest, IsContextLost) {
|
||||
EXPECT_TRUE(helper_->IsContextLost());
|
||||
}
|
||||
|
||||
+TEST_F(CommandBufferHelperTest, TestInvalidGetOffset) {
|
||||
+ EXPECT_FALSE(helper_->IsContextLost());
|
||||
+ EXPECT_TRUE(helper_->usable());
|
||||
+
|
||||
+ command_buffer_->SetGetOffsetForTest(TotalEntryCount() + 1);
|
||||
+ helper_->RefreshCachedToken(); // calls UpdateCachedState internally.
|
||||
+
|
||||
+ EXPECT_TRUE(helper_->IsContextLost());
|
||||
+ EXPECT_FALSE(helper_->usable());
|
||||
+}
|
||||
+
|
||||
// Checks helper's 'flush generation' updates.
|
||||
TEST_F(CommandBufferHelperTest, TestFlushGeneration) {
|
||||
// Explicit flushing only.
|
||||
@@ -1,368 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Eugene Zemtsov <eugene@chromium.org>
|
||||
Date: Wed, 8 Apr 2026 18:32:31 -0700
|
||||
Subject: media: Zero-copy VP9 alpha decoding in VpxVideoDecoder
|
||||
|
||||
Configures the VP9 alpha decoder to use `memory_pool_` for external
|
||||
frame buffers, eliminating the need for `libyuv::CopyPlane`.
|
||||
|
||||
The `VideoFrame` now wraps the alpha data directly from the pool using
|
||||
a second destruction observer. `AllocateAlphaPlaneForFrameBuffer` and
|
||||
`alpha_data` tracking are removed from `FrameBufferPool`.
|
||||
|
||||
Bug: 500066234
|
||||
Change-Id: I6e7cf13bcc8a5a1759acfd51961859c4c57fcbf2
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7737984
|
||||
Reviewed-by: Ted (Chromium) Meyer <tmathmeyer@chromium.org>
|
||||
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
|
||||
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1611919}
|
||||
|
||||
diff --git a/media/base/frame_buffer_pool.cc b/media/base/frame_buffer_pool.cc
|
||||
index ceb0313c1fa36483ac545a6440737da035a1224c..59bc0790178aa2eb2e6960ef396c96ae18bbba09 100644
|
||||
--- a/media/base/frame_buffer_pool.cc
|
||||
+++ b/media/base/frame_buffer_pool.cc
|
||||
@@ -56,7 +56,6 @@ struct FrameBufferPool::FrameBuffer {
|
||||
// Not using std::vector<uint8_t> as resize() calls take a really long time
|
||||
// for large buffers.
|
||||
BytesArray data;
|
||||
- BytesArray alpha_data;
|
||||
bool held_by_library = false;
|
||||
// Needs to be a counter since a frame buffer might be used multiple times.
|
||||
int held_by_frame = 0;
|
||||
@@ -155,24 +154,6 @@ void FrameBufferPool::ReleaseFrameBuffer(void* fb_priv) {
|
||||
}
|
||||
}
|
||||
|
||||
-base::span<uint8_t> FrameBufferPool::AllocateAlphaPlaneForFrameBuffer(
|
||||
- size_t min_size,
|
||||
- void* fb_priv) {
|
||||
- base::AutoLock lock(lock_);
|
||||
- DCHECK(fb_priv);
|
||||
-
|
||||
- auto* frame_buffer = static_cast<FrameBuffer*>(fb_priv);
|
||||
- DCHECK(IsUsedLocked(frame_buffer));
|
||||
- if (frame_buffer->alpha_data.size() < min_size) {
|
||||
- // Free the existing |alpha_data| first so that the memory can be reused,
|
||||
- // if possible.
|
||||
- frame_buffer->alpha_data = {};
|
||||
- frame_buffer->alpha_data = AllocateMemory(min_size, zero_initialize_memory_,
|
||||
- force_allocation_error_);
|
||||
- }
|
||||
- return frame_buffer->alpha_data;
|
||||
-}
|
||||
-
|
||||
base::OnceClosure FrameBufferPool::CreateFrameCallback(void* fb_priv) {
|
||||
base::AutoLock lock(lock_);
|
||||
|
||||
@@ -210,10 +191,9 @@ bool FrameBufferPool::OnMemoryDump(
|
||||
size_t bytes_reserved = 0;
|
||||
for (const auto& frame_buffer : frame_buffers_) {
|
||||
if (IsUsedLocked(frame_buffer.get())) {
|
||||
- bytes_used += frame_buffer->data.size() + frame_buffer->alpha_data.size();
|
||||
+ bytes_used += frame_buffer->data.size();
|
||||
}
|
||||
- bytes_reserved +=
|
||||
- frame_buffer->data.size() + frame_buffer->alpha_data.size();
|
||||
+ bytes_reserved += frame_buffer->data.size();
|
||||
}
|
||||
|
||||
memory_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
|
||||
diff --git a/media/base/frame_buffer_pool.h b/media/base/frame_buffer_pool.h
|
||||
index ac839b8e8bfa00d2fea203be5248a56f04cecc71..2ccb01676b0e8e1e3ca1b3cb60f2883538f2f13c 100644
|
||||
--- a/media/base/frame_buffer_pool.h
|
||||
+++ b/media/base/frame_buffer_pool.h
|
||||
@@ -48,11 +48,6 @@ class MEDIA_EXPORT FrameBufferPool
|
||||
// Called when a frame buffer allocation is no longer needed.
|
||||
void ReleaseFrameBuffer(void* fb_priv);
|
||||
|
||||
- // Allocates (or reuses) room for an alpha plane on a given frame buffer.
|
||||
- // |fb_priv| must be a value previously returned by GetFrameBuffer().
|
||||
- base::span<uint8_t> AllocateAlphaPlaneForFrameBuffer(size_t min_size,
|
||||
- void* fb_priv);
|
||||
-
|
||||
// Generates a "no_longer_needed" closure that holds a reference to this pool;
|
||||
// |fb_priv| must be a value previously returned by GetFrameBuffer(). The
|
||||
// callback may be called on any thread.
|
||||
diff --git a/media/base/frame_buffer_pool_unittest.cc b/media/base/frame_buffer_pool_unittest.cc
|
||||
index 893e941f9f9b6d7eaff98b3e9ae4278861a2b0fd..8b50896e7544e34589614216373b566598b30ec6 100644
|
||||
--- a/media/base/frame_buffer_pool_unittest.cc
|
||||
+++ b/media/base/frame_buffer_pool_unittest.cc
|
||||
@@ -32,12 +32,6 @@ TEST(FrameBufferPool, BasicFunctionality) {
|
||||
EXPECT_NE(buf1.data(), buf2.data());
|
||||
std::ranges::fill(buf2, 0);
|
||||
|
||||
- auto alpha = pool->AllocateAlphaPlaneForFrameBuffer(kBufferSize, priv1);
|
||||
- ASSERT_FALSE(alpha.empty());
|
||||
- EXPECT_NE(alpha.data(), buf1.data());
|
||||
- EXPECT_NE(alpha.data(), buf2.data());
|
||||
- std::ranges::fill(alpha, 0);
|
||||
-
|
||||
EXPECT_EQ(2u, pool->get_pool_size_for_testing());
|
||||
|
||||
// Frames are not released immediately, so this should still show two frames.
|
||||
@@ -52,7 +46,6 @@ TEST(FrameBufferPool, BasicFunctionality) {
|
||||
EXPECT_EQ(1u, pool->get_pool_size_for_testing());
|
||||
|
||||
std::ranges::fill(buf1, 0);
|
||||
- std::ranges::fill(alpha, 0);
|
||||
|
||||
// This will release all memory since we're in the shutdown state.
|
||||
std::move(frame_release_cb).Run();
|
||||
@@ -132,13 +125,6 @@ TEST(FrameBufferPool, DoesClearAllocations) {
|
||||
}
|
||||
EXPECT_FALSE(nonzero);
|
||||
|
||||
- auto alpha_buf = pool->AllocateAlphaPlaneForFrameBuffer(kBufferSize, priv1);
|
||||
- nonzero = false;
|
||||
- for (size_t i = 0; i < kBufferSize; i++) {
|
||||
- nonzero |= !!alpha_buf[i];
|
||||
- }
|
||||
- EXPECT_FALSE(nonzero);
|
||||
-
|
||||
pool->Shutdown();
|
||||
}
|
||||
|
||||
diff --git a/media/filters/vpx_video_decoder.cc b/media/filters/vpx_video_decoder.cc
|
||||
index ca1e45ed3cddcbf878f03c233f982b04425287f5..fe1b8b9b0686ed0b89737097087fca00cb1677c6 100644
|
||||
--- a/media/filters/vpx_video_decoder.cc
|
||||
+++ b/media/filters/vpx_video_decoder.cc
|
||||
@@ -250,7 +250,21 @@ bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) {
|
||||
|
||||
DCHECK(!vpx_codec_alpha_);
|
||||
vpx_codec_alpha_ = InitializeVpxContext(config);
|
||||
- return !!vpx_codec_alpha_;
|
||||
+ if (!vpx_codec_alpha_) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if (config.codec() == VideoCodec::kVP9) {
|
||||
+ if (vpx_codec_set_frame_buffer_functions(
|
||||
+ vpx_codec_alpha_.get(), &GetVP9FrameBuffer, &ReleaseVP9FrameBuffer,
|
||||
+ memory_pool_.get())) {
|
||||
+ DLOG(ERROR) << "Failed to configure external buffers for alpha. "
|
||||
+ << vpx_codec_error(vpx_codec_alpha_.get());
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
}
|
||||
|
||||
void VpxVideoDecoder::CloseDecoder() {
|
||||
@@ -546,20 +560,13 @@ bool VpxVideoDecoder::CopyVpxImageToVideoFrame(
|
||||
if (memory_pool_) {
|
||||
DCHECK_EQ(VideoCodec::kVP9, config_.codec());
|
||||
if (vpx_image_alpha) {
|
||||
+ CHECK_GT(vpx_image_alpha->stride[VPX_PLANE_Y], 0);
|
||||
size_t alpha_plane_size =
|
||||
vpx_image_alpha->stride[VPX_PLANE_Y] * vpx_image_alpha->d_h;
|
||||
- auto alpha_plane = memory_pool_->AllocateAlphaPlaneForFrameBuffer(
|
||||
- alpha_plane_size, vpx_image->fb_priv);
|
||||
- if (alpha_plane.empty()) {
|
||||
- error_status_ = DecoderStatus::Codes::kOutOfMemory;
|
||||
- // In case of OOM, abort copy.
|
||||
- return false;
|
||||
- }
|
||||
- libyuv::CopyPlane(vpx_image_alpha->planes[VPX_PLANE_Y],
|
||||
- vpx_image_alpha->stride[VPX_PLANE_Y],
|
||||
- alpha_plane.data(),
|
||||
- vpx_image_alpha->stride[VPX_PLANE_Y],
|
||||
- vpx_image_alpha->d_w, vpx_image_alpha->d_h);
|
||||
+ // SAFETY: libvpx guarantees that the Y plane has at least `stride * d_h`
|
||||
+ // bytes available.
|
||||
+ auto alpha_plane = UNSAFE_BUFFERS(base::span<uint8_t>(
|
||||
+ vpx_image_alpha->planes[VPX_PLANE_Y], alpha_plane_size));
|
||||
*video_frame = VideoFrame::WrapExternalYuvaData(
|
||||
codec_format, coded_size, gfx::Rect(visible_size), natural_size,
|
||||
vpx_image->stride[VPX_PLANE_Y], vpx_image->stride[VPX_PLANE_U],
|
||||
@@ -575,8 +582,14 @@ bool VpxVideoDecoder::CopyVpxImageToVideoFrame(
|
||||
if (!(*video_frame))
|
||||
return false;
|
||||
|
||||
- video_frame->get()->AddDestructionObserver(
|
||||
- memory_pool_->CreateFrameCallback(vpx_image->fb_priv));
|
||||
+ (*video_frame)
|
||||
+ ->AddDestructionObserver(
|
||||
+ memory_pool_->CreateFrameCallback(vpx_image->fb_priv));
|
||||
+ if (vpx_image_alpha) {
|
||||
+ (*video_frame)
|
||||
+ ->AddDestructionObserver(
|
||||
+ memory_pool_->CreateFrameCallback(vpx_image_alpha->fb_priv));
|
||||
+ }
|
||||
return true;
|
||||
}
|
||||
|
||||
diff --git a/media/filters/vpx_video_decoder.h b/media/filters/vpx_video_decoder.h
|
||||
index f53da976ba4ba9dc39c6f03c99d5937b82650399..8f8f07e419b7d16c52f550edd97af6f235fbd2ac 100644
|
||||
--- a/media/filters/vpx_video_decoder.h
|
||||
+++ b/media/filters/vpx_video_decoder.h
|
||||
@@ -102,8 +102,8 @@ class MEDIA_EXPORT VpxVideoDecoder : public OffloadableVideoDecoder {
|
||||
std::unique_ptr<vpx_codec_ctx> vpx_codec_;
|
||||
std::unique_ptr<vpx_codec_ctx> vpx_codec_alpha_;
|
||||
|
||||
- // |memory_pool_| is a single-threaded memory pool used for VP9 decoding
|
||||
- // with no alpha. |frame_pool_| is used for all other cases.
|
||||
+ // |memory_pool_| is a thread-safe memory pool used for zero-copy VP9 decoding
|
||||
+ // (both with and without alpha). |frame_pool_| is used for VP8.
|
||||
scoped_refptr<FrameBufferPool> memory_pool_;
|
||||
VideoFramePool frame_pool_;
|
||||
|
||||
diff --git a/media/filters/vpx_video_decoder_unittest.cc b/media/filters/vpx_video_decoder_unittest.cc
|
||||
index 8fba2b469656c7bd60b555bf6dea02b7b37ee701..bb32fa8e7d59f5a3136a48a2ef14dda08a95fff0 100644
|
||||
--- a/media/filters/vpx_video_decoder_unittest.cc
|
||||
+++ b/media/filters/vpx_video_decoder_unittest.cc
|
||||
@@ -175,6 +175,28 @@ class VpxVideoDecoderTest : public testing::Test {
|
||||
output_frames_.push_back(std::move(frame));
|
||||
}
|
||||
|
||||
+ // Extracts the compressed video data from the AVPacket and also checks for
|
||||
+ // side data containing an alpha channel. If found, it copies the alpha data
|
||||
+ // into the DecoderBuffer's side data. This is necessary because FFmpeg
|
||||
+ // demuxes alpha channel data as side data associated with the video packet.
|
||||
+ static scoped_refptr<DecoderBuffer> CreateBufferWithAlphaFromPacket(
|
||||
+ const AVPacket* packet) {
|
||||
+ auto buffer = DecoderBuffer::CopyFrom(AVPacketData(*packet));
|
||||
+ size_t side_data_size = 0;
|
||||
+ uint8_t* side_data_ptr = av_packet_get_side_data(
|
||||
+ packet, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, &side_data_size);
|
||||
+ if (side_data_size > 8) {
|
||||
+ // SAFETY: The best we can do here is trust the size reported by ffmpeg.
|
||||
+ auto side_data =
|
||||
+ UNSAFE_BUFFERS(base::span(side_data_ptr, side_data_size));
|
||||
+ if (base::U64FromBigEndian(side_data.first<8u>()) == 1) {
|
||||
+ buffer->WritableSideData().alpha_data =
|
||||
+ base::HeapArray<uint8_t>::CopiedFrom(side_data.subspan(8u));
|
||||
+ }
|
||||
+ }
|
||||
+ return buffer;
|
||||
+ }
|
||||
+
|
||||
MOCK_METHOD1(DecodeDone, void(DecoderStatus));
|
||||
|
||||
base::test::TaskEnvironment task_env_;
|
||||
@@ -292,6 +314,68 @@ TEST_F(VpxVideoDecoderTest, SimpleFrameReuse) {
|
||||
EXPECT_EQ(old_y_data, output_frames_.back()->data(VideoFrame::Plane::kY));
|
||||
}
|
||||
|
||||
+TEST_F(VpxVideoDecoderTest, SimpleAlphaFrameReuse) {
|
||||
+ VideoDecoderConfig config = TestVideoConfig::Normal(VideoCodec::kVP9);
|
||||
+ config.Initialize(
|
||||
+ config.codec(), config.profile(),
|
||||
+ VideoDecoderConfig::AlphaMode::kHasAlpha, config.color_space_info(),
|
||||
+ config.video_transformation(), config.coded_size(), config.visible_rect(),
|
||||
+ config.natural_size(), config.extra_data(), config.encryption_scheme());
|
||||
+ InitializeWithConfig(config);
|
||||
+ scoped_refptr<DecoderBuffer> alpha_frame = ReadTestDataFile("bear-vp9a.webm");
|
||||
+
|
||||
+ // Read frames from the webm file.
|
||||
+ InMemoryUrlProtocol protocol(*alpha_frame, false);
|
||||
+ FFmpegGlue glue(&protocol);
|
||||
+ ASSERT_TRUE(glue.OpenContext());
|
||||
+
|
||||
+ auto packet = ScopedAVPacket::Allocate();
|
||||
+
|
||||
+ // Decode first frame
|
||||
+ ASSERT_GE(av_read_frame(glue.format_context(), packet.get()), 0);
|
||||
+ auto buffer = CreateBufferWithAlphaFromPacket(packet.get());
|
||||
+ Decode(buffer);
|
||||
+ av_packet_unref(packet.get());
|
||||
+
|
||||
+ ASSERT_EQ(1u, output_frames_.size());
|
||||
+ scoped_refptr<VideoFrame> frame = std::move(output_frames_.front());
|
||||
+ EXPECT_EQ(PIXEL_FORMAT_I420A, frame->format());
|
||||
+ const uint8_t* old_y_data = frame->data(VideoFrame::Plane::kY);
|
||||
+ const uint8_t* old_a_data = frame->data(VideoFrame::Plane::kA);
|
||||
+ output_frames_.pop_back();
|
||||
+
|
||||
+ // Clear frame reference to return the frame to the pool.
|
||||
+ frame = nullptr;
|
||||
+
|
||||
+ // Decode second frame.
|
||||
+ Decode(buffer);
|
||||
+ const uint8_t* mid_y_data =
|
||||
+ output_frames_.front()->data(VideoFrame::Plane::kY);
|
||||
+ const uint8_t* mid_a_data =
|
||||
+ output_frames_.front()->data(VideoFrame::Plane::kA);
|
||||
+ output_frames_.clear();
|
||||
+
|
||||
+ // Issuing another decode should reuse buffers from the pool.
|
||||
+ Decode(buffer);
|
||||
+
|
||||
+ ASSERT_EQ(1u, output_frames_.size());
|
||||
+ const uint8_t* new_y_data =
|
||||
+ output_frames_.back()->data(VideoFrame::Plane::kY);
|
||||
+ const uint8_t* new_a_data =
|
||||
+ output_frames_.back()->data(VideoFrame::Plane::kA);
|
||||
+
|
||||
+ // The pool is shared, so buffers might be reused in a different order (e.g. Y
|
||||
+ // might get the buffer previously used for A). Because libvpx allocates the
|
||||
+ // new frame before releasing the old reference frame, we need to check across
|
||||
+ // all previously allocated buffers.
|
||||
+ bool reused_y = new_y_data == old_y_data || new_y_data == old_a_data ||
|
||||
+ new_y_data == mid_y_data || new_y_data == mid_a_data;
|
||||
+ bool reused_a = new_a_data == old_y_data || new_a_data == old_a_data ||
|
||||
+ new_a_data == mid_y_data || new_a_data == mid_a_data;
|
||||
+ EXPECT_TRUE(reused_y);
|
||||
+ EXPECT_TRUE(reused_a);
|
||||
+}
|
||||
+
|
||||
TEST_F(VpxVideoDecoderTest, SimpleFormatChange) {
|
||||
scoped_refptr<DecoderBuffer> large_frame =
|
||||
ReadTestDataFile("vp9-I-frame-1280x720");
|
||||
@@ -311,10 +395,41 @@ TEST_F(VpxVideoDecoderTest, FrameValidAfterPoolDestruction) {
|
||||
|
||||
// Write to the Y plane. The memory tools should detect a
|
||||
// use-after-free if the storage was actually removed by pool destruction.
|
||||
- UNSAFE_TODO(
|
||||
- memset(output_frames_.front()->writable_data(VideoFrame::Plane::kY), 0xff,
|
||||
- output_frames_.front()->rows(VideoFrame::Plane::kY) *
|
||||
- output_frames_.front()->stride(VideoFrame::Plane::kY)));
|
||||
+ std::ranges::fill(
|
||||
+ output_frames_.front()->writable_span(VideoFrame::Plane::kY), 0xff);
|
||||
+}
|
||||
+
|
||||
+TEST_F(VpxVideoDecoderTest, AlphaFrameValidAfterPoolDestruction) {
|
||||
+ VideoDecoderConfig config = TestVideoConfig::Normal(VideoCodec::kVP9);
|
||||
+ config.Initialize(
|
||||
+ config.codec(), config.profile(),
|
||||
+ VideoDecoderConfig::AlphaMode::kHasAlpha, config.color_space_info(),
|
||||
+ config.video_transformation(), config.coded_size(), config.visible_rect(),
|
||||
+ config.natural_size(), config.extra_data(), config.encryption_scheme());
|
||||
+ InitializeWithConfig(config);
|
||||
+ scoped_refptr<DecoderBuffer> alpha_frame = ReadTestDataFile("bear-vp9a.webm");
|
||||
+
|
||||
+ InMemoryUrlProtocol protocol(*alpha_frame, false);
|
||||
+ FFmpegGlue glue(&protocol);
|
||||
+ ASSERT_TRUE(glue.OpenContext());
|
||||
+
|
||||
+ auto packet = ScopedAVPacket::Allocate();
|
||||
+ ASSERT_GE(av_read_frame(glue.format_context(), packet.get()), 0);
|
||||
+ auto buffer = CreateBufferWithAlphaFromPacket(packet.get());
|
||||
+ Decode(std::move(buffer));
|
||||
+ av_packet_unref(packet.get());
|
||||
+
|
||||
+ ASSERT_EQ(1u, output_frames_.size());
|
||||
+ EXPECT_EQ(PIXEL_FORMAT_I420A, output_frames_.front()->format());
|
||||
+
|
||||
+ Destroy();
|
||||
+
|
||||
+ // Write to the Y and A planes. The memory tools should detect a
|
||||
+ // use-after-free if the storage was actually removed by pool destruction.
|
||||
+ std::ranges::fill(
|
||||
+ output_frames_.front()->writable_span(VideoFrame::Plane::kY), 0xff);
|
||||
+ std::ranges::fill(
|
||||
+ output_frames_.front()->writable_span(VideoFrame::Plane::kA), 0xff);
|
||||
}
|
||||
|
||||
// The test stream uses profile 2, which needs high bit depth support in libvpx.
|
||||
@@ -362,8 +477,7 @@ TEST_F(VpxVideoDecoderTest, MemoryPoolAllowsMultipleDisplay) {
|
||||
Destroy();
|
||||
|
||||
// ASAN will be very unhappy with this line if the above is incorrect.
|
||||
- UNSAFE_TODO(memset(last_frame->writable_data(VideoFrame::Plane::kY), 0,
|
||||
- last_frame->row_bytes(VideoFrame::Plane::kY)));
|
||||
+ std::ranges::fill(last_frame->writable_span(VideoFrame::Plane::kY), 0);
|
||||
}
|
||||
#endif // !defined(LIBVPX_NO_HIGH_BIT_DEPTH) && !defined(ARCH_CPU_ARM_FAMILY)
|
||||
|
||||
@@ -8,10 +8,10 @@ electron objects that extend gin::Wrappable and gets
|
||||
allocated on the cpp heap
|
||||
|
||||
diff --git a/gin/public/wrappable_pointer_tags.h b/gin/public/wrappable_pointer_tags.h
|
||||
index fee622ebde42211de6f702b754cfa38595df5a1c..0f649fa562cef261b127dca7769dd5687a916342 100644
|
||||
index fee622ebde42211de6f702b754cfa38595df5a1c..6b524632ebb405e473cf4fe8e253bd13bf7b67e5 100644
|
||||
--- a/gin/public/wrappable_pointer_tags.h
|
||||
+++ b/gin/public/wrappable_pointer_tags.h
|
||||
@@ -77,7 +77,23 @@ enum WrappablePointerTag : uint16_t {
|
||||
@@ -77,7 +77,20 @@ enum WrappablePointerTag : uint16_t {
|
||||
kWebAXObjectProxy, // content::WebAXObjectProxy
|
||||
kWrappedExceptionHandler, // extensions::WrappedExceptionHandler
|
||||
kIndigoContext, // indigo::IndigoContext
|
||||
@@ -20,15 +20,12 @@ index fee622ebde42211de6f702b754cfa38595df5a1c..0f649fa562cef261b127dca7769dd568
|
||||
+ kElectronDataPipeHolder, // electron::api::DataPipeHolder
|
||||
+ kElectronDebugger, // electron::api::Debugger
|
||||
+ kElectronEvent, // gin_helper::internal::Event
|
||||
+ kElectronExtensions, // electron::api::Extensions
|
||||
+ kElectronMenu, // electron::api::Menu
|
||||
+ kElectronNetLog, // electron::api::NetLog
|
||||
+ kElectronPowerMonitor, // electron::api::PowerMonitor
|
||||
+ kElectronPowerSaveBlocker, // electron::api::PowerSaveBlocker
|
||||
+ kElectronProtocol, // electron::api::Protocol
|
||||
+ kElectronReplyChannel, // gin_helper::internal::ReplyChannel
|
||||
+ kElectronScreen, // electron::api::Screen
|
||||
+ kElectronServiceWorkerContext, // electron::api::ServiceWorkerContext
|
||||
+ kElectronSession, // electron::api::Session
|
||||
+ kElectronTray, // electron::api::Tray
|
||||
+ kElectronWebRequest, // electron::api::WebRequest
|
||||
|
||||
@@ -34,10 +34,10 @@ index 1d03dc809d4c18f24314d94811e0bf527aa7b5b4..16030bcecb2e39b8870144ce7c3d11dd
|
||||
virtual void DidClearWindowObject() {}
|
||||
virtual void DidChangeScrollOffset() {}
|
||||
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
|
||||
index 5279ba15f45bd7634b5f24553ad64c0069318cc0..2840f22e2b8b4aae09a06774a70f2ec7340536d9 100644
|
||||
index f28214d369138eb854a556165f0a946c07cfdb9c..7fb428cfdda42d1aac6922f2ed6f37369d4979e2 100644
|
||||
--- a/content/renderer/render_frame_impl.cc
|
||||
+++ b/content/renderer/render_frame_impl.cc
|
||||
@@ -4739,10 +4739,11 @@ void RenderFrameImpl::DidInstallConditionalFeatures(
|
||||
@@ -4737,10 +4737,11 @@ void RenderFrameImpl::DidInstallConditionalFeatures(
|
||||
observer.DidInstallConditionalFeatures(context, world_id);
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ index 8482d7fab12634e6b9a8d5f9bab6c7e428bb99ee..4f131fbfc9350352bce4430f92b9f2cf
|
||||
void WillInitializeWorkerContext() override;
|
||||
void WillDestroyWorkerContext(v8::Local<v8::Context> context) override;
|
||||
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
|
||||
index fd3960fce4c61c5c530c817bd12e1ba1698b8db6..48a159d7d5ea57b4533fdaf38fe79a74c490207a 100644
|
||||
index df4634ffecb4b58885374199a863092bfdecf681..33e0ed7a7beae556328ec8bff5e8101acc4b3d26 100644
|
||||
--- a/extensions/renderer/dispatcher.cc
|
||||
+++ b/extensions/renderer/dispatcher.cc
|
||||
@@ -530,6 +530,7 @@ void Dispatcher::DidInitializeServiceWorkerContextOnWorkerThread(
|
||||
@@ -259,10 +259,10 @@ index 5a0f42b4b7e5eb67d476c948caa201ee6fc7b3ca..1a0562ad9ccfd414d6295b597b9d8094
|
||||
bool AllowScriptExtensions() override { return false; }
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
|
||||
index abbabcb7e2767ddef7e5bcda7812471282474260..e9e4864dbb1433d70344c58c4aeac6a906c50e02 100644
|
||||
index f8bcd6fada82f9f0d473fa02799d0218c0e53b0b..765f10c71c50f2d89f8cdaf06d07ce4a53ef298c 100644
|
||||
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
|
||||
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
|
||||
@@ -181,6 +181,7 @@ void ServiceWorkerGlobalScopeProxy::WillEvaluateScript() {
|
||||
@@ -182,6 +182,7 @@ void ServiceWorkerGlobalScopeProxy::WillEvaluateScript() {
|
||||
ScriptState::Scope scope(
|
||||
WorkerGlobalScope()->ScriptController()->GetScriptState());
|
||||
Client().WillEvaluateScript(
|
||||
|
||||