mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c09e2aa6b8 | ||
|
|
44f02f61ff | ||
|
|
904fbbd598 | ||
|
|
36c88a46db | ||
|
|
9bf9c3606f | ||
|
|
d2841683c1 | ||
|
|
4aa36102d7 | ||
|
|
e1c17fd1e8 | ||
|
|
2871c1d392 | ||
|
|
0d3f57f3de | ||
|
|
6247116f8d | ||
|
|
5a1bda2277 | ||
|
|
cca4a7388d | ||
|
|
a8dfe3687c | ||
|
|
a4955396e1 | ||
|
|
bd193de24b | ||
|
|
ef66db337e | ||
|
|
00827a2da4 | ||
|
|
5f020c4685 | ||
|
|
e094b3939e | ||
|
|
339d44c723 | ||
|
|
ee2d3db030 | ||
|
|
139e238d07 | ||
|
|
a1550f5102 | ||
|
|
3dd04c2725 | ||
|
|
d32b8a64d0 | ||
|
|
425fe98c14 | ||
|
|
6b4b7df937 | ||
|
|
cc81658f40 | ||
|
|
9be5389e77 | ||
|
|
8264495aff | ||
|
|
ed9ec1a535 | ||
|
|
b8362fe96f | ||
|
|
4480c3545d | ||
|
|
de5d94bc49 | ||
|
|
4fe62718b9 | ||
|
|
1c9e1cd141 | ||
|
|
04e39e24e6 | ||
|
|
e0c8b9b168 | ||
|
|
77f3f5f2b2 | ||
|
|
a349e616d4 | ||
|
|
8c1b38d443 | ||
|
|
06278ba3b3 | ||
|
|
15b95fcd52 | ||
|
|
d574f99c9e | ||
|
|
cbc6959269 | ||
|
|
f4c7a3ff66 | ||
|
|
66ce2439cc | ||
|
|
de61f6c5e8 | ||
|
|
90f85f2bf4 | ||
|
|
60951cdca9 | ||
|
|
a3022df30f | ||
|
|
996fbfd6bc | ||
|
|
79d1e32281 | ||
|
|
1598b9116d | ||
|
|
68cd11118d | ||
|
|
1e2d5902a5 | ||
|
|
1bbe8c9610 | ||
|
|
4bfc2a83fc | ||
|
|
d80adf4aed | ||
|
|
b39f8e3e1c | ||
|
|
9c3f76f617 | ||
|
|
3925b82a07 | ||
|
|
e2b4cbfff3 | ||
|
|
29216464dc | ||
|
|
649c19c877 | ||
|
|
603bcb73e3 | ||
|
|
488af02eaa | ||
|
|
589e08af80 | ||
|
|
06521fad4c | ||
|
|
771cbce43b | ||
|
|
b5cfc5dd91 | ||
|
|
d6e6fcea86 | ||
|
|
cc64a5e8d9 | ||
|
|
2d12f059b4 | ||
|
|
f715930d49 | ||
|
|
477fe8566e | ||
|
|
a9837ed476 | ||
|
|
1f7269f482 | ||
|
|
2813b89824 | ||
|
|
a4560db9f0 | ||
|
|
adfc062313 | ||
|
|
8dc34b4b25 | ||
|
|
6cc5ad763d | ||
|
|
4c5637c687 | ||
|
|
0d71ed0f29 | ||
|
|
fc63000ee7 | ||
|
|
8d41dbe65f | ||
|
|
f3b90cc91c | ||
|
|
a78a8cd30c | ||
|
|
2511f78120 | ||
|
|
f711af1080 | ||
|
|
616026ce1c | ||
|
|
ada2c4e072 | ||
|
|
1a76e35971 | ||
|
|
3d475716f4 | ||
|
|
c7a033dd06 | ||
|
|
2ff6e7e042 | ||
|
|
3302c4dbd8 | ||
|
|
76ce7a7ca0 | ||
|
|
4237bcbc4c | ||
|
|
c81c505fea | ||
|
|
638dd2221a | ||
|
|
8ec629e871 | ||
|
|
ba487e914d | ||
|
|
4cd269a752 |
2
.github/actions/build-electron/action.yml
vendored
2
.github/actions/build-electron/action.yml
vendored
@@ -95,7 +95,7 @@ runs:
|
||||
# Upload build stats to Datadog
|
||||
if ($env:DD_API_KEY) {
|
||||
try {
|
||||
npx node electron\script\build-stats.mjs out\Default\siso.exe.INFO --upload-stats
|
||||
npx node electron\script\build-stats.mjs out\Default\siso.exe.INFO --upload-stats ; $LASTEXITCODE = 0
|
||||
} catch {
|
||||
Write-Host "Build stats upload failed, continuing..."
|
||||
}
|
||||
|
||||
2
.github/actions/checkout/action.yml
vendored
2
.github/actions/checkout/action.yml
vendored
@@ -43,7 +43,7 @@ runs:
|
||||
curl --unix-socket /var/run/sas/sas.sock --fail "http://foo/$CACHE_FILE?platform=${{ inputs.target-platform }}&getAccountName=true" > sas-token
|
||||
- name: Save SAS Key
|
||||
if: ${{ inputs.generate-sas-token == 'true' }}
|
||||
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: sas-token
|
||||
key: sas-key-${{ inputs.target-platform }}-${{ github.run_number }}-${{ github.run_attempt }}
|
||||
|
||||
@@ -15,7 +15,7 @@ runs:
|
||||
git config --global core.preloadindex true
|
||||
git config --global core.longpaths true
|
||||
fi
|
||||
export BUILD_TOOLS_SHA=4430e4a505e0f4fa2a41b707a10a36f780bbdd26
|
||||
export BUILD_TOOLS_SHA=a0cc95a1884a631559bcca0c948465b725d9295a
|
||||
npm i -g @electron/build-tools
|
||||
# Update depot_tools to ensure python
|
||||
e d update_depot_tools
|
||||
|
||||
@@ -7,7 +7,7 @@ runs:
|
||||
shell: bash
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(node src/electron/script/yarn.js config get cacheFolder)" >> $GITHUB_OUTPUT
|
||||
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
- uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
|
||||
122
.github/copilot-instructions.md
vendored
Normal file
122
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
# Copilot Instructions for Electron
|
||||
|
||||
## Build System
|
||||
|
||||
Electron uses `@electron/build-tools` (`e` CLI). Install with `npm i -g @electron/build-tools`.
|
||||
|
||||
```bash
|
||||
e sync # Fetch sources and apply patches
|
||||
e build # Build Electron (GN + Ninja)
|
||||
e build -k 999 # Build, continuing through errors
|
||||
e start # Run built Electron
|
||||
e start --version # Verify Electron launches
|
||||
e test # Run full test suite
|
||||
e debug # Run in debugger (lldb on macOS, gdb on Linux)
|
||||
```
|
||||
|
||||
### Linting
|
||||
|
||||
```bash
|
||||
npm run lint # Run all linters (JS, C++, Python, GN, docs)
|
||||
npm run lint:js # JavaScript/TypeScript only
|
||||
npm run lint:clang-format # C++ formatting only
|
||||
npm run lint:cpp # C++ linting only
|
||||
npm run lint:docs # Documentation only
|
||||
```
|
||||
|
||||
### Running a Single Test
|
||||
|
||||
```bash
|
||||
npm run test -- -g "pattern" # Run tests matching a regex pattern
|
||||
# Example: npm run test -- -g "ipc"
|
||||
```
|
||||
|
||||
### Running a Single Node.js Test
|
||||
|
||||
```bash
|
||||
node script/node-spec-runner.js parallel/test-crypto-keygen
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
Electron embeds Chromium (rendering) and Node.js (backend) to enable desktop apps with web technologies. The parent directory (`../`) is the Chromium source tree.
|
||||
|
||||
### Process Model
|
||||
|
||||
Electron has two primary process types, mirroring Chromium:
|
||||
|
||||
- **Main process** (`shell/browser/` + `lib/browser/`): Controls app lifecycle, creates windows, system APIs
|
||||
- **Renderer process** (`shell/renderer/` + `lib/renderer/`): Runs web content in BrowserWindows
|
||||
|
||||
### Native ↔ JavaScript Bridge
|
||||
|
||||
Each API is implemented as a C++/JS pair:
|
||||
|
||||
- C++ side: `shell/browser/api/electron_api_{name}.cc/.h` — uses `gin::Wrappable` and `ObjectTemplateBuilder`
|
||||
- JS side: `lib/browser/api/{name}.ts` — exports the module, registered in `lib/browser/api/module-list.ts`
|
||||
- Binding: `NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_{name}, Initialize)` in C++ and registered in `shell/common/node_bindings.cc`
|
||||
- Type declaration: `typings/internal-ambient.d.ts` maps `process._linkedBinding('electron_browser_{name}')`
|
||||
|
||||
### Patches System
|
||||
|
||||
Electron patches upstream dependencies (Chromium, Node.js, V8, etc.) rather than forking them. Patches live in `patches/` organized by target, with `patches/config.json` mapping directories to repos.
|
||||
|
||||
```text
|
||||
patches/{target}/*.patch → [e sync] → target repo commits
|
||||
← [e patches] ←
|
||||
```
|
||||
|
||||
Key rules:
|
||||
|
||||
- Fix existing patches rather than creating new ones
|
||||
- Preserve original authorship in TODO comments — never change `TODO(name)` assignees
|
||||
- Each patch commit message must explain why the patch exists
|
||||
- After modifying patches, run `e patches {target}` to export
|
||||
|
||||
When working on the `roller/chromium/main` branch for Chromium upgrades, use `e sync --3` for 3-way merge conflict resolution.
|
||||
|
||||
## Conventions
|
||||
|
||||
### File Naming
|
||||
|
||||
- JS/TS files: kebab-case (`file-name.ts`)
|
||||
- C++ files: snake_case with `electron_api_` prefix (`electron_api_safe_storage.cc`)
|
||||
- Test files: `api-{module-name}-spec.ts` in `spec/`
|
||||
- Source file lists are maintained in `filenames.gni` (with platform-specific sections)
|
||||
|
||||
### JavaScript/TypeScript
|
||||
|
||||
- Semicolons required (`"semi": ["error", "always"]`)
|
||||
- `const` and `let` only (no `var`)
|
||||
- Arrow functions preferred
|
||||
- Import order enforced: `@electron/internal` → `@electron` → `electron` → external → builtin → relative
|
||||
- API naming: `PascalCase` for classes (`BrowserWindow`), `camelCase` for module APIs (`globalShortcut`)
|
||||
- Prefer getters/setters over jQuery-style `.text([text])` patterns
|
||||
|
||||
### C++
|
||||
|
||||
- Follows Chromium coding style, enforced by `clang-format` and `clang-tidy`
|
||||
- Uses Chromium abstractions (`base::`, `content::`, etc.)
|
||||
- Header guards: `#ifndef ELECTRON_SHELL_BROWSER_API_ELECTRON_API_{NAME}_H_`
|
||||
- Platform-specific files: `_mac.mm`, `_win.cc`, `_linux.cc`
|
||||
|
||||
### Testing
|
||||
|
||||
- Framework: Mocha + Chai + Sinon
|
||||
- Test helpers in `spec/lib/` (e.g., `spec-helpers.ts`, `window-helpers.ts`)
|
||||
- Use `defer()` from spec-helpers for cleanup, `closeAllWindows()` for window teardown
|
||||
- Tests import from `electron/main` or `electron/renderer`
|
||||
|
||||
### Documentation
|
||||
|
||||
- API docs in `docs/api/` as Markdown, parsed by `@electron/docs-parser` to generate `electron.d.ts`
|
||||
- API history tracked via YAML blocks in HTML comments within doc files
|
||||
- Docs must pass `npm run lint:docs`
|
||||
|
||||
### Build Configuration
|
||||
|
||||
- `BUILD.gn`: Main GN build config
|
||||
- `buildflags/buildflags.gni`: Feature flags (PDF viewer, extensions, spellchecker)
|
||||
- `build/args/`: Build argument profiles (`testing.gn`, `release.gn`, `all.gn`)
|
||||
- `DEPS`: Dependency versions and checkout paths
|
||||
- `chromium_src/`: Chromium source file overrides (compiled instead of originals)
|
||||
4
.github/workflows/pipeline-electron-lint.yml
vendored
4
.github/workflows/pipeline-electron-lint.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
|
||||
gn_version="$(curl -sL "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/DEPS?format=TEXT" | base64 -d | 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/
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
chromium_revision="$(grep -A1 chromium_version src/electron/DEPS | tr -d '\n' | cut -d\' -f4)"
|
||||
|
||||
mkdir -p src/buildtools
|
||||
curl -sL "https://chromium.googlesource.com/chromium/src/+/${chromium_revision}/buildtools/DEPS?format=TEXT" | base64 -d > 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
|
||||
|
||||
@@ -191,15 +191,25 @@ jobs:
|
||||
run: |
|
||||
cd src/out/Default
|
||||
unzip -:o dist.zip
|
||||
#- name: Import & Trust Self-Signed Codesigning Cert on MacOS
|
||||
# if: ${{ inputs.target-platform == 'macos' && inputs.target-arch == 'x64' }}
|
||||
# run: |
|
||||
# sudo security authorizationdb write com.apple.trust-settings.admin allow
|
||||
# cd src/electron
|
||||
# ./script/codesign/generate-identity.sh
|
||||
- name: Import & Trust Self-Signed Codesigning Cert on MacOS
|
||||
if: ${{ inputs.target-platform == 'macos' }}
|
||||
run: |
|
||||
cd src/electron
|
||||
./script/codesign/generate-identity.sh
|
||||
# Only sign on x64 — arm64 builds are already ad-hoc signed, and re-signing
|
||||
# with an untrusted cert breaks macOS system integrations (e.g. dock bounce).
|
||||
# Autoupdater tests sign their own fixture copies via signApp().
|
||||
- name: Sign Electron.app for macOS tests
|
||||
if: ${{ inputs.target-platform == 'macos' && inputs.target-arch == 'x64' }}
|
||||
run: |
|
||||
identity=$(src/electron/script/codesign/get-trusted-identity.sh)
|
||||
if [ -n "$identity" ]; then
|
||||
codesign -s "$identity" --deep --force src/out/Default/Electron.app
|
||||
fi
|
||||
|
||||
- name: Run Electron Tests
|
||||
shell: bash
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
MOCHA_MULTI_REPORTERS: mocha-junit-reporter, tap
|
||||
@@ -250,6 +260,19 @@ jobs:
|
||||
|
||||
fi
|
||||
fi
|
||||
- name: Take screenshot on timeout or cancellation
|
||||
if: ${{ inputs.target-platform != 'linux' && (cancelled() || failure()) }}
|
||||
shell: bash
|
||||
run: |
|
||||
screenshot_dir="src/electron/spec/artifacts"
|
||||
mkdir -p "$screenshot_dir"
|
||||
screenshot_file="$screenshot_dir/screenshot-timeout-$(date +%Y%m%d%H%M%S).png"
|
||||
if [ "${{ inputs.target-platform }}" = "macos" ]; then
|
||||
screencapture -x "$screenshot_file" || true
|
||||
elif [ "${{ inputs.target-platform }}" = "win" ]; then
|
||||
powershell -command "Add-Type -AssemblyName System.Windows.Forms; \$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds; \$bitmap = New-Object System.Drawing.Bitmap(\$screen.Width, \$screen.Height); \$graphics = [System.Drawing.Graphics]::FromImage(\$bitmap); \$graphics.CopyFromScreen(\$screen.Location, [System.Drawing.Point]::Empty, \$screen.Size); \$bitmap.Save('$screenshot_file')" || true
|
||||
fi
|
||||
|
||||
- name: Upload Test results to Datadog
|
||||
env:
|
||||
DD_ENV: ci
|
||||
@@ -265,7 +288,7 @@ jobs:
|
||||
fi
|
||||
if: always() && !cancelled()
|
||||
- name: Upload Test Artifacts
|
||||
if: always() && !cancelled()
|
||||
if: always()
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
|
||||
with:
|
||||
name: test_artifacts_${{ env.ARTIFACT_KEY }}_${{ matrix.shard }}
|
||||
|
||||
2
.github/workflows/update-website-docs.yml
vendored
2
.github/workflows/update-website-docs.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
echo "isLatestRelease=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Trigger website docs update
|
||||
if: ${{ steps.check-if-latest-release.outputs.isLatestRelease }}
|
||||
if: ${{ steps.check-if-latest-release.outputs.isLatestRelease == 'true' }}
|
||||
env:
|
||||
GH_REPO: electron/website
|
||||
GH_TOKEN: ${{ fromJSON(steps.secret-service.outputs.secrets).WEBSITE_DOCS_UPDATER_APP_TOKEN }}
|
||||
|
||||
@@ -9,4 +9,8 @@ npmMinimalAgeGate: 10080
|
||||
npmPreapprovedPackages:
|
||||
- "@electron/*"
|
||||
|
||||
httpProxy: "${HTTP_PROXY:-}"
|
||||
|
||||
httpsProxy: "${HTTPS_PROXY:-}"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.12.0.cjs
|
||||
|
||||
4
DEPS
4
DEPS
@@ -2,9 +2,9 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'144.0.7559.173',
|
||||
'144.0.7559.236',
|
||||
'node_version':
|
||||
'v24.13.0',
|
||||
'v24.14.0',
|
||||
'nan_version':
|
||||
'675cefebca42410733da8a454c8d9391fcebfbc2',
|
||||
'squirrel.mac_version':
|
||||
|
||||
@@ -255,7 +255,7 @@ async function startRepl () {
|
||||
if (option.file && !option.webdriver) {
|
||||
const file = option.file;
|
||||
// eslint-disable-next-line n/no-deprecated-api
|
||||
const protocol = url.parse(file).protocol;
|
||||
const protocol = URL.canParse(file) ? new URL(file).protocol : null;
|
||||
const extension = path.extname(file);
|
||||
if (protocol === 'http:' || protocol === 'https:' || protocol === 'file:' || protocol === 'chrome:') {
|
||||
await loadApplicationByURL(file);
|
||||
|
||||
@@ -250,7 +250,9 @@ Returns:
|
||||
|
||||
Emitted when the user clicks the native macOS new tab button. The new
|
||||
tab button is only visible if the current `BrowserWindow` has a
|
||||
`tabbingIdentifier`
|
||||
`tabbingIdentifier`.
|
||||
|
||||
You must create a window in this handler in order for macOS tabbing to work as expected.
|
||||
|
||||
### Event: 'browser-window-blur'
|
||||
|
||||
@@ -1121,6 +1123,19 @@ Updates the current activity if its type matches `type`, merging the entries fro
|
||||
|
||||
Changes the [Application User Model ID][app-user-model-id] to `id`.
|
||||
|
||||
### `app.setToastActivatorCLSID(id)` _Windows_
|
||||
|
||||
* `id` string
|
||||
|
||||
Changes the [Toast Activator CLSID][toast-activator-clsid] to `id`. If one is not set via this method, it will be randomly generated for the app.
|
||||
|
||||
* The value must be a valid GUID/CLSID in one of the following forms:
|
||||
* Canonical brace-wrapped: `{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}` (preferred)
|
||||
* Canonical without braces: `XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX` (braces will be added automatically)
|
||||
* Hex digits are case-insensitive.
|
||||
|
||||
This method should be called early (before showing notifications) so the value is baked into the registration/shortcut. Supplying an empty string or an unparsable value throws and leaves the existing (or generated) CLSID unchanged. If this method is never called, a random CLSID is generated once per run and exposed via `app.toastActivatorCLSID`.
|
||||
|
||||
### `app.setActivationPolicy(policy)` _macOS_
|
||||
|
||||
* `policy` string - Can be 'regular', 'accessory', or 'prohibited'.
|
||||
@@ -1316,7 +1331,7 @@ Returns `boolean` - Whether the current desktop environment is Unity launcher.
|
||||
### `app.getLoginItemSettings([options])` _macOS_ _Windows_
|
||||
|
||||
* `options` Object (optional)
|
||||
* `type` string (optional) _macOS_ - Can be one of `mainAppService`, `agentService`, `daemonService`, or `loginItemService`. Defaults to `mainAppService`. Only available on macOS 13 and up. See [app.setLoginItemSettings](app.md#appsetloginitemsettingssettings-macos-windows) for more information about each type.
|
||||
* `type` string (optional) _macOS_ - Can be `mainAppService`, `agentService`, `daemonService`, or `loginItemService`. Defaults to `mainAppService`. Only available on macOS 13 and up. See [app.setLoginItemSettings](app.md#appsetloginitemsettingssettings-macos-windows) for more information about each type.
|
||||
* `serviceName` string (optional) _macOS_ - The name of the service. Required if `type` is non-default. Only available on macOS 13 and up.
|
||||
* `path` string (optional) _Windows_ - The executable path to compare against. Defaults to `process.execPath`.
|
||||
* `args` string[] (optional) _Windows_ - The command-line arguments to compare against. Defaults to an empty array.
|
||||
@@ -1331,13 +1346,13 @@ Returns `Object`:
|
||||
* `wasOpenedAtLogin` boolean _macOS_ - `true` if the app was opened at login automatically.
|
||||
* `wasOpenedAsHidden` boolean _macOS_ _Deprecated_ - `true` if the app was opened as a hidden login item. This indicates that the app should not open any windows at startup. This setting is not available on [MAS builds][mas-builds] or on macOS 13 and up.
|
||||
* `restoreState` boolean _macOS_ _Deprecated_ - `true` if the app was opened as a login item that should restore the state from the previous session. This indicates that the app should restore the windows that were open the last time the app was closed. This setting is not available on [MAS builds][mas-builds] or on macOS 13 and up.
|
||||
* `status` string _macOS_ - can be one of `not-registered`, `enabled`, `requires-approval`, or `not-found`.
|
||||
* `status` string _macOS_ - can be `not-registered`, `enabled`, `requires-approval`, or `not-found`.
|
||||
* `executableWillLaunchAtLogin` boolean _Windows_ - `true` if app is set to open at login and its run key is not deactivated. This differs from `openAtLogin` as it ignores the `args` option, this property will be true if the given executable would be launched at login with **any** arguments.
|
||||
* `launchItems` Object[] _Windows_
|
||||
* `name` string _Windows_ - name value of a registry entry.
|
||||
* `path` string _Windows_ - The executable to an app that corresponds to a registry entry.
|
||||
* `args` string[] _Windows_ - the command-line arguments to pass to the executable.
|
||||
* `scope` string _Windows_ - one of `user` or `machine`. Indicates whether the registry entry is under `HKEY_CURRENT USER` or `HKEY_LOCAL_MACHINE`.
|
||||
* `scope` string _Windows_ - can be `user` or `machine`. Indicates whether the registry entry is under `HKEY_CURRENT USER` or `HKEY_LOCAL_MACHINE`.
|
||||
* `enabled` boolean _Windows_ - `true` if the app registry key is startup approved and therefore shows as `enabled` in Task Manager and Windows settings.
|
||||
|
||||
### `app.setLoginItemSettings(settings)` _macOS_ _Windows_
|
||||
@@ -1703,8 +1718,13 @@ platforms) that allows you to perform actions on your app icon in the user's doc
|
||||
|
||||
A `boolean` property that returns `true` if the app is packaged, `false` otherwise. For many apps, this property can be used to distinguish development and production environments.
|
||||
|
||||
### `app.toastActivatorCLSID` _Windows_ _Readonly_
|
||||
|
||||
A `string` property that returns the app's [Toast Activator CLSID][toast-activator-clsid].
|
||||
|
||||
[tasks]:https://learn.microsoft.com/en-us/windows/win32/shell/taskbar-extensions#tasks
|
||||
[app-user-model-id]: https://learn.microsoft.com/en-us/windows/win32/shell/appids
|
||||
[toast-activator-clsid]: https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-toastactivatorclsid
|
||||
[electron-forge]: https://www.electronforge.io/
|
||||
[electron-packager]: https://github.com/electron/packager
|
||||
[CFBundleURLTypes]: https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-102207-TPXREF115
|
||||
|
||||
@@ -351,7 +351,11 @@ Emitted when the window has closed a sheet.
|
||||
|
||||
#### Event: 'new-window-for-tab' _macOS_
|
||||
|
||||
Emitted when the native new tab button is clicked.
|
||||
Emitted when the user clicks the native macOS new tab button. The new
|
||||
tab button is only visible if the current `BrowserWindow` has a
|
||||
`tabbingIdentifier`.
|
||||
|
||||
You must create a window in this handler in order for macOS tabbing to work as expected.
|
||||
|
||||
#### Event: 'system-context-menu' _Windows_ _Linux_
|
||||
|
||||
|
||||
@@ -431,7 +431,11 @@ Emitted when the window has closed a sheet.
|
||||
|
||||
#### Event: 'new-window-for-tab' _macOS_
|
||||
|
||||
Emitted when the native new tab button is clicked.
|
||||
Emitted when the user clicks the native macOS new tab button. The new
|
||||
tab button is only visible if the current `BrowserWindow` has a
|
||||
`tabbingIdentifier`.
|
||||
|
||||
You must create a window in this handler in order for macOS tabbing to work as expected.
|
||||
|
||||
#### Event: 'system-context-menu' _Windows_ _Linux_
|
||||
|
||||
|
||||
@@ -350,6 +350,11 @@ Affects the default output directory of [v8.setHeapSnapshotNearHeapLimit](https:
|
||||
|
||||
Disable exposition of [Navigator API][] on the global scope from Node.js.
|
||||
|
||||
### `--experimental-transform-types`
|
||||
|
||||
Enables the [transformation](https://nodejs.org/api/typescript.html#type-stripping)
|
||||
of TypeScript-only syntax into JavaScript code.
|
||||
|
||||
## Chromium Flags
|
||||
|
||||
There isn't a documented list of all Chromium switches, but there are a few ways to find them.
|
||||
@@ -366,6 +371,13 @@ Keep in mind that standalone switches can sometimes be split into individual fea
|
||||
|
||||
Finally, you'll need to ensure that the version of Chromium in Electron matches the version of the browser you're using to cross-reference the switches.
|
||||
|
||||
### Chromium features relevant to Electron apps
|
||||
|
||||
* `AlwaysLogLOAFURL`: enables script attribution for
|
||||
[`long-animation-frame`](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Long_animation_frame_timing)
|
||||
`PerformanceObserver` events for non-http(s), non-data, non-blob URLs (such as `file:` or custom
|
||||
protocol URLs).
|
||||
|
||||
[app]: app.md
|
||||
[append-switch]: command-line.md#commandlineappendswitchswitch-value
|
||||
[debugging-main-process]: ../tutorial/debugging-main-process.md
|
||||
|
||||
@@ -94,18 +94,56 @@ The `desktopCapturer` module has the following methods:
|
||||
Returns `Promise<DesktopCapturerSource[]>` - Resolves with an array of [`DesktopCapturerSource`](structures/desktop-capturer-source.md) objects, each `DesktopCapturerSource` represents a screen or an individual window that can be captured.
|
||||
|
||||
> [!NOTE]
|
||||
> Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher,
|
||||
> which can detected by [`systemPreferences.getMediaAccessStatus`][].
|
||||
<!-- markdownlint-disable-next-line MD032 -->
|
||||
> * Capturing audio requires `NSAudioCaptureUsageDescription` Info.plist key on macOS 14.2 Sonoma and higher - [read more](#macos-versions-142-or-higher).
|
||||
> * Capturing the screen contents requires user consent on macOS 10.15 Catalina or higher, which can detected by [`systemPreferences.getMediaAccessStatus`][].
|
||||
|
||||
[`navigator.mediaDevices.getUserMedia`]: https://developer.mozilla.org/en/docs/Web/API/MediaDevices/getUserMedia
|
||||
[`systemPreferences.getMediaAccessStatus`]: system-preferences.md#systempreferencesgetmediaaccessstatusmediatype-windows-macos
|
||||
|
||||
## Caveats
|
||||
|
||||
### Linux
|
||||
|
||||
`desktopCapturer.getSources(options)` only returns a single source on Linux when using Pipewire.
|
||||
|
||||
PipeWire supports a single capture for both screens and windows. If you request the window and screen type, the selected source will be returned as a window capture.
|
||||
|
||||
`navigator.mediaDevices.getUserMedia` does not work on macOS for audio capture due to a fundamental limitation whereby apps that want to access the system's audio require a [signed kernel extension](https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/KernelExtensions/KernelExtensions.html). Chromium, and by extension Electron, does not provide this.
|
||||
### macOS versions 14.2 or higher
|
||||
|
||||
It is possible to circumvent this limitation by capturing system audio with another macOS app like Soundflower and passing it through a virtual audio input device. This virtual device can then be queried with `navigator.mediaDevices.getUserMedia`.
|
||||
`NSAudioCaptureUsageDescription` Info.plist key must be added in order for audio to be captured by
|
||||
`desktopCapturer`. If instead you are running Electron from another program like a terminal or IDE
|
||||
then that parent program must contain the Info.plist key.
|
||||
|
||||
This is in order to facillitate use of Apple's new [CoreAudio Tap API](https://developer.apple.com/documentation/CoreAudio/capturing-system-audio-with-core-audio-taps#Configure-the-sample-code-project) by Chromium.
|
||||
|
||||
> [!WARNING]
|
||||
> Failure of `desktopCapturer` to start an audio stream due to `NSAudioCaptureUsageDescription`
|
||||
> permission not present will still create a dead audio stream however no warnings or errors are
|
||||
> displayed.
|
||||
|
||||
As of Electron `v39.0.0-beta.4`, Chromium [made Apple's new `CoreAudio Tap API` the default](https://source.chromium.org/chromium/chromium/src/+/ad17e8f8b93d5f34891b06085d373a668918255e)
|
||||
for desktop audio capture. There is no fallback to the older `Screen & System Audio Recording`
|
||||
permissions system even if [CoreAudio Tap API](https://developer.apple.com/documentation/CoreAudio/capturing-system-audio-with-core-audio-taps) stream creation fails.
|
||||
|
||||
If you need to continue using `Screen & System Audio Recording` permissions for `desktopCapturer`
|
||||
on macOS versions 14.2 and later, you can apply a Chromium feature flag to force use of that older
|
||||
permissions system:
|
||||
|
||||
```js
|
||||
// main.js (right beneath your require/import statments)
|
||||
app.commandLine.appendSwitch('disable-features', 'MacCatapLoopbackAudioForScreenShare')
|
||||
```
|
||||
|
||||
### macOS versions 12.7.6 or lower
|
||||
|
||||
`navigator.mediaDevices.getUserMedia` does not work on macOS versions 12.7.6 and prior for audio
|
||||
capture due to a fundamental limitation whereby apps that want to access the system's audio require
|
||||
a [signed kernel extension](https://developer.apple.com/library/archive/documentation/Security/Conceptual/System_Integrity_Protection_Guide/KernelExtensions/KernelExtensions.html).
|
||||
Chromium, and by extension Electron, does not provide this. Only in macOS 13 and onwards does Apple
|
||||
provide APIs to capture desktop audio without the need for a signed kernel extension.
|
||||
|
||||
It is possible to circumvent this limitation by capturing system audio with another macOS app like
|
||||
[BlackHole](https://existential.audio/blackhole/) or [Soundflower](https://rogueamoeba.com/freebies/soundflower/)
|
||||
and passing it through a virtual audio input device. This virtual device can then be queried
|
||||
with `navigator.mediaDevices.getUserMedia`.
|
||||
|
||||
@@ -73,13 +73,16 @@ The following properties are available on instances of `MenuItem`:
|
||||
|
||||
#### `menuItem.id`
|
||||
|
||||
A `string` indicating the item's unique id. This property can be
|
||||
dynamically changed.
|
||||
A `string` indicating the item's unique id.
|
||||
|
||||
This property can be dynamically changed.
|
||||
|
||||
#### `menuItem.label`
|
||||
|
||||
A `string` indicating the item's visible label.
|
||||
|
||||
This property can be dynamically changed.
|
||||
|
||||
#### `menuItem.click`
|
||||
|
||||
A `Function` that is fired when the MenuItem receives a click event.
|
||||
@@ -118,31 +121,37 @@ An `Accelerator | null` indicating the item's [user-assigned accelerator](https:
|
||||
|
||||
#### `menuItem.icon`
|
||||
|
||||
A `NativeImage | string` (optional) indicating the
|
||||
item's icon, if set.
|
||||
A `NativeImage | string` (optional) indicating the item's icon, if set.
|
||||
|
||||
This property can be dynamically changed.
|
||||
|
||||
#### `menuItem.sublabel`
|
||||
|
||||
A `string` indicating the item's sublabel.
|
||||
|
||||
This property can be dynamically changed.
|
||||
|
||||
#### `menuItem.toolTip` _macOS_
|
||||
|
||||
A `string` indicating the item's hover text.
|
||||
|
||||
#### `menuItem.enabled`
|
||||
|
||||
A `boolean` indicating whether the item is enabled. This property can be
|
||||
dynamically changed.
|
||||
A `boolean` indicating whether the item is enabled.
|
||||
|
||||
This property can be dynamically changed.
|
||||
|
||||
#### `menuItem.visible`
|
||||
|
||||
A `boolean` indicating whether the item is visible. This property can be
|
||||
dynamically changed.
|
||||
A `boolean` indicating whether the item is visible.
|
||||
|
||||
This property can be dynamically changed.
|
||||
|
||||
#### `menuItem.checked`
|
||||
|
||||
A `boolean` indicating whether the item is checked. This property can be
|
||||
dynamically changed.
|
||||
A `boolean` indicating whether the item is checked.
|
||||
|
||||
This property can be dynamically changed.
|
||||
|
||||
A `checkbox` menu item will toggle the `checked` property on and off when
|
||||
selected.
|
||||
|
||||
@@ -67,6 +67,22 @@ Emitted when the notification is shown to the user. Note that this event can be
|
||||
multiple times as a notification can be shown multiple times through the
|
||||
`show()` method.
|
||||
|
||||
```js
|
||||
const { Notification, app } = require('electron')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const n = new Notification({
|
||||
title: 'Title!',
|
||||
subtitle: 'Subtitle!',
|
||||
body: 'Body!'
|
||||
})
|
||||
|
||||
n.on('show', () => console.log('Notification shown!'))
|
||||
|
||||
n.show()
|
||||
})
|
||||
```
|
||||
|
||||
#### Event: 'click'
|
||||
|
||||
Returns:
|
||||
@@ -75,11 +91,28 @@ Returns:
|
||||
|
||||
Emitted when the notification is clicked by the user.
|
||||
|
||||
```js
|
||||
const { Notification, app } = require('electron')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const n = new Notification({
|
||||
title: 'Title!',
|
||||
subtitle: 'Subtitle!',
|
||||
body: 'Body!'
|
||||
})
|
||||
|
||||
n.on('click', () => console.log('Notification clicked!'))
|
||||
|
||||
n.show()
|
||||
})
|
||||
```
|
||||
|
||||
#### Event: 'close'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `details` Event\<\>
|
||||
* `reason` _Windows_ string (optional) - The reason the notification was closed. This can be 'userCanceled', 'applicationHidden', or 'timedOut'.
|
||||
|
||||
Emitted when the notification is closed by manual intervention from the user.
|
||||
|
||||
@@ -88,21 +121,85 @@ is closed.
|
||||
|
||||
On Windows, the `close` event can be emitted in one of three ways: programmatic dismissal with `notification.close()`, by the user closing the notification, or via system timeout. If a notification is in the Action Center after the initial `close` event is emitted, a call to `notification.close()` will remove the notification from the action center but the `close` event will not be emitted again.
|
||||
|
||||
#### Event: 'reply' _macOS_
|
||||
```js
|
||||
const { Notification, app } = require('electron')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const n = new Notification({
|
||||
title: 'Title!',
|
||||
subtitle: 'Subtitle!',
|
||||
body: 'Body!'
|
||||
})
|
||||
|
||||
n.on('close', () => console.log('Notification closed!'))
|
||||
|
||||
n.show()
|
||||
})
|
||||
```
|
||||
|
||||
#### Event: 'reply' _macOS_ _Windows_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `reply` string - The string the user entered into the inline reply field.
|
||||
* `details` Event\<\>
|
||||
* `reply` string - The string the user entered into the inline reply field.
|
||||
* `reply` string _Deprecated_
|
||||
|
||||
Emitted when the user clicks the "Reply" button on a notification with `hasReply: true`.
|
||||
|
||||
#### Event: 'action' _macOS_
|
||||
```js
|
||||
const { Notification, app } = require('electron')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const n = new Notification({
|
||||
title: 'Send a Message',
|
||||
body: 'Body Text',
|
||||
hasReply: true,
|
||||
replyPlaceholder: 'Message text...'
|
||||
})
|
||||
|
||||
n.on('reply', (e, reply) => console.log(`User replied: ${reply}`))
|
||||
n.on('click', () => console.log('Notification clicked'))
|
||||
|
||||
n.show()
|
||||
})
|
||||
```
|
||||
|
||||
#### Event: 'action' _macOS_ _Windows_
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `index` number - The index of the action that was activated.
|
||||
* `details` Event\<\>
|
||||
* `actionIndex` number - The index of the action that was activated.
|
||||
* `selectionIndex` number _Windows_ - The index of the selected item, if one was chosen. -1 if none was chosen.
|
||||
* `actionIndex` number _Deprecated_
|
||||
* `selectionIndex` number _Windows_ _Deprecated_
|
||||
|
||||
```js
|
||||
const { Notification, app } = require('electron')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const items = ['One', 'Two', 'Three']
|
||||
const n = new Notification({
|
||||
title: 'Choose an Action!',
|
||||
actions: [
|
||||
{ type: 'button', text: 'Action 1' },
|
||||
{ type: 'button', text: 'Action 2' },
|
||||
{ type: 'selection', text: 'Apply', items }
|
||||
]
|
||||
})
|
||||
|
||||
n.on('click', () => console.log('Notification clicked'))
|
||||
n.on('action', (e) => {
|
||||
console.log(`User triggered action at index: ${e.actionIndex}`)
|
||||
if (e.selectionIndex > -1) {
|
||||
console.log(`User chose selection item '${items[e.selectionIndex]}'`)
|
||||
}
|
||||
})
|
||||
|
||||
n.show()
|
||||
})
|
||||
```
|
||||
|
||||
#### Event: 'failed' _Windows_
|
||||
|
||||
@@ -113,6 +210,22 @@ Returns:
|
||||
|
||||
Emitted when an error is encountered while creating and showing the native notification.
|
||||
|
||||
```js
|
||||
const { Notification, app } = require('electron')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const n = new Notification({
|
||||
title: 'Bad Action'
|
||||
})
|
||||
|
||||
n.on('failed', (e, err) => {
|
||||
console.log('Notification failed: ', err)
|
||||
})
|
||||
|
||||
n.show()
|
||||
})
|
||||
```
|
||||
|
||||
### Instance Methods
|
||||
|
||||
Objects created with the `new Notification()` constructor have the following instance methods:
|
||||
@@ -126,12 +239,42 @@ call this method before the OS will display it.
|
||||
If the notification has been shown before, this method will dismiss the previously
|
||||
shown notification and create a new one with identical properties.
|
||||
|
||||
```js
|
||||
const { Notification, app } = require('electron')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const n = new Notification({
|
||||
title: 'Title!',
|
||||
subtitle: 'Subtitle!',
|
||||
body: 'Body!'
|
||||
})
|
||||
|
||||
n.show()
|
||||
})
|
||||
```
|
||||
|
||||
#### `notification.close()`
|
||||
|
||||
Dismisses the notification.
|
||||
|
||||
On Windows, calling `notification.close()` while the notification is visible on screen will dismiss the notification and remove it from the Action Center. If `notification.close()` is called after the notification is no longer visible on screen, calling `notification.close()` will try remove it from the Action Center.
|
||||
|
||||
```js
|
||||
const { Notification, app } = require('electron')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const n = new Notification({
|
||||
title: 'Title!',
|
||||
subtitle: 'Subtitle!',
|
||||
body: 'Body!'
|
||||
})
|
||||
|
||||
n.show()
|
||||
|
||||
setTimeout(() => n.close(), 5000)
|
||||
})
|
||||
```
|
||||
|
||||
### Instance Properties
|
||||
|
||||
#### `notification.title`
|
||||
|
||||
@@ -110,6 +110,8 @@ Returns [`Point`](structures/point.md)
|
||||
|
||||
The current absolute position of the mouse pointer.
|
||||
|
||||
Not supported on Wayland (Linux).
|
||||
|
||||
> [!NOTE]
|
||||
> The return value is a DIP point, not a screen physical point.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# sharedTexture
|
||||
|
||||
> Import shared textures into Electron and converts platform specific handles into [`VideoFrame`](https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame). Supports all Web rendering systems, and can be transferred across Electron processes. Read [here](https://github.com/electron/electron/blob/main/shell/common/api/shared_texture/README.md) for more information.
|
||||
> Import shared textures into Electron and converts platform specific handles into [`VideoFrame`](https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame). Supports all Web rendering systems, and can be transferred across Electron processes. Read [here](../../shell/common/api/shared_texture/README.md) for more information.
|
||||
|
||||
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# NotificationAction Object
|
||||
|
||||
* `type` string - The type of action, can be `button`.
|
||||
* `type` string - The type of action, can be `button` or `selection`. `selection` is only supported on Windows.
|
||||
* `text` string (optional) - The label for the given action.
|
||||
* `items` string[] (optional) _Windows_ - The list of items for the `selection` action `type`.
|
||||
|
||||
## Platform / Action Support
|
||||
|
||||
| Action Type | Platform Support | Usage of `text` | Default `text` | Limitations |
|
||||
|-------------|------------------|-----------------|----------------|-------------|
|
||||
| `button` | macOS | Used as the label for the button | "Show" (or a localized string by system default if first of such `button`, otherwise empty) | Only the first one is used. If multiple are provided, those beyond the first will be listed as additional actions (displayed when mouse active over the action button). Any such action also is incompatible with `hasReply` and will be ignored if `hasReply` is `true`. |
|
||||
| `button` | macOS, Windows | Used as the label for the button | "Show" on macOS (localized) if first `button`, otherwise empty; Windows uses provided `text` | macOS: Only the first one is used as primary; others shown as additional actions (hover). Incompatible with `hasReply` (beyond first ignored). |
|
||||
| `selection` | Windows | Used as the label for the submit button for the selection menu | "Select" | Requires an `items` array property specifying option labels. Emits the `action` event with `(index, selectedIndex)` where `selectedIndex` is the chosen option (>= 0). Ignored on platforms that do not support selection actions. |
|
||||
|
||||
### Button support on macOS
|
||||
|
||||
@@ -18,3 +20,34 @@ following criteria.
|
||||
* App has its `NSUserNotificationAlertStyle` set to `alert` in the `Info.plist`.
|
||||
|
||||
If either of these requirements are not met the button won't appear.
|
||||
|
||||
### Selection support on Windows
|
||||
|
||||
To add a selection (combo box) style action, include an action with `type: 'selection'`, a `text` label for the submit button, and an `items` array of strings:
|
||||
|
||||
```js
|
||||
const { Notification, app } = require('electron')
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const items = ['One', 'Two', 'Three']
|
||||
const n = new Notification({
|
||||
title: 'Choose an option',
|
||||
actions: [{
|
||||
type: 'selection',
|
||||
text: 'Apply',
|
||||
items
|
||||
}]
|
||||
})
|
||||
|
||||
n.on('action', (e) => {
|
||||
console.log(`User triggered action at index: ${e.actionIndex}`)
|
||||
if (e.selectionIndex > 0) {
|
||||
console.log(`User chose selection item '${items[e.selectionIndex]}'`)
|
||||
}
|
||||
})
|
||||
|
||||
n.show()
|
||||
})
|
||||
```
|
||||
|
||||
When the user activates the selection action, the notification's `action` event will be emitted with two parameters: `actionIndex` (the action's index in the `actions` array) and `selectedIndex` (the zero-based index of the chosen item, or `-1` if unavailable). On non-Windows platforms selection actions are ignored.
|
||||
|
||||
@@ -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.
|
||||
* `deviceScaleFactor` number (optional) _Experimental_ - The device scale factor of the offscreen rendering output. If not set, will use primary display's scale factor as default.
|
||||
* `contextIsolation` boolean (optional) - Whether to run Electron APIs and
|
||||
the specified `preload` script in a separate JavaScript context. Defaults
|
||||
to `true`. The context that the `preload` script runs in will only have
|
||||
@@ -156,6 +157,8 @@
|
||||
`WebContents` when the preferred size changes. Default is `false`.
|
||||
* `transparent` boolean (optional) - Whether to enable background transparency for the guest page. Default is `true`. **Note:** The guest page's text and background colors are derived from the [color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme) of its root element. When transparency is enabled, the text color will still change accordingly but the background will remain transparent.
|
||||
* `enableDeprecatedPaste` boolean (optional) _Deprecated_ - Whether to enable the `paste` [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand). Default is `false`.
|
||||
* `focusOnNavigation` boolean (optional) - Whether to focus the WebContents
|
||||
when navigating. Default is `true`.
|
||||
|
||||
[chrome-content-scripts]: https://developer.chrome.com/extensions/content_scripts#execution-environment
|
||||
[runtime-enabled-features]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/runtime_enabled_features.json5
|
||||
|
||||
@@ -933,7 +933,7 @@ copying data between CPU and GPU memory, with Chromium's hardware acceleration s
|
||||
Only a limited number of textures can exist at the same time, so it's important that you call `texture.release()` as soon as you're done with the texture.
|
||||
By managing the texture lifecycle by yourself, you can safely pass the `texture.textureInfo` to other processes through IPC.
|
||||
|
||||
More details can be found in the [offscreen rendering tutorial](../tutorial/offscreen-rendering.md). To learn about how to handle the texture in native code, refer to [offscreen rendering's code documentation.](https://github.com/electron/electron/blob/main/shell/browser/osr/README.md).
|
||||
More details can be found in the [offscreen rendering tutorial](../tutorial/offscreen-rendering.md). To learn about how to handle the texture in native code, refer to [offscreen rendering's code documentation.](../../shell/browser/osr/README.md).
|
||||
|
||||
```js
|
||||
const { BrowserWindow } = require('electron')
|
||||
|
||||
@@ -50,6 +50,22 @@ webContents.setWindowOpenHandler((details) => {
|
||||
})
|
||||
```
|
||||
|
||||
### Behavior Changed: `NSAudioCaptureUsageDescription` should be included in your app's Info.plist file to use `desktopCapturer` (🍏 macOS ≥14.2)
|
||||
|
||||
Per [Chromium update](https://source.chromium.org/chromium/chromium/src/+/ad17e8f8b93d5f34891b06085d373a668918255e) which enables Apple's newer [CoreAudio Tap API](https://developer.apple.com/documentation/CoreAudio/capturing-system-audio-with-core-audio-taps#Configure-the-sample-code-project) by default, you now must have `NSAudioCaptureUsageDescription` defined in your `Info.plist` to use `desktopCapturer`.
|
||||
|
||||
Electron's `desktopCapturer` will create a dead audio stream if the new permission is absent however no errors or warnings will occur. This is partially a side-effect of Chromium not falling back to the older `Screen & System Audio Recording` permissions system if the new system fails.
|
||||
|
||||
To restore previous behavior:
|
||||
|
||||
```js
|
||||
// main.js (right beneath your require/import statments)
|
||||
app.commandLine.appendSwitch(
|
||||
'disable-features',
|
||||
'MacCatapLoopbackAudioForScreenShare'
|
||||
)
|
||||
```
|
||||
|
||||
### Behavior Changed: shared texture OSR `paint` event data structure
|
||||
|
||||
When using shared texture offscreen rendering feature, the `paint` event now emits a more structured object.
|
||||
|
||||
@@ -41,7 +41,7 @@ e init --root=~/electron --bootstrap testing
|
||||
```
|
||||
|
||||
The `--bootstrap` flag also runs `e sync` (synchronizes source code branches from
|
||||
[`DEPS`](https://github.com/electron/electron/blob/main/DEPS) using
|
||||
[`DEPS`](../../DEPS) using
|
||||
[`gclient`](https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/HEAD/README.gclient.md))
|
||||
and `e build` (compiles the Electron binary into the `${root}/src/out` folder).
|
||||
|
||||
@@ -63,7 +63,7 @@ Some quick tips on building once your checkout is set up:
|
||||
* **Updating your checkout:** Run git commands such as `git checkout <branch>` and `git pull` from `${root}/src/electron`.
|
||||
Whenever you update your commit `HEAD`, make sure to `e sync` before `e build` to sync dependencies
|
||||
such as Chromium and Node.js. This is especially relevant because the Chromium version in
|
||||
[`DEPS`](https://github.com/electron/electron/blob/main/DEPS) changes frequently.
|
||||
[`DEPS`](../../DEPS) changes frequently.
|
||||
* **Rebuilding:** When making changes to code in `${root}/src/electron/` in a local branch, you only need to re-run `e build`.
|
||||
* **Adding patches:** When contributing changes in `${root}/src/` outside of `${root}/src/electron/`, you need to do so
|
||||
via Electron's [patch system](./patches.md). The `e patches` command can export all relevant patches to
|
||||
@@ -98,7 +98,7 @@ Project configurations can be found in the `.gn` and `.gni` files in the `electr
|
||||
|
||||
The following `gn` files contain the main rules for building Electron:
|
||||
|
||||
* [`BUILD.gn`](https://github.com/electron/electron/blob/main/BUILD.gn) defines how Electron itself
|
||||
* [`BUILD.gn`](../../BUILD.gn) defines how Electron itself
|
||||
is built and includes the default configurations for linking with Chromium.
|
||||
* [`build/args/{testing,release,all}.gn`](https://github.com/electron/electron/tree/main/build/args)
|
||||
contain the default build arguments for building Electron.
|
||||
|
||||
@@ -6,7 +6,7 @@ This is not a comprehensive end-all guide to creating an Electron Browser API, r
|
||||
|
||||
## Add your files to Electron's project configuration
|
||||
|
||||
Electron uses [GN](https://gn.googlesource.com/gn) as a meta build system to generate files for its compiler, [Ninja](https://ninja-build.org/). This means that in order to tell Electron to compile your code, we have to add your API's code and header file names into [`filenames.gni`](https://github.com/electron/electron/blob/main/filenames.gni).
|
||||
Electron uses [GN](https://gn.googlesource.com/gn) as a meta build system to generate files for its compiler, [Ninja](https://ninja-build.org/). This means that in order to tell Electron to compile your code, we have to add your API's code and header file names into [`filenames.gni`](../../filenames.gni).
|
||||
|
||||
You will need to append your API file names alphabetically into the appropriate files like so:
|
||||
|
||||
@@ -127,7 +127,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
|
||||
## Link your Electron API with Node
|
||||
|
||||
In the [`typings/internal-ambient.d.ts`](https://github.com/electron/electron/blob/main/typings/internal-ambient.d.ts) file, we need to append a new property onto the `Process` interface like so:
|
||||
In the [`typings/internal-ambient.d.ts`](../../typings/internal-ambient.d.ts) file, we need to append a new property onto the `Process` interface like so:
|
||||
|
||||
```ts title='typings/internal-ambient.d.ts' @ts-nocheck
|
||||
interface Process {
|
||||
@@ -141,7 +141,7 @@ At the very bottom of your `api_name.cc` file:
|
||||
NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_{api_name},Initialize)
|
||||
```
|
||||
|
||||
In your [`shell/common/node_bindings.cc`](https://github.com/electron/electron/blob/main/shell/common/node_bindings.cc) file, add your node binding name to Electron's built-in modules.
|
||||
In your [`shell/common/node_bindings.cc`](../../shell/common/node_bindings.cc) file, add your node binding name to Electron's built-in modules.
|
||||
|
||||
```cpp title='shell/common/node_bindings.cc'
|
||||
#define ELECTRON_BROWSER_MODULES(V) \
|
||||
@@ -159,7 +159,7 @@ We will need to create a new TypeScript file in the path that follows:
|
||||
|
||||
`"lib/browser/api/{electron_browser_{api_name}}.ts"`
|
||||
|
||||
An example of the contents of this file can be found [here](https://github.com/electron/electron/blob/main/lib/browser/api/native-theme.ts).
|
||||
An example of the contents of this file can be found [here](../../lib/browser/api/native-theme.ts).
|
||||
|
||||
### Expose your module to TypeScript
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ $ git push origin my-branch
|
||||
### Step 9: Opening the Pull Request
|
||||
|
||||
From within GitHub, opening a new pull request will present you with a template
|
||||
that should be filled out. It can be found [here](https://github.com/electron/electron/blob/main/.github/PULL_REQUEST_TEMPLATE.md).
|
||||
that should be filled out. It can be found [here](../../.github/PULL_REQUEST_TEMPLATE.md).
|
||||
|
||||
If you do not adequately complete this template, your PR may be delayed in being merged as maintainers
|
||||
seek more information or clarify ambiguities.
|
||||
@@ -218,8 +218,7 @@ seem unfamiliar, refer to this
|
||||
|
||||
#### Approval and Request Changes Workflow
|
||||
|
||||
All pull requests require approval from a
|
||||
[Code Owner](https://github.com/electron/electron/blob/main/.github/CODEOWNERS)
|
||||
All pull requests require approval from a [Code Owner](../../.github/CODEOWNERS)
|
||||
of the area you modified in order to land. Whenever a maintainer reviews a pull
|
||||
request they may request changes. These may be small, such as fixing a typo, or
|
||||
may involve substantive changes. Such requests are intended to be helpful, but
|
||||
|
||||
@@ -10,7 +10,7 @@ to understand the source code better.
|
||||
## Project structure
|
||||
|
||||
Electron is a complex project containing multiple upstream dependencies, which are tracked in source
|
||||
control via the [`DEPS`](https://github.com/electron/electron/blob/main/DEPS) file. When
|
||||
control via the [`DEPS`](../../DEPS) file. When
|
||||
[initializing a local Electron checkout](./build-instructions-gn.md), Electron's source code is just one
|
||||
of many nested folders within the project root.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ hide_title: false
|
||||
---
|
||||
|
||||
After creating an [application distribution](application-distribution.md), the
|
||||
app's source code are usually bundled into an [ASAR archive](https://github.com/electron/asar),
|
||||
app's source code is usually bundled into an [ASAR archive](https://github.com/electron/asar),
|
||||
which is a simple extensive archive format designed for Electron apps. By bundling the app
|
||||
we can mitigate issues around long path names on Windows, speed up `require` and conceal your source
|
||||
code from cursory inspection.
|
||||
@@ -134,7 +134,7 @@ underlying system calls, Electron will extract the needed file into a
|
||||
temporary file and pass the path of the temporary file to the APIs to make them
|
||||
work. This adds a little overhead for those APIs.
|
||||
|
||||
APIs that requires extra unpacking are:
|
||||
APIs that require extra unpacking are:
|
||||
|
||||
* `child_process.execFile`
|
||||
* `child_process.execFileSync`
|
||||
|
||||
@@ -15,6 +15,14 @@ Currently, ASAR integrity checking is supported on:
|
||||
* macOS as of `electron>=16.0.0`
|
||||
* Windows as of `electron>=30.0.0`
|
||||
|
||||
> [!NOTE]
|
||||
> ASAR integrity is fully supported in Mac App Store (MAS) builds and is recommended
|
||||
> as a best practice. While MAS-installed applications have their `Resources/` folder
|
||||
> protected by the system (owned by root), ASAR integrity still provides an additional
|
||||
> layer of security. It is especially important if you use Electron's MAS build but
|
||||
> distribute your app through channels other than the Mac App Store (such as direct
|
||||
> download), since those installations won't have the system-level read-only protections.
|
||||
|
||||
In order to enable ASAR integrity checking, you also need to ensure that your `app.asar` file
|
||||
was generated by a version of the `@electron/asar` npm package that supports ASAR integrity.
|
||||
|
||||
@@ -24,7 +32,7 @@ All versions of `@electron/asar` support ASAR integrity.
|
||||
## How it works
|
||||
|
||||
Each ASAR archive contains a JSON string header. The header format includes an `integrity` object
|
||||
that contain a hex encoded hash of the entire archive as well as an array of hex encoded hashes for each
|
||||
that contains a hex encoded hash of the entire archive as well as an array of hex encoded hashes for each
|
||||
block of `blockSize` bytes.
|
||||
|
||||
```json
|
||||
|
||||
@@ -203,7 +203,7 @@ test('launch app', async () => {
|
||||
})
|
||||
```
|
||||
|
||||
After that, you will access to an instance of Playwright's `ElectronApp` class. This
|
||||
After that, you will have access to an instance of Playwright's `ElectronApp` class. This
|
||||
is a powerful class that has access to main process modules for example:
|
||||
|
||||
```js {5-10} @ts-nocheck
|
||||
@@ -237,7 +237,7 @@ test('save screenshot', async () => {
|
||||
})
|
||||
```
|
||||
|
||||
Putting all this together using the Playwright test-runner, let's create a `example.spec.js`
|
||||
Putting all this together using the Playwright test-runner, let's create an `example.spec.js`
|
||||
test file with a single test and assertion:
|
||||
|
||||
```js title='example.spec.js' @ts-nocheck
|
||||
@@ -377,7 +377,7 @@ class TestDriver {
|
||||
module.exports = { TestDriver }
|
||||
```
|
||||
|
||||
In your app code, can then write a simple handler to receive RPC calls:
|
||||
In your app code, you can then write a simple handler to receive RPC calls:
|
||||
|
||||
```js title='main.js'
|
||||
const METHODS = {
|
||||
|
||||
@@ -17,7 +17,7 @@ run them, users need to go through multiple advanced and manual steps.
|
||||
|
||||
If you are building an Electron app that you intend to package and distribute,
|
||||
it should be code signed. The Electron ecosystem tooling makes codesigning your
|
||||
apps straightforward - this documentation explains how sign your apps on both
|
||||
apps straightforward - this documentation explains how to sign your apps on both
|
||||
Windows and macOS.
|
||||
|
||||
## Signing & notarizing macOS builds
|
||||
|
||||
@@ -110,7 +110,7 @@ const win = new BrowserWindow({
|
||||
#### Show and hide the traffic lights programmatically _macOS_
|
||||
|
||||
You can also show and hide the traffic lights programmatically from the main process.
|
||||
The `win.setWindowButtonVisibility` forces traffic lights to be show or hidden depending
|
||||
The `win.setWindowButtonVisibility` forces traffic lights to be shown or hidden depending
|
||||
on the value of its boolean parameter.
|
||||
|
||||
```js title='main.js'
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
By default, windows are dragged using the title bar provided by the OS chrome. Apps
|
||||
that remove the default title bar need to use the `app-region` CSS property to define
|
||||
specific areas that can be used to drag the window. Setting `app-region: drag` marks
|
||||
a rectagular area as draggable.
|
||||
a rectangular area as draggable.
|
||||
|
||||
It is important to note that draggable areas ignore all pointer events. For example,
|
||||
a button element that overlaps a draggable region will not emit mouse clicks or mouse
|
||||
enter/exit events within that overlapping area. Setting `app-region: no-drag` reenables
|
||||
pointer events by excluding a rectagular area from a draggable region.
|
||||
pointer events by excluding a rectangular area from a draggable region.
|
||||
|
||||
To make the whole window draggable, you can add `app-region: drag` as
|
||||
`body`'s style:
|
||||
|
||||
@@ -29,7 +29,7 @@ be updated accordingly.
|
||||
In macOS 10.14 Mojave, Apple introduced a new [system-wide dark mode][system-wide-dark-mode]
|
||||
for all macOS computers. If your Electron app has a dark mode, you can make it
|
||||
follow the system-wide dark mode setting using
|
||||
[the `nativeTheme` api](../api/native-theme.md).
|
||||
[the `nativeTheme` API](../api/native-theme.md).
|
||||
|
||||
In macOS 10.15 Catalina, Apple introduced a new "automatic" dark mode option
|
||||
for all macOS computers. In order for the `nativeTheme.shouldUseDarkColors` and
|
||||
|
||||
@@ -7,47 +7,7 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
|
||||
|
||||
## Timeline
|
||||
|
||||
| Electron | Alpha | Beta | Stable | EOL | Chrome | Node | Supported |
|
||||
| ------- | ----- | ------- | ------ | ------ | ---- | ---- | ---- |
|
||||
| 40.0.0 | 2025-Oct-30 | 2025-Dec-03 | 2026-Jan-13 | 2026-Jun-30 | M144 | TBD | ✅ |
|
||||
| 39.0.0 | 2025-Sep-04 | 2025-Oct-01 | 2025-Oct-28 | 2026-May-05 | M142 | v22.20 | ✅ |
|
||||
| 38.0.0 | 2025-Jun-26 | 2025-Aug-06 | 2025-Sep-02 | 2026-Mar-10 | M140 | v22.18 | ✅ |
|
||||
| 37.0.0 | 2025-May-01 | 2025-May-28 | 2025-Jun-24 | 2026-Jan-13 | M138 | v22.16 | ✅ |
|
||||
| 36.0.0 | 2025-Mar-06 | 2025-Apr-02 | 2025-Apr-29 | 2025-Oct-28 | M136 | v22.14 | 🚫 |
|
||||
| 35.0.0 | 2025-Jan-16 | 2025-Feb-05 | 2025-Mar-04 | 2025-Sep-02 | M134 | v22.14 | 🚫 |
|
||||
| 34.0.0 | 2024-Oct-17 | 2024-Nov-13 | 2025-Jan-14 | 2025-Jun-24 | M132 | v20.18 | 🚫 |
|
||||
| 33.0.0 | 2024-Aug-22 | 2024-Sep-18 | 2024-Oct-15 | 2025-Apr-29 | M130 | v20.18 | 🚫 |
|
||||
| 32.0.0 | 2024-Jun-14 | 2024-Jul-24 | 2024-Aug-20 | 2025-Mar-04 | M128 | v20.16 | 🚫 |
|
||||
| 31.0.0 | 2024-Apr-18 | 2024-May-15 | 2024-Jun-11 | 2025-Jan-14 | M126 | v20.14 | 🚫 |
|
||||
| 30.0.0 | 2024-Feb-22 | 2024-Mar-20 | 2024-Apr-16 | 2024-Oct-15 | M124 | v20.11 | 🚫 |
|
||||
| 29.0.0 | 2023-Dec-07 | 2024-Jan-24 | 2024-Feb-20 | 2024-Aug-20 | M122 | v20.9 | 🚫 |
|
||||
| 28.0.0 | 2023-Oct-11 | 2023-Nov-06 | 2023-Dec-05 | 2024-Jun-11 | M120 | v18.18 | 🚫 |
|
||||
| 27.0.0 | 2023-Aug-17 | 2023-Sep-13 | 2023-Oct-10 | 2024-Apr-16 | M118 | v18.17 | 🚫 |
|
||||
| 26.0.0 | 2023-Jun-01 | 2023-Jun-27 | 2023-Aug-15 | 2024-Feb-20 | M116 | v18.16 | 🚫 |
|
||||
| 25.0.0 | 2023-Apr-10 | 2023-May-02 | 2023-May-30 | 2023-Dec-05 | M114 | v18.15 | 🚫 |
|
||||
| 24.0.0 | 2023-Feb-09 | 2023-Mar-07 | 2023-Apr-04 | 2023-Oct-10 | M112 | v18.14 | 🚫 |
|
||||
| 23.0.0 | 2022-Dec-01 | 2023-Jan-10 | 2023-Feb-07 | 2023-Aug-15 | M110 | v18.12 | 🚫 |
|
||||
| 22.0.0 | 2022-Sep-29 | 2022-Oct-25 | 2022-Nov-29 | 2023-Oct-10 | M108 | v16.17 | 🚫 |
|
||||
| 21.0.0 | 2022-Aug-04 | 2022-Aug-30 | 2022-Sep-27 | 2023-Apr-04 | M106 | v16.16 | 🚫 |
|
||||
| 20.0.0 | 2022-May-26 | 2022-Jun-21 | 2022-Aug-02 | 2023-Feb-07 | M104 | v16.15 | 🚫 |
|
||||
| 19.0.0 | 2022-Mar-31 | 2022-Apr-26 | 2022-May-24 | 2022-Nov-29 | M102 | v16.14 | 🚫 |
|
||||
| 18.0.0 | 2022-Feb-03 | 2022-Mar-03 | 2022-Mar-29 | 2022-Sep-27 | M100 | v16.13 | 🚫 |
|
||||
| 17.0.0 | 2021-Nov-18 | 2022-Jan-06 | 2022-Feb-01 | 2022-Aug-02 | M98 | v16.13 | 🚫 |
|
||||
| 16.0.0 | 2021-Sep-23 | 2021-Oct-20 | 2021-Nov-16 | 2022-May-24 | M96 | v16.9 | 🚫 |
|
||||
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | 2022-May-24 | M94 | v16.5 | 🚫 |
|
||||
| 14.0.0 | -- | 2021-May-27 | 2021-Aug-31 | 2022-Mar-29 | M93 | v14.17 | 🚫 |
|
||||
| 13.0.0 | -- | 2021-Mar-04 | 2021-May-25 | 2022-Feb-01 | M91 | v14.16 | 🚫 |
|
||||
| 12.0.0 | -- | 2020-Nov-19 | 2021-Mar-02 | 2021-Nov-16 | M89 | v14.16 | 🚫 |
|
||||
| 11.0.0 | -- | 2020-Aug-27 | 2020-Nov-17 | 2021-Aug-31 | M87 | v12.18 | 🚫 |
|
||||
| 10.0.0 | -- | 2020-May-21 | 2020-Aug-25 | 2021-May-25 | M85 | v12.16 | 🚫 |
|
||||
| 9.0.0 | -- | 2020-Feb-06 | 2020-May-19 | 2021-Mar-02 | M83 | v12.14 | 🚫 |
|
||||
| 8.0.0 | -- | 2019-Oct-24 | 2020-Feb-04 | 2020-Nov-17 | M80 | v12.13 | 🚫 |
|
||||
| 7.0.0 | -- | 2019-Aug-01 | 2019-Oct-22 | 2020-Aug-25 | M78 | v12.8 | 🚫 |
|
||||
| 6.0.0 | -- | 2019-Apr-25 | 2019-Jul-30 | 2020-May-19 | M76 | v12.14.0 | 🚫 |
|
||||
| 5.0.0 | -- | 2019-Jan-22 | 2019-Apr-23 | 2020-Feb-04 | M73 | v12.0 | 🚫 |
|
||||
| 4.0.0 | -- | 2018-Oct-11 | 2018-Dec-20 | 2019-Oct-22 | M69 | v10.11 | 🚫 |
|
||||
| 3.0.0 | -- | 2018-Jun-21 | 2018-Sep-18 | 2019-Jul-30 | M66 | v10.2 | 🚫 |
|
||||
| 2.0.0 | -- | 2018-Feb-21 | 2018-May-01 | 2019-Apr-23 | M61 | v8.9 | 🚫 |
|
||||
[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.
|
||||
|
||||
:::info Official support dates may change
|
||||
|
||||
|
||||
@@ -197,4 +197,4 @@ Somewhere in the Electron binary, there will be a sequence of bytes that look li
|
||||
|
||||
To flip a fuse, you find its position in the fuse wire and change it to "0" or "1" depending on the state you'd like.
|
||||
|
||||
You can view the current schema [here](https://github.com/electron/electron/blob/main/build/fuses/fuses.json5).
|
||||
You can view the current schema [here](../../build/fuses/fuses.json5).
|
||||
|
||||
@@ -171,7 +171,7 @@ sections.
|
||||
|
||||
In the main process, we'll be creating a `handleFileOpen()` function that calls
|
||||
`dialog.showOpenDialog` and returns the value of the file path selected by the user. This function
|
||||
is used as a callback whenever an `ipcRender.invoke` message is sent through the `dialog:openFile`
|
||||
is used as a callback whenever an `ipcRenderer.invoke` message is sent through the `dialog:openFile`
|
||||
channel from the renderer process. The return value is then returned as a Promise to the original
|
||||
`invoke` call.
|
||||
|
||||
@@ -446,7 +446,7 @@ After loading the preload script, your renderer process should have access to th
|
||||
We don't directly expose the whole `ipcRenderer.on` API for [security reasons][]. Make sure to
|
||||
limit the renderer's access to Electron APIs as much as possible.
|
||||
Also don't just pass the callback to `ipcRenderer.on` as this will leak `ipcRenderer` via `event.sender`.
|
||||
Use a custom handler that invoke the `callback` only with the desired arguments.
|
||||
Use a custom handler that invokes the `callback` only with the desired arguments.
|
||||
:::
|
||||
|
||||
:::info
|
||||
|
||||
@@ -10,7 +10,7 @@ hide_title: false
|
||||
## Accelerators
|
||||
|
||||
Accelerators are strings that can be used to represent keyboard shortcuts throughout your Electron.
|
||||
These strings can contain multiple modifiers keys and a single key code joined by the `+` character.
|
||||
These strings can contain multiple modifier keys and a single key code joined by the `+` character.
|
||||
|
||||
> [!NOTE]
|
||||
> Accelerators are **case-insensitive**.
|
||||
|
||||
@@ -62,9 +62,9 @@ const createWindow = () => {
|
||||
}
|
||||
```
|
||||
|
||||
In this next step, we will create our `BrowserWindow` and tell our application how to handle an event in which an external protocol is clicked.
|
||||
In this next step, we will create our `BrowserWindow` and tell our application how to handle an event in which an external protocol is clicked.
|
||||
|
||||
This code will be different in Windows and Linux compared to MacOS. This is due to both platforms emitting the `second-instance` event rather than the `open-url` event and Windows requiring additional code in order to open the contents of the protocol link within the same Electron instance. Read more about this [here](../api/app.md#apprequestsingleinstancelockadditionaldata).
|
||||
This code will be different in Windows and Linux compared to macOS. This is due to both platforms emitting the `second-instance` event rather than the `open-url` event and Windows requiring additional code in order to open the contents of the protocol link within the same Electron instance. Read more about this [here](../api/app.md#apprequestsingleinstancelockadditionaldata).
|
||||
|
||||
#### Windows and Linux code:
|
||||
|
||||
@@ -91,7 +91,7 @@ if (!gotTheLock) {
|
||||
}
|
||||
```
|
||||
|
||||
#### MacOS code:
|
||||
#### macOS code:
|
||||
|
||||
```js @ts-type={createWindow:()=>void}
|
||||
// This method will be called when Electron has finished
|
||||
|
||||
@@ -65,7 +65,7 @@ The full list of certificate types can be found
|
||||
Apps signed with "Apple Development" and "Apple Distribution" certificates can
|
||||
only run under [App Sandbox][app-sandboxing], so they must use the MAS build of
|
||||
Electron. However, the "Developer ID Application" certificate does not have this
|
||||
restrictions, so apps signed with it can use either the normal build or the MAS
|
||||
restriction, so apps signed with it can use either the normal build or the MAS
|
||||
build of Electron.
|
||||
|
||||
#### Legacy certificate names
|
||||
@@ -208,7 +208,7 @@ signAsync({
|
||||
After signing the app with the "Apple Distribution" certificate, you can
|
||||
continue to submit it to Mac App Store.
|
||||
|
||||
However, this guide do not ensure your app will be approved by Apple; you
|
||||
However, this guide does not ensure your app will be approved by Apple; you
|
||||
still need to read Apple's [Submitting Your App][submitting-your-app] guide on
|
||||
how to meet the Mac App Store requirements.
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Electron application, and this property only exists on macOS.
|
||||
One of the main uses for your app's Dock icon is to expose additional app menus. The Dock menu is
|
||||
triggered by right-clicking or <kbd>Ctrl</kbd>-clicking the app icon. By default, the app's Dock menu
|
||||
will come with system-provided window management utilities, including the ability to show all windows,
|
||||
hide the app, and switch betweeen different open windows.
|
||||
hide the app, and switch between different open windows.
|
||||
|
||||
To set an app-defined custom Dock menu, pass any [Menu](../api/menu.md) instance into the
|
||||
[`dock.setMenu`](../api/dock.md#docksetmenumenu-macos) API.
|
||||
|
||||
@@ -200,7 +200,7 @@ macOS has a number of platform-specific menu roles available. Many of these map
|
||||
|
||||
* `recentDocuments` - The submenu is an "Open Recent" menu.
|
||||
* `clearRecentDocuments` - Map to the [`clearRecentDocuments`](https://developer.apple.com/documentation/appkit/nsdocumentcontroller/clearrecentdocuments(_:)) action.
|
||||
* `shareMenu` - The submenu is [share menu][ShareMenu]. The `sharingItem` property must also be set to indicate the item to share.
|
||||
* `shareMenu` - The submenu is [share menu](../api/share-menu.md). The `sharingItem` property must also be set to indicate the item to share.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> When specifying a `role` on macOS, `label` and `accelerator` are the only
|
||||
|
||||
@@ -1339,7 +1339,7 @@ For developers wanting to learn more, you can refer to the [official N-API docum
|
||||
|
||||
### Putting `cpp_addon.cc` together
|
||||
|
||||
We've now finished the bridge part our addon - that is, the code that's most concerned with being the bridge between your JavaScript and C++ code (and by contrast, less so actually interacting with the operating system or GTK). After adding all the sections above, your `src/cpp_addon.cc` should look like this:
|
||||
We've now finished the bridge part of our addon - that is, the code that's most concerned with being the bridge between your JavaScript and C++ code (and by contrast, less so actually interacting with the operating system or GTK). After adding all the sections above, your `src/cpp_addon.cc` should look like this:
|
||||
|
||||
```cpp title='src/cpp_addon.cc'
|
||||
#include <napi.h>
|
||||
|
||||
@@ -4,13 +4,13 @@ This tutorial builds on the [general introduction to Native Code and Electron](.
|
||||
|
||||
Specifically, we'll be integrating with two commonly used native Windows libraries:
|
||||
|
||||
* `comctl32.lib`, which contains common controls and user interface components. It provides various UI elements like buttons, scrollbars, toolbars, status bars, progress bars, and tree views. As far as GUI development on Windows goes, this library is very low-level and basic - more modern frameworks like WinUI or WPF are advanced and alternatives but require a lot more C++ and Windows version considerations than are useful for this tutorial. This way, we can avoid the many perils of building native interfaces for multiple Windows versions!
|
||||
* `comctl32.lib`, which contains common controls and user interface components. It provides various UI elements like buttons, scrollbars, toolbars, status bars, progress bars, and tree views. As far as GUI development on Windows goes, this library is very low-level and basic - more modern frameworks like WinUI or WPF are more advanced alternatives but require a lot more C++ and Windows version considerations than are useful for this tutorial. This way, we can avoid the many perils of building native interfaces for multiple Windows versions!
|
||||
* `shcore.lib`, a library that provides high-DPI awareness functionality and other Shell-related features around managing displays and UI elements.
|
||||
|
||||
This tutorial will be most useful to those who already have some familiarity with native C++ GUI development on Windows. You should have experience with basic window classes and procedures, like `WNDCLASSEXW` and `WindowProc` functions. You should also be familiar with the Windows message loop, which is the heart of any native application - our code will be using `GetMessage`, `TranslateMessage`, and `DispatchMessage` to handle messages. Lastly, we'll be using (but not explaining) standard Win32 controls like `WC_EDITW` or `WC_BUTTONW`.
|
||||
|
||||
> [!NOTE]
|
||||
> If you're not familiar with C++ GUI development on Windows, we recommend Microsoft's excellent documentation and guides, particular for beginners. "[Get Started with Win32 and C++](https://learn.microsoft.com/en-us/windows/win32/learnwin32/learn-to-program-for-windows)" is a great introduction.
|
||||
> If you're not familiar with C++ GUI development on Windows, we recommend Microsoft's excellent documentation and guides, particularly for beginners. "[Get Started with Win32 and C++](https://learn.microsoft.com/en-us/windows/win32/learnwin32/learn-to-program-for-windows)" is a great introduction.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -1333,7 +1333,7 @@ npm run build
|
||||
|
||||
## Conclusion
|
||||
|
||||
You've now built a complete native Node.js addon for Windows using C++ and the Win32 API. Some of things we've done here are:
|
||||
You've now built a complete native Node.js addon for Windows using C++ and the Win32 API. Some of the things we've done here are:
|
||||
|
||||
1. Creating a native Windows GUI from C++
|
||||
2. Implementing a Todo list application with Add, Edit, and Delete functionality
|
||||
|
||||
@@ -1167,7 +1167,7 @@ The approach demonstrated here allows you to:
|
||||
* Setting up bidirectional communication using callbacks and events
|
||||
* Configuring a custom build process to compile Swift code
|
||||
|
||||
For more information on developing with Swift and Swift, refer to Apple's developer documentation:
|
||||
For more information on developing with Swift and SwiftUI, refer to Apple's developer documentation:
|
||||
|
||||
* [Swift Programming Language](https://developer.apple.com/swift/)
|
||||
* [SwiftUI Framework](https://developer.apple.com/documentation/swiftui)
|
||||
|
||||
@@ -36,8 +36,8 @@ setting.
|
||||
This is an advanced feature requiring a native node module to work with your own code.
|
||||
The frames are directly copied in GPU textures, thus this mode is very fast because
|
||||
there's no CPU-GPU memory copies overhead, and you can directly import the shared
|
||||
texture to your own rendering program. You can read more details at
|
||||
[here](https://github.com/electron/electron/blob/main/shell/browser/osr/README.md).
|
||||
texture to your own rendering program. You can read more details
|
||||
[here](../../shell/common/api/shared_texture/README.md).
|
||||
|
||||
2. Use CPU shared memory bitmap
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ particularly useful if users complain about your app sometimes "stuttering".
|
||||
|
||||
Generally speaking, all advice for building performant web apps for modern
|
||||
browsers apply to Electron's renderers, too. The two primary tools at your
|
||||
disposal are currently `requestIdleCallback()` for small operations and
|
||||
disposal are currently `requestIdleCallback()` for small operations and
|
||||
`Web Workers` for long-running operations.
|
||||
|
||||
_`requestIdleCallback()`_ allows developers to queue up a function to be
|
||||
@@ -360,7 +360,7 @@ turning into a desktop application. As web developers, we are used to loading
|
||||
resources from a variety of content delivery networks. Now that you are
|
||||
shipping a proper desktop application, attempt to "cut the cord" where possible
|
||||
and avoid letting your users wait for resources that never change and could
|
||||
easily be included in your app.
|
||||
easily be included in your app.
|
||||
|
||||
A typical example is Google Fonts. Many developers make use of Google's
|
||||
impressive collection of free fonts, which comes with a content delivery
|
||||
|
||||
@@ -113,7 +113,7 @@ For a full list of Electron's main process modules, check out our API documentat
|
||||
|
||||
Each Electron app spawns a separate renderer process for each open `BrowserWindow`
|
||||
(and each web embed). As its name implies, a renderer is responsible for
|
||||
_rendering_ web content. For all intents and purposes, code ran in renderer processes
|
||||
_rendering_ web content. For all intents and purposes, code run in renderer processes
|
||||
should behave according to web standards (insofar as Chromium does, at least).
|
||||
|
||||
Therefore, all user interfaces and app functionality within a single browser
|
||||
|
||||
@@ -9,7 +9,7 @@ toc_max_heading_level: 3
|
||||
|
||||
:::info Reporting security issues
|
||||
For information on how to properly disclose an Electron vulnerability,
|
||||
see [SECURITY.md](https://github.com/electron/electron/blob/main/SECURITY.md).
|
||||
see [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
For upstream Chromium vulnerabilities: Electron keeps up to date with alternating
|
||||
Chromium releases. For more information, see the
|
||||
@@ -771,7 +771,7 @@ ipcMain.handle('get-secrets', (e) => {
|
||||
})
|
||||
|
||||
function validateSender (frame) {
|
||||
// Value the host of the URL using an actual URL parser and an allowlist
|
||||
// Validate the host of the URL using an actual URL parser and an allowlist
|
||||
if ((new URL(frame.url)).host === 'electronjs.org') return true
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
* For information on supported releases, see the [Electron Releases](./electron-timelines.md) doc.
|
||||
* For community support on Electron, see the [Community page](https://www.electronjs.org/community).
|
||||
* For platform support info, see the [README](https://github.com/electron/electron/blob/main/README.md).
|
||||
* For platform support info, see the [README](../../README.md).
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
Being based on Chromium, Electron requires a display driver to function.
|
||||
If Chromium can't find a display driver, Electron will fail to launch -
|
||||
and therefore not executing any of your tests, regardless of how you are running
|
||||
them. Testing Electron-based apps on Travis, CircleCI, Jenkins or similar Systems
|
||||
and therefore not execute any of your tests, regardless of how you are running
|
||||
them. Testing Electron-based apps on Travis, CircleCI, Jenkins or similar systems
|
||||
requires therefore a little bit of configuration. In essence, we need to use
|
||||
a virtual display driver.
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ following JSON format:
|
||||
"updateTo": {
|
||||
"version": "1.2.1",
|
||||
"pub_date": "2023-09-18T12:29:53+01:00",
|
||||
"notes": "Theses are some release notes innit",
|
||||
"notes": "These are some release notes innit",
|
||||
"name": "1.2.1",
|
||||
"url": "https://mycompany.example.com/myapp/releases/myrelease"
|
||||
}
|
||||
@@ -54,7 +54,7 @@ following JSON format:
|
||||
"updateTo": {
|
||||
"version": "1.2.3",
|
||||
"pub_date": "2024-09-18T12:29:53+01:00",
|
||||
"notes": "Theses are some more release notes innit",
|
||||
"notes": "These are some more release notes innit",
|
||||
"name": "1.2.3",
|
||||
"url": "https://mycompany.example.com/myapp/releases/myrelease3"
|
||||
}
|
||||
@@ -307,7 +307,7 @@ app update. All other properties in the object are optional.
|
||||
{
|
||||
"url": "https://your-static.storage/your-app-1.2.3-darwin.zip",
|
||||
"name": "1.2.3",
|
||||
"notes": "Theses are some release notes innit",
|
||||
"notes": "These are some release notes innit",
|
||||
"pub_date": "2024-09-18T12:29:53+01:00"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -149,7 +149,7 @@ for an example delay-load hook if you're implementing your own.
|
||||
native Node modules with prebuilt binaries for multiple versions of Node
|
||||
and Electron.
|
||||
|
||||
If the `prebuild`-powered module provide binaries for the usage in Electron,
|
||||
If the `prebuild`-powered module provides binaries for the usage in Electron,
|
||||
make sure to omit `--build-from-source` and the `npm_config_build_from_source`
|
||||
environment variable in order to take full advantage of the prebuilt binaries.
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ To test your app, use a Windows on Arm device running Windows 10 (version 1903 o
|
||||
|
||||
### Node.js/node-gyp
|
||||
|
||||
[Node.js v12.9.0 or later is recommended.](https://nodejs.org/en/) If updating to a new version of Node is undesirable, you can instead [update npm's copy of node-gyp manually](https://github.com/nodejs/node-gyp/wiki/Updating-npm's-bundled-node-gyp) to version 5.0.2 or later, which contains the required changes to compile native modules for Arm.
|
||||
[Node.js v12.9.0 or later is recommended.](https://nodejs.org/en/) If updating to a new version of Node is undesirable, you can instead [update npm's copy of node-gyp manually](https://github.com/nodejs/node-gyp/wiki/Updating-npm's-bundled-node-gyp) to version 5.0.2 or later, which contains the required changes to compile native modules for Arm.
|
||||
|
||||
### Visual Studio 2017
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ system.
|
||||
|
||||
Before running the CLI for the first time, you will have to setup the "Windows Desktop App
|
||||
Converter". This will take a few minutes, but don't worry - you only have to do
|
||||
this once. Download and Desktop App Converter from [here][app-converter].
|
||||
this once. Download the Desktop App Converter from [here][app-converter].
|
||||
You will receive two files: `DesktopAppConverter.zip` and `BaseImage-14316.wim`.
|
||||
|
||||
1. Unzip `DesktopAppConverter.zip`. From an elevated PowerShell (opened with
|
||||
|
||||
@@ -79,6 +79,8 @@ filenames = {
|
||||
"shell/browser/notifications/win/notification_presenter_win.h",
|
||||
"shell/browser/notifications/win/windows_toast_notification.cc",
|
||||
"shell/browser/notifications/win/windows_toast_notification.h",
|
||||
"shell/browser/notifications/win/windows_toast_activator.cc",
|
||||
"shell/browser/notifications/win/windows_toast_activator.h",
|
||||
"shell/browser/relauncher_win.cc",
|
||||
"shell/browser/ui/certificate_trust_win.cc",
|
||||
"shell/browser/ui/file_dialog_win.cc",
|
||||
@@ -113,6 +115,8 @@ filenames = {
|
||||
"shell/browser/win/scoped_hstring.h",
|
||||
"shell/common/api/electron_api_native_image_win.cc",
|
||||
"shell/common/application_info_win.cc",
|
||||
"shell/common/command_line_util_win.cc",
|
||||
"shell/common/command_line_util_win.h",
|
||||
"shell/common/language_util_win.cc",
|
||||
"shell/common/node_bindings_win.cc",
|
||||
"shell/common/node_bindings_win.h",
|
||||
|
||||
@@ -26,9 +26,9 @@ const MenuItem = function (this: any, options: any) {
|
||||
this.overrideReadOnlyProperty('type', roles.getDefaultType(this.role));
|
||||
this.overrideReadOnlyProperty('role');
|
||||
this.overrideReadOnlyProperty('accelerator', roles.getDefaultAccelerator(this.role));
|
||||
this.overrideReadOnlyProperty('icon');
|
||||
this.overrideReadOnlyProperty('submenu');
|
||||
|
||||
this.overrideProperty('icon');
|
||||
this.overrideProperty('label', roles.getDefaultLabel(this.role));
|
||||
this.overrideProperty('sublabel', '');
|
||||
this.overrideProperty('toolTip', '');
|
||||
|
||||
@@ -53,6 +53,18 @@ Menu.prototype._isCommandIdVisible = function (id) {
|
||||
return this.commandsMap[id]?.visible ?? false;
|
||||
};
|
||||
|
||||
Menu.prototype._getLabelForCommandId = function (id) {
|
||||
return this.commandsMap[id]?.label ?? '';
|
||||
};
|
||||
|
||||
Menu.prototype._getSecondaryLabelForCommandId = function (id) {
|
||||
return this.commandsMap[id]?.sublabel ?? '';
|
||||
};
|
||||
|
||||
Menu.prototype._getIconForCommandId = function (id) {
|
||||
return this.commandsMap[id]?.icon ?? null;
|
||||
};
|
||||
|
||||
Menu.prototype._getAcceleratorForCommandId = function (id, useDefaultAccelerator) {
|
||||
const command = this.commandsMap[id];
|
||||
if (!command) return;
|
||||
@@ -158,7 +170,6 @@ Menu.prototype.insert = function (pos, item) {
|
||||
insertItemByType.call(this, item, pos);
|
||||
|
||||
// set item properties
|
||||
if (item.sublabel) this.setSublabel(pos, item.sublabel);
|
||||
if (item.toolTip) this.setToolTip(pos, item.toolTip);
|
||||
if (item.icon) this.setIcon(pos, item.icon);
|
||||
if (item.role) this.setRole(pos, item.role);
|
||||
|
||||
@@ -8,13 +8,19 @@ const {
|
||||
isOnBatteryPower
|
||||
} = process._linkedBinding('electron_browser_power_monitor');
|
||||
|
||||
// Hold the native PowerMonitor at module level so it is never garbage-collected
|
||||
// while this module is alive. The C++ side registers OS-level callbacks (HWND
|
||||
// user-data on Windows, shutdown handler on macOS, notification observers) that
|
||||
// prevent safe collection of the C++ wrapper while those registrations exist.
|
||||
let pm: any;
|
||||
|
||||
class PowerMonitor extends EventEmitter implements Electron.PowerMonitor {
|
||||
constructor () {
|
||||
super();
|
||||
// Don't start the event source until both a) the app is ready and b)
|
||||
// there's a listener registered for a powerMonitor event.
|
||||
this.once('newListener', () => {
|
||||
const pm = createPowerMonitor();
|
||||
pm = createPowerMonitor();
|
||||
pm.emit = this.emit.bind(this);
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
|
||||
@@ -777,8 +777,7 @@ WebContents.prototype._init = function () {
|
||||
const originCounts = new Map<string, number>();
|
||||
const openDialogs = new Set<AbortController>();
|
||||
this.on('-run-dialog', async (info, callback) => {
|
||||
const originUrl = new URL(info.frame.url);
|
||||
const origin = originUrl.protocol === 'file:' ? originUrl.href : originUrl.origin;
|
||||
const origin = info.frame.origin === 'file://' ? info.frame.url : info.frame.origin;
|
||||
if ((originCounts.get(origin) ?? 0) < 0) return callback(false, '');
|
||||
|
||||
const prefs = this.getLastWebPreferences();
|
||||
|
||||
@@ -17,11 +17,6 @@ export type WindowOpenArgs = {
|
||||
features: string,
|
||||
}
|
||||
|
||||
const frameNamesToWindow = new Map<string, WebContents>();
|
||||
const registerFrameNameToGuestWindow = (name: string, webContents: WebContents) => frameNamesToWindow.set(name, webContents);
|
||||
const unregisterFrameName = (name: string) => frameNamesToWindow.delete(name);
|
||||
const getGuestWebContentsByFrameName = (name: string) => frameNamesToWindow.get(name);
|
||||
|
||||
/**
|
||||
* `openGuestWindow` is called to create and setup event handling for the new
|
||||
* window.
|
||||
@@ -47,20 +42,6 @@ export function openGuestWindow ({ embedder, guest, referrer, disposition, postD
|
||||
...overrideBrowserWindowOptions
|
||||
};
|
||||
|
||||
// To spec, subsequent window.open calls with the same frame name (`target` in
|
||||
// spec parlance) will reuse the previous window.
|
||||
// https://html.spec.whatwg.org/multipage/window-object.html#apis-for-creating-and-navigating-browsing-contexts-by-name
|
||||
const existingWebContents = getGuestWebContentsByFrameName(frameName);
|
||||
if (existingWebContents) {
|
||||
if (existingWebContents.isDestroyed()) {
|
||||
// FIXME(t57ser): The webContents is destroyed for some reason, unregister the frame name
|
||||
unregisterFrameName(frameName);
|
||||
} else {
|
||||
existingWebContents.loadURL(url);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (createWindow) {
|
||||
const webContents = createWindow({
|
||||
webContents: guest,
|
||||
@@ -72,7 +53,7 @@ export function openGuestWindow ({ embedder, guest, referrer, disposition, postD
|
||||
throw new Error('Invalid webContents. Created window should be connected to webContents passed with options object.');
|
||||
}
|
||||
|
||||
handleWindowLifecycleEvents({ embedder, frameName, guest, outlivesOpener });
|
||||
handleWindowLifecycleEvents({ embedder, guest, outlivesOpener });
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -96,7 +77,7 @@ export function openGuestWindow ({ embedder, guest, referrer, disposition, postD
|
||||
});
|
||||
}
|
||||
|
||||
handleWindowLifecycleEvents({ embedder, frameName, guest: window.webContents, outlivesOpener });
|
||||
handleWindowLifecycleEvents({ embedder, guest: window.webContents, outlivesOpener });
|
||||
|
||||
embedder.emit('did-create-window', window, { url, frameName, options: browserWindowOptions, disposition, referrer, postData });
|
||||
}
|
||||
@@ -107,10 +88,9 @@ export function openGuestWindow ({ embedder, guest, referrer, disposition, postD
|
||||
* too is the guest destroyed; this is Electron convention and isn't based in
|
||||
* browser behavior.
|
||||
*/
|
||||
const handleWindowLifecycleEvents = function ({ embedder, guest, frameName, outlivesOpener }: {
|
||||
const handleWindowLifecycleEvents = function ({ embedder, guest, outlivesOpener }: {
|
||||
embedder: WebContents,
|
||||
guest: WebContents,
|
||||
frameName: string,
|
||||
outlivesOpener: boolean
|
||||
}) {
|
||||
const closedByEmbedder = function () {
|
||||
@@ -128,13 +108,6 @@ const handleWindowLifecycleEvents = function ({ embedder, guest, frameName, outl
|
||||
embedder.once('current-render-view-deleted' as any, closedByEmbedder);
|
||||
}
|
||||
guest.once('destroyed', closedByUser);
|
||||
|
||||
if (frameName) {
|
||||
registerFrameNameToGuestWindow(frameName, guest);
|
||||
guest.once('destroyed', function () {
|
||||
unregisterFrameName(frameName);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Security options that child windows will always inherit from parent windows
|
||||
|
||||
@@ -19,8 +19,8 @@ export function invokeInWebContents<T> (sender: Electron.WebContents, command: s
|
||||
const requestId = ++nextId;
|
||||
const channel = `${command}_RESPONSE_${requestId}`;
|
||||
ipcMainInternal.on(channel, function handler (event, error: Error, result: any) {
|
||||
if (event.type === 'frame' && event.sender !== sender) {
|
||||
console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`);
|
||||
if (event.type !== 'frame' || event.sender !== sender) {
|
||||
console.error(`Reply to ${command} sent by unexpected sender`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,8 +43,8 @@ export function invokeInWebFrameMain<T> (sender: Electron.WebFrameMain, command:
|
||||
const channel = `${command}_RESPONSE_${requestId}`;
|
||||
const frameTreeNodeId = sender.frameTreeNodeId;
|
||||
ipcMainInternal.on(channel, function handler (event, error: Error, result: any) {
|
||||
if (event.type === 'frame' && event.frameTreeNodeId !== frameTreeNodeId) {
|
||||
console.error(`Reply to ${command} sent by unexpected WebFrameMain (${event.frameTreeNodeId})`);
|
||||
if (event.type !== 'frame' || event.frameTreeNodeId !== frameTreeNodeId) {
|
||||
console.error(`Reply to ${command} sent by unexpected sender`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -227,10 +227,9 @@ function validateHeader (name: any, value: any): void {
|
||||
}
|
||||
|
||||
function parseOptions (optionsIn: ClientRequestConstructorOptions | string): NodeJS.CreateURLLoaderOptions & ExtraURLLoaderOptions {
|
||||
// eslint-disable-next-line n/no-deprecated-api
|
||||
const options: any = typeof optionsIn === 'string' ? url.parse(optionsIn) : { ...optionsIn };
|
||||
const options: any = typeof optionsIn === 'string' ? new URL(optionsIn) : { ...optionsIn };
|
||||
|
||||
let urlStr: string = options.url;
|
||||
let urlStr: string = options.url || options.href;
|
||||
|
||||
if (!urlStr) {
|
||||
const urlObj: url.UrlObject = {};
|
||||
@@ -260,8 +259,8 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
|
||||
// an invalid request.
|
||||
throw new TypeError('Request path contains unescaped characters');
|
||||
}
|
||||
// eslint-disable-next-line n/no-deprecated-api
|
||||
const pathObj = url.parse(options.path || '/');
|
||||
|
||||
const pathObj = new URL(options.path || '/', 'http://localhost');
|
||||
urlObj.pathname = pathObj.pathname;
|
||||
urlObj.search = pathObj.search;
|
||||
urlObj.hash = pathObj.hash;
|
||||
|
||||
@@ -1232,6 +1232,8 @@ export const wrapFsWithAsar = (fs: Record<string, any>) => {
|
||||
// has filesystem caching.
|
||||
overrideAPI(fs, 'copyFile');
|
||||
overrideAPISync(fs, 'copyFileSync');
|
||||
overrideAPI(fs, 'cp');
|
||||
overrideAPISync(fs, 'cpSync');
|
||||
|
||||
overrideAPI(fs, 'open');
|
||||
overrideAPISync(process, 'dlopen', 1);
|
||||
|
||||
@@ -9,6 +9,7 @@ if ((globalThis as any).blinkfetch) {
|
||||
const keys = ['fetch', 'Response', 'FormData', 'Request', 'Headers', 'EventSource'];
|
||||
for (const key of keys) {
|
||||
(globalThis as any)[key] = (globalThis as any)[`blink${key}`];
|
||||
delete (globalThis as any)[`blink${key}`];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,14 @@ export default contextBridge;
|
||||
|
||||
export const internalContextBridge = {
|
||||
contextIsolationEnabled: process.contextIsolated,
|
||||
tryOverrideGlobalValueFromIsolatedWorld: (keys: string[], value: any) => {
|
||||
return binding._overrideGlobalValueFromIsolatedWorld(keys, value, true, true);
|
||||
},
|
||||
overrideGlobalValueFromIsolatedWorld: (keys: string[], value: any) => {
|
||||
return binding._overrideGlobalValueFromIsolatedWorld(keys, value, false);
|
||||
return binding._overrideGlobalValueFromIsolatedWorld(keys, value, false, false);
|
||||
},
|
||||
overrideGlobalValueWithDynamicPropsFromIsolatedWorld: (keys: string[], value: any) => {
|
||||
return binding._overrideGlobalValueFromIsolatedWorld(keys, value, true);
|
||||
return binding._overrideGlobalValueFromIsolatedWorld(keys, value, true, false);
|
||||
},
|
||||
overrideGlobalPropertyFromIsolatedWorld: (keys: string[], getter: Function, setter?: Function) => {
|
||||
return binding._overrideGlobalPropertyFromIsolatedWorld(keys, getter, setter || null);
|
||||
|
||||
@@ -11,14 +11,12 @@ const { contextIsolationEnabled } = internalContextBridge;
|
||||
* 1) Use menu API to show context menu.
|
||||
*/
|
||||
window.onload = function () {
|
||||
if (window.InspectorFrontendHost) {
|
||||
if (contextIsolationEnabled) {
|
||||
internalContextBridge.overrideGlobalValueFromIsolatedWorld([
|
||||
'InspectorFrontendHost', 'showContextMenuAtPoint'
|
||||
], createMenu);
|
||||
} else {
|
||||
window.InspectorFrontendHost.showContextMenuAtPoint = createMenu;
|
||||
}
|
||||
if (contextIsolationEnabled) {
|
||||
internalContextBridge.tryOverrideGlobalValueFromIsolatedWorld([
|
||||
'InspectorFrontendHost', 'showContextMenuAtPoint'
|
||||
], createMenu);
|
||||
} else {
|
||||
window.InspectorFrontendHost!.showContextMenuAtPoint = createMenu;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ if ((globalThis as any).blinkfetch) {
|
||||
const keys = ['fetch', 'Response', 'FormData', 'Request', 'Headers', 'EventSource'];
|
||||
for (const key of keys) {
|
||||
(globalThis as any)[key] = (globalThis as any)[`blink${key}`];
|
||||
delete (globalThis as any)[`blink${key}`];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
"lint:gn": "node ./script/lint.js --gn",
|
||||
"lint:docs": "remark docs -qf && npm run lint:js-in-markdown && npm run create-typescript-definitions && npm run lint:ts-check-js-in-markdown && npm run lint:docs-fiddles && npm run lint:docs-relative-links && npm run lint:markdown && npm run lint:api-history",
|
||||
"lint:docs-fiddles": "standard \"docs/fiddles/**/*.js\"",
|
||||
"lint:docs-relative-links": "lint-roller-markdown-links --root docs \"**/*.md\"",
|
||||
"lint:docs-relative-links": "lint-roller-markdown-links --resource-root . --root docs \"**/*.md\"",
|
||||
"lint:markdown": "node ./script/lint.js --md",
|
||||
"lint:ts-check-js-in-markdown": "lint-roller-markdown-ts-check --root docs \"**/*.md\" --ignore \"breaking-changes.md\"",
|
||||
"lint:js-in-markdown": "lint-roller-markdown-standard --root docs \"**/*.md\"",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
optionally_validate_gl_max_uniform_blocks_at_compile_time.patch
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Geoff Lang <geofflang@chromium.org>
|
||||
Date: Wed, 11 Feb 2026 15:51:46 -0500
|
||||
Subject: Optionally validate GL_MAX_*_UNIFORM_BLOCKS at compile time.
|
||||
|
||||
These were validated at link time but some drivers have compiler crashes
|
||||
when compiling shaders with too many uniform blocks.
|
||||
|
||||
Bug: chromium:475877320
|
||||
Change-Id: I4413ce06307b4fe9e27105d85f66f610c235a301
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7568089
|
||||
Commit-Queue: Geoff Lang <geofflang@chromium.org>
|
||||
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
|
||||
diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
|
||||
index 90b1239689c98a8c94f8c6f57d572268a4450923..81445ade5f2285d52a6b83bfc574c0fa56d4a0da 100644
|
||||
--- a/include/GLSLANG/ShaderLang.h
|
||||
+++ b/include/GLSLANG/ShaderLang.h
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
// Version number for shader translation API.
|
||||
// It is incremented every time the API changes.
|
||||
-#define ANGLE_SH_VERSION 386
|
||||
+#define ANGLE_SH_VERSION 387
|
||||
|
||||
enum ShShaderSpec
|
||||
{
|
||||
@@ -383,6 +383,10 @@ struct ShCompileOptions
|
||||
|
||||
uint64_t forceShaderPrecisionHighpToMediump : 1;
|
||||
|
||||
+ // Validate that the count of uniform blocks is within the GL_MAX_*_UNIFORM_BLOCKS limits. These
|
||||
+ // limits must be supplied in the BuiltinResources.
|
||||
+ uint64_t validatePerStageMaxUniformBlocks : 1;
|
||||
+
|
||||
// Ask compiler to generate Vulkan transform feedback emulation support code.
|
||||
uint64_t addVulkanXfbEmulationSupportCode : 1;
|
||||
|
||||
@@ -584,6 +588,12 @@ struct ShBuiltInResources
|
||||
int MinProgramTexelOffset;
|
||||
int MaxProgramTexelOffset;
|
||||
|
||||
+ // GL_MAX_FRAGMENT_UNIFORM_BLOCKS
|
||||
+ int MaxFragmentUniformBlocks;
|
||||
+
|
||||
+ // GL_MAX_VERTEX_UNIFORM_BLOCKS
|
||||
+ int MaxVertexUniformBlocks;
|
||||
+
|
||||
// Extension constants.
|
||||
|
||||
// Value of GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT for OpenGL ES output context.
|
||||
@@ -701,6 +711,9 @@ struct ShBuiltInResources
|
||||
// maximum point size (higher limit from ALIASED_POINT_SIZE_RANGE)
|
||||
float MaxPointSize;
|
||||
|
||||
+ // GL_MAX_COMPUTE_UNIFORM_BLOCKS
|
||||
+ int MaxComputeUniformBlocks;
|
||||
+
|
||||
// EXT_geometry_shader constants
|
||||
int MaxGeometryUniformComponents;
|
||||
int MaxGeometryUniformBlocks;
|
||||
@@ -724,6 +737,7 @@ struct ShBuiltInResources
|
||||
int MaxTessControlImageUniforms;
|
||||
int MaxTessControlAtomicCounters;
|
||||
int MaxTessControlAtomicCounterBuffers;
|
||||
+ int MaxTessControlUniformBlocks;
|
||||
|
||||
int MaxTessPatchComponents;
|
||||
int MaxPatchVertices;
|
||||
@@ -736,6 +750,7 @@ struct ShBuiltInResources
|
||||
int MaxTessEvaluationImageUniforms;
|
||||
int MaxTessEvaluationAtomicCounters;
|
||||
int MaxTessEvaluationAtomicCounterBuffers;
|
||||
+ int MaxTessEvaluationUniformBlocks;
|
||||
|
||||
// Subpixel bits used in rasterization.
|
||||
int SubPixelBits;
|
||||
diff --git a/include/platform/autogen/FeaturesGL_autogen.h b/include/platform/autogen/FeaturesGL_autogen.h
|
||||
index a732a77d66ec2fad9677d500d8f0ae3d0d92c454..f0b391dfbc4d238df458b334951371429fd496a4 100644
|
||||
--- a/include/platform/autogen/FeaturesGL_autogen.h
|
||||
+++ b/include/platform/autogen/FeaturesGL_autogen.h
|
||||
@@ -638,6 +638,12 @@ struct FeaturesGL : FeatureSetBase
|
||||
&members,
|
||||
};
|
||||
|
||||
+ FeatureInfo validateMaxPerStageUniformBlocksAtCompileTime = {
|
||||
+ "validateMaxPerStageUniformBlocksAtCompileTime",
|
||||
+ FeatureCategory::OpenGLWorkarounds,
|
||||
+ &members,
|
||||
+ };
|
||||
+
|
||||
};
|
||||
|
||||
inline FeaturesGL::FeaturesGL() = default;
|
||||
diff --git a/include/platform/gl_features.json b/include/platform/gl_features.json
|
||||
index e993768974e63d19bf4b71702fe9725aa97ea2fb..03454778c2acfd10c2a3762ef6ad1e9eb0fc7bb2 100644
|
||||
--- a/include/platform/gl_features.json
|
||||
+++ b/include/platform/gl_features.json
|
||||
@@ -828,6 +828,14 @@
|
||||
"Some ES2 Mali drivers are unable to query enough information from a linked program to use passthrough shaders."
|
||||
],
|
||||
"issue": "https://crbug.com/451796659"
|
||||
+ },
|
||||
+ {
|
||||
+ "name": "validate_max_per_stage_uniform_blocks_at_compile_time",
|
||||
+ "category": "Workarounds",
|
||||
+ "description": [
|
||||
+ "Validate GL_MAX_*_UNIFORM_BLOCKS at compile time instead of link time to work around compiler bugs."
|
||||
+ ],
|
||||
+ "issue": "http://crbug.com/475877320"
|
||||
}
|
||||
]
|
||||
}
|
||||
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
|
||||
index 7e36dc2cd24a5d0a3a1a009a1171684e4dfe62e5..aae0f1008d573a55e822c10cf02149b8ccef6055 100644
|
||||
--- a/src/compiler/translator/Compiler.cpp
|
||||
+++ b/src/compiler/translator/Compiler.cpp
|
||||
@@ -1563,6 +1563,8 @@ void TCompiler::setResourceString()
|
||||
<< ":MaxFragmentInputVectors:" << mResources.MaxFragmentInputVectors
|
||||
<< ":MinProgramTexelOffset:" << mResources.MinProgramTexelOffset
|
||||
<< ":MaxProgramTexelOffset:" << mResources.MaxProgramTexelOffset
|
||||
+ << ":MaxFragmentUniformBlocks:" << mResources.MaxFragmentUniformBlocks
|
||||
+ << ":MaxVertexUniformBlocks:" << mResources.MaxVertexUniformBlocks
|
||||
<< ":MaxDualSourceDrawBuffers:" << mResources.MaxDualSourceDrawBuffers
|
||||
<< ":MaxViewsOVR:" << mResources.MaxViewsOVR
|
||||
<< ":NV_draw_buffers:" << mResources.NV_draw_buffers
|
||||
@@ -1612,6 +1614,7 @@ void TCompiler::setResourceString()
|
||||
<< ":MaxFragmentAtomicCounterBuffers:" << mResources.MaxFragmentAtomicCounterBuffers
|
||||
<< ":MaxCombinedAtomicCounterBuffers:" << mResources.MaxCombinedAtomicCounterBuffers
|
||||
<< ":MaxAtomicCounterBufferSize:" << mResources.MaxAtomicCounterBufferSize
|
||||
+ << ":MaxComputeUnformBlocks:" << mResources.MaxComputeUniformBlocks
|
||||
<< ":MaxGeometryUniformComponents:" << mResources.MaxGeometryUniformComponents
|
||||
<< ":MaxGeometryUniformBlocks:" << mResources.MaxGeometryUniformBlocks
|
||||
<< ":MaxGeometryInputComponents:" << mResources.MaxGeometryInputComponents
|
||||
@@ -1635,6 +1638,7 @@ void TCompiler::setResourceString()
|
||||
<< ":MaxTessControlImageUniforms:" << mResources.MaxTessControlImageUniforms
|
||||
<< ":MaxTessControlAtomicCounters:" << mResources.MaxTessControlAtomicCounters
|
||||
<< ":MaxTessControlAtomicCounterBuffers:" << mResources.MaxTessControlAtomicCounterBuffers
|
||||
+ << ":MaxTessControlUniformBlocks:" << mResources.MaxTessControlUniformBlocks
|
||||
<< ":MaxTessPatchComponents:" << mResources.MaxTessPatchComponents
|
||||
<< ":MaxPatchVertices:" << mResources.MaxPatchVertices
|
||||
<< ":MaxTessGenLevel:" << mResources.MaxTessGenLevel
|
||||
@@ -1644,7 +1648,9 @@ void TCompiler::setResourceString()
|
||||
<< ":MaxTessEvaluationUniformComponents:" << mResources.MaxTessEvaluationUniformComponents
|
||||
<< ":MaxTessEvaluationImageUniforms:" << mResources.MaxTessEvaluationImageUniforms
|
||||
<< ":MaxTessEvaluationAtomicCounters:" << mResources.MaxTessEvaluationAtomicCounters
|
||||
- << ":MaxTessEvaluationAtomicCounterBuffers:" << mResources.MaxTessEvaluationAtomicCounterBuffers;
|
||||
+ << ":MaxTessEvaluationAtomicCounterBuffers:" << mResources.MaxTessEvaluationAtomicCounterBuffers
|
||||
+ << ":MaxTessControlUniformBlocks:" << mResources.MaxTessControlUniformBlocks
|
||||
+ ;
|
||||
// clang-format on
|
||||
|
||||
mBuiltInResourcesString = strstream.str();
|
||||
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
|
||||
index a8a5e562b2006402e1473c8c6710d75d7c83c42f..e04d27fe695caba55bb29f94c7425d4ecc3d2344 100644
|
||||
--- a/src/compiler/translator/ParseContext.cpp
|
||||
+++ b/src/compiler/translator/ParseContext.cpp
|
||||
@@ -367,6 +367,37 @@ bool IsESSL100ConstantExpression(TIntermNode *node)
|
||||
{
|
||||
return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst;
|
||||
}
|
||||
+
|
||||
+unsigned int GetMaxUniformBlocksForShaderType(sh::GLenum shaderType,
|
||||
+ const ShCompileOptions &options,
|
||||
+ const ShBuiltInResources &resources)
|
||||
+{
|
||||
+ // If the validatePerStageMaxUniformBlocks workaround is disabled. Set a limit that will not be
|
||||
+ // hit.
|
||||
+ if (!options.validatePerStageMaxUniformBlocks)
|
||||
+ {
|
||||
+ return std::numeric_limits<unsigned int>::max();
|
||||
+ }
|
||||
+
|
||||
+ switch (shaderType)
|
||||
+ {
|
||||
+ case GL_FRAGMENT_SHADER:
|
||||
+ return resources.MaxFragmentUniformBlocks;
|
||||
+ case GL_VERTEX_SHADER:
|
||||
+ return resources.MaxVertexUniformBlocks;
|
||||
+ case GL_COMPUTE_SHADER:
|
||||
+ return resources.MaxComputeUniformBlocks;
|
||||
+ case GL_GEOMETRY_SHADER:
|
||||
+ return resources.MaxGeometryUniformBlocks;
|
||||
+ case GL_TESS_CONTROL_SHADER:
|
||||
+ return resources.MaxTessControlUniformBlocks;
|
||||
+ case GL_TESS_EVALUATION_SHADER:
|
||||
+ return resources.MaxTessEvaluationUniformBlocks;
|
||||
+ default:
|
||||
+ UNREACHABLE();
|
||||
+ return 0;
|
||||
+ }
|
||||
+}
|
||||
} // namespace
|
||||
|
||||
// This tracks each binding point's current default offset for inheritance of subsequent
|
||||
@@ -459,6 +490,8 @@ TParseContext::TParseContext(TSymbolTable &symt,
|
||||
mMaxPixelLocalStoragePlanes(resources.MaxPixelLocalStoragePlanes),
|
||||
mMaxFunctionParameters(resources.MaxFunctionParameters),
|
||||
mMaxCallStackDepth(resources.MaxCallStackDepth),
|
||||
+ mMaxUniformBlocks(GetMaxUniformBlocksForShaderType(mShaderType, options, resources)),
|
||||
+ mNumUniformBlocks(0),
|
||||
mDeclaringFunction(false),
|
||||
mDeclaringMain(false),
|
||||
mMainFunction(nullptr),
|
||||
@@ -6082,6 +6115,22 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
|
||||
error(arraySizesLine, "geometry shader input blocks must be an array", "");
|
||||
}
|
||||
|
||||
+ // Validate max uniform block limits
|
||||
+ if (typeQualifier.qualifier == EvqUniform)
|
||||
+ {
|
||||
+ unsigned int blockCount =
|
||||
+ arraySizes == nullptr || arraySizes->empty() ? 1 : (*arraySizes)[0];
|
||||
+ if (mNumUniformBlocks + blockCount > mMaxUniformBlocks)
|
||||
+ {
|
||||
+ error(arraySizesLine,
|
||||
+ "uniform block count greater than per stage maximum uniform blocks", "");
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ mNumUniformBlocks += blockCount;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
checkIndexIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier.index);
|
||||
|
||||
if (mShaderVersion < 310)
|
||||
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
|
||||
index f3d0417dd91cda2226203b9b39c2dc8acc1ac880..baf074bd86fe587a72cd3a431fbf0231d6ebd68c 100644
|
||||
--- a/src/compiler/translator/ParseContext.h
|
||||
+++ b/src/compiler/translator/ParseContext.h
|
||||
@@ -910,6 +910,12 @@ class TParseContext : angle::NonCopyable
|
||||
// and there are no known users.
|
||||
TUnorderedMap<TQualifier, bool> mBuiltInQualified;
|
||||
|
||||
+ // Maximum number of uniform blocks allowed to be declared in this shader. Taken from the
|
||||
+ // built-in resources and resolved to this shader type.
|
||||
+ unsigned int mMaxUniformBlocks;
|
||||
+ // Current count of declared uniform blocks.
|
||||
+ unsigned int mNumUniformBlocks;
|
||||
+
|
||||
// keeps track whether we are declaring / defining a function
|
||||
bool mDeclaringFunction;
|
||||
|
||||
diff --git a/src/compiler/translator/ShaderLang.cpp b/src/compiler/translator/ShaderLang.cpp
|
||||
index 6044849e6a6392fed77909b45fea475fa4978325..e292cf0403a77f2f70ca9176785fcdad9aac7302 100644
|
||||
--- a/src/compiler/translator/ShaderLang.cpp
|
||||
+++ b/src/compiler/translator/ShaderLang.cpp
|
||||
@@ -263,6 +263,8 @@ void InitBuiltInResources(ShBuiltInResources *resources)
|
||||
resources->MaxFragmentInputVectors = 15;
|
||||
resources->MinProgramTexelOffset = -8;
|
||||
resources->MaxProgramTexelOffset = 7;
|
||||
+ resources->MaxFragmentUniformBlocks = 12;
|
||||
+ resources->MaxVertexUniformBlocks = 12;
|
||||
|
||||
// Extensions constants.
|
||||
resources->MaxDualSourceDrawBuffers = 0;
|
||||
@@ -323,6 +325,8 @@ void InitBuiltInResources(ShBuiltInResources *resources)
|
||||
resources->MaxUniformBufferBindings = 32;
|
||||
resources->MaxShaderStorageBufferBindings = 4;
|
||||
|
||||
+ resources->MaxComputeUniformBlocks = 12;
|
||||
+
|
||||
resources->MaxGeometryUniformComponents = 1024;
|
||||
resources->MaxGeometryUniformBlocks = 12;
|
||||
resources->MaxGeometryInputComponents = 64;
|
||||
@@ -344,6 +348,7 @@ void InitBuiltInResources(ShBuiltInResources *resources)
|
||||
resources->MaxTessControlImageUniforms = 0;
|
||||
resources->MaxTessControlAtomicCounters = 0;
|
||||
resources->MaxTessControlAtomicCounterBuffers = 0;
|
||||
+ resources->MaxTessControlUniformBlocks = 12;
|
||||
|
||||
resources->MaxTessPatchComponents = 120;
|
||||
resources->MaxPatchVertices = 32;
|
||||
@@ -356,6 +361,7 @@ void InitBuiltInResources(ShBuiltInResources *resources)
|
||||
resources->MaxTessEvaluationImageUniforms = 0;
|
||||
resources->MaxTessEvaluationAtomicCounters = 0;
|
||||
resources->MaxTessEvaluationAtomicCounterBuffers = 0;
|
||||
+ resources->MaxTessEvaluationUniformBlocks = 12;
|
||||
|
||||
resources->SubPixelBits = 8;
|
||||
|
||||
diff --git a/src/libANGLE/Compiler.cpp b/src/libANGLE/Compiler.cpp
|
||||
index 00684c8ed08609a3a4d6ef6f36107756207ed72b..1893b6bddb33fde567162e2e9dbb12785dad538d 100644
|
||||
--- a/src/libANGLE/Compiler.cpp
|
||||
+++ b/src/libANGLE/Compiler.cpp
|
||||
@@ -169,6 +169,8 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state, egl::Disp
|
||||
mResources.MaxFragmentInputVectors = caps.maxFragmentInputComponents / 4;
|
||||
mResources.MinProgramTexelOffset = caps.minProgramTexelOffset;
|
||||
mResources.MaxProgramTexelOffset = caps.maxProgramTexelOffset;
|
||||
+ mResources.MaxFragmentUniformBlocks = caps.maxShaderUniformBlocks[gl::ShaderType::Fragment];
|
||||
+ mResources.MaxVertexUniformBlocks = caps.maxShaderUniformBlocks[gl::ShaderType::Vertex];
|
||||
|
||||
// EXT_blend_func_extended
|
||||
mResources.EXT_blend_func_extended = extensions.blendFuncExtendedEXT;
|
||||
@@ -211,6 +213,7 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state, egl::Disp
|
||||
mResources.MaxCombinedImageUniforms = caps.maxCombinedImageUniforms;
|
||||
mResources.MaxCombinedShaderOutputResources = caps.maxCombinedShaderOutputResources;
|
||||
mResources.MaxUniformLocations = caps.maxUniformLocations;
|
||||
+ mResources.MaxComputeUniformBlocks = caps.maxShaderUniformBlocks[gl::ShaderType::Compute];
|
||||
|
||||
for (size_t index = 0u; index < 3u; ++index)
|
||||
{
|
||||
@@ -280,6 +283,8 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state, egl::Disp
|
||||
mResources.MaxTessControlAtomicCounters = caps.maxShaderAtomicCounters[ShaderType::TessControl];
|
||||
mResources.MaxTessControlAtomicCounterBuffers =
|
||||
caps.maxShaderAtomicCounterBuffers[ShaderType::TessControl];
|
||||
+ mResources.MaxTessControlUniformBlocks =
|
||||
+ caps.maxShaderUniformBlocks[gl::ShaderType::TessControl];
|
||||
|
||||
mResources.MaxTessPatchComponents = caps.maxTessPatchComponents;
|
||||
mResources.MaxPatchVertices = caps.maxPatchVertices;
|
||||
@@ -297,6 +302,8 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state, egl::Disp
|
||||
caps.maxShaderAtomicCounters[ShaderType::TessEvaluation];
|
||||
mResources.MaxTessEvaluationAtomicCounterBuffers =
|
||||
caps.maxShaderAtomicCounterBuffers[ShaderType::TessEvaluation];
|
||||
+ mResources.MaxTessEvaluationUniformBlocks =
|
||||
+ caps.maxShaderUniformBlocks[gl::ShaderType::TessEvaluation];
|
||||
|
||||
// Subpixel bits.
|
||||
mResources.SubPixelBits = static_cast<int>(caps.subPixelBits);
|
||||
diff --git a/src/libANGLE/renderer/gl/ShaderGL.cpp b/src/libANGLE/renderer/gl/ShaderGL.cpp
|
||||
index a10d5545f409faa259a464437401980e40b80e33..d8db2a1f92494fd0dbf0cb6011ab9cea989c5b45 100644
|
||||
--- a/src/libANGLE/renderer/gl/ShaderGL.cpp
|
||||
+++ b/src/libANGLE/renderer/gl/ShaderGL.cpp
|
||||
@@ -272,6 +272,11 @@ std::shared_ptr<ShaderTranslateTask> ShaderGL::compile(const gl::Context *contex
|
||||
options->pls = contextGL->getNativePixelLocalStorageOptions();
|
||||
}
|
||||
|
||||
+ if (features.validateMaxPerStageUniformBlocksAtCompileTime.enabled)
|
||||
+ {
|
||||
+ options->validatePerStageMaxUniformBlocks = true;
|
||||
+ }
|
||||
+
|
||||
return std::shared_ptr<ShaderTranslateTask>(
|
||||
new ShaderTranslateTaskGL(functions, mShaderID, contextGL->hasNativeParallelCompile()));
|
||||
}
|
||||
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
|
||||
index d9694c09146dcb34948646df5ca95a4d8b993d9e..e82a10262dc128e55d2357bf9efc67903c30206b 100644
|
||||
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
|
||||
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
|
||||
@@ -2718,6 +2718,10 @@ void InitializeFeatures(const FunctionsGL *functions, angle::FeaturesGL *feature
|
||||
// Mali 400 series drivers fail linking shaders when passthrough shaders are enabled. Likely due
|
||||
// to not querying correct information from varyings and uniforms.
|
||||
ANGLE_FEATURE_CONDITION(features, disablePassthroughShaders, IsAdreno4xx(functions));
|
||||
+
|
||||
+ // IMG GL drivers crash while compiling shaders with more than the limit of uniform blocks.
|
||||
+ ANGLE_FEATURE_CONDITION(features, validateMaxPerStageUniformBlocksAtCompileTime,
|
||||
+ IsPowerVR(vendor));
|
||||
}
|
||||
|
||||
void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFeatures *features)
|
||||
diff --git a/util/autogen/angle_features_autogen.cpp b/util/autogen/angle_features_autogen.cpp
|
||||
index f38ad24bdce73c1ec5facd1130bdf09f896298ee..c07c7e5fa2d28c50101c77bd856f18158777e276 100644
|
||||
--- a/util/autogen/angle_features_autogen.cpp
|
||||
+++ b/util/autogen/angle_features_autogen.cpp
|
||||
@@ -484,6 +484,7 @@ constexpr PackedEnumMap<Feature, const char *> kFeatureNames = {{
|
||||
{Feature::UseVkEventForBufferBarrier, "useVkEventForBufferBarrier"},
|
||||
{Feature::UseVkEventForImageBarrier, "useVkEventForImageBarrier"},
|
||||
{Feature::UseVmaForImageSuballocation, "useVmaForImageSuballocation"},
|
||||
+ {Feature::ValidateMaxPerStageUniformBlocksAtCompileTime, "validateMaxPerStageUniformBlocksAtCompileTime"},
|
||||
{Feature::VaryingsRequireMatchingPrecisionInSpirv, "varyingsRequireMatchingPrecisionInSpirv"},
|
||||
{Feature::VerifyPipelineCacheInBlobCache, "verifyPipelineCacheInBlobCache"},
|
||||
{Feature::VertexIDDoesNotIncludeBaseVertex, "vertexIDDoesNotIncludeBaseVertex"},
|
||||
diff --git a/util/autogen/angle_features_autogen.h b/util/autogen/angle_features_autogen.h
|
||||
index 0eef4d53d814db20bb943fcda9e5b1837c371951..e33fd47ac493a3946d87c03a2d6f29b08e2b63aa 100644
|
||||
--- a/util/autogen/angle_features_autogen.h
|
||||
+++ b/util/autogen/angle_features_autogen.h
|
||||
@@ -484,6 +484,7 @@ enum class Feature
|
||||
UseVkEventForBufferBarrier,
|
||||
UseVkEventForImageBarrier,
|
||||
UseVmaForImageSuballocation,
|
||||
+ ValidateMaxPerStageUniformBlocksAtCompileTime,
|
||||
VaryingsRequireMatchingPrecisionInSpirv,
|
||||
VerifyPipelineCacheInBlobCache,
|
||||
VertexIDDoesNotIncludeBaseVertex,
|
||||
@@ -145,4 +145,17 @@ viz_create_isbufferqueuesupportedandenabled.patch
|
||||
viz_fix_visual_artifacts_while_resizing_window_with_dcomp.patch
|
||||
fix_os_crypt_async_cookie_encryption.patch
|
||||
graphite_handle_out_of_order_recording_errors.patch
|
||||
cherry-pick-e045399a1ecb.patch
|
||||
move_wayland_pointer_lock_overrides_to_common_code.patch
|
||||
loaf_add_feature_to_enable_sourceurl_for_all_protocols.patch
|
||||
fix_update_dbus_signal_signature_for_xdg_globalshortcuts_portal.patch
|
||||
patch_osr_control_screen_info.patch
|
||||
cherry-pick-12f932985275.patch
|
||||
fix_mac_high_res_icons.patch
|
||||
cherry-pick-074d472db745.patch
|
||||
cherry-pick-50b057660b4d.patch
|
||||
cherry-pick-45c5a70d984d.patch
|
||||
cherry-pick-05e4b544803c.patch
|
||||
cherry-pick-5efc7a0127a6.patch
|
||||
feat_plumb_node_integration_in_worker_through_workersettings.patch
|
||||
cherry-pick-fbfb27470bf6.patch
|
||||
fix_fire_menu_popup_start_for_dynamically_created_aria_menus.patch
|
||||
|
||||
@@ -6,10 +6,10 @@ Subject: build: allow electron to use exec_script
|
||||
This is similar to the //build usecase so we're OK adding ourselves here
|
||||
|
||||
diff --git a/.gn b/.gn
|
||||
index ae58a0b0a64ae1fdb3f9cd8587041d71a121c6b9..0ed56526002b12deb6d29f3dd23a0d74d8e7473c 100644
|
||||
index ae58a0b0a64ae1fdb3f9cd8587041d71a121c6b9..f9d4e9b015ad266452dfa2a442b432ef31d09a5b 100644
|
||||
--- a/.gn
|
||||
+++ b/.gn
|
||||
@@ -167,4 +167,27 @@ exec_script_allowlist =
|
||||
@@ -167,4 +167,28 @@ exec_script_allowlist =
|
||||
|
||||
"//tools/grit/grit_rule.gni",
|
||||
"//tools/gritsettings/BUILD.gn",
|
||||
@@ -25,6 +25,7 @@ index ae58a0b0a64ae1fdb3f9cd8587041d71a121c6b9..0ed56526002b12deb6d29f3dd23a0d74
|
||||
+ "//third_party/electron_node/deps/googletest/unofficial.gni",
|
||||
+ "//third_party/electron_node/deps/histogram/unofficial.gni",
|
||||
+ "//third_party/electron_node/deps/llhttp/unofficial.gni",
|
||||
+ "//third_party/electron_node/deps/merve/unofficial.gni",
|
||||
+ "//third_party/electron_node/deps/nbytes/unofficial.gni",
|
||||
+ "//third_party/electron_node/deps/ncrypto/unofficial.gni",
|
||||
+ "//third_party/electron_node/deps/nghttp2/unofficial.gni",
|
||||
|
||||
@@ -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 edaf9a7b2efc5ed7f4e946d720de54d5001f44d4..7d27b076c1947d2cd08364f87286ed6d9f460cdc 100644
|
||||
index ce64151028cc30c81292326ee73126cb8415aec5..3feca12a6185afef139a0cb4a8148b5a3ca9e32f 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -9867,6 +9867,7 @@ void RenderFrameHostImpl::CreateNewWindow(
|
||||
@@ -9868,6 +9868,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,
|
||||
|
||||
204
patches/chromium/cherry-pick-05e4b544803c.patch
Normal file
204
patches/chromium/cherry-pick-05e4b544803c.patch
Normal file
@@ -0,0 +1,204 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Date: Wed, 25 Feb 2026 03:24:31 -0800
|
||||
Subject: Stringify CSSUnparsedValues via toString, as normal
|
||||
|
||||
CSSUnparsedValue exposes a special stringification function
|
||||
ToUnparsedString() in addition to the regular toString().
|
||||
The documentation says it returns "tokens without substituting
|
||||
variables", but it's not clear what this means; we don't substitute
|
||||
any variables in CSSStyleValue::toString() either.
|
||||
|
||||
This CL makes ToUnparsedString() private (and renames it).
|
||||
Clients needing to serialize a CSSUnparsedValue can do so via
|
||||
the normal toString() function. (If ToUnparsedString() existed
|
||||
for performance reasons, that should have been documented.)
|
||||
|
||||
Also, the /**/-"fixup" pass over the value has been folded into
|
||||
ToStringInternal(). This is to make it easy to find the canonical string
|
||||
representation of this value within CSSUnparsedValue (without going
|
||||
through a CSSValue).
|
||||
|
||||
The main point of this CL is to prepare for validating
|
||||
the "argument grammar" of the value during the StyleValue-to-CSSValue
|
||||
conversion in StylePropertyMap (which requires item (2) above).
|
||||
|
||||
We now jump through additional hoops to ultimately get a string
|
||||
from the outside of CSSUnparsedValue, but there should otherwise
|
||||
be no behavior change.
|
||||
|
||||
Bug: 484751092
|
||||
Change-Id: I5db45ad85f780c67a2ea3ba8482c390ebab10068
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7600415
|
||||
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1590041}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc b/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
|
||||
index dcc2eccbc84e6cd5710ab51cee2dab49661467c1..86d42c87a6bd10838a3e059c9227868e5bfc0798 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/cross_thread_style_value_test.cc
|
||||
@@ -19,12 +19,12 @@
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_style_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unit_value.h"
|
||||
-#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unsupported_color.h"
|
||||
#include "third_party/blink/renderer/platform/scheduler/public/non_main_thread.h"
|
||||
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_std.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
|
||||
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
@@ -152,8 +152,7 @@ TEST_F(CrossThreadStyleValueTest, CrossThreadUnparsedValueToCSSStyleValue) {
|
||||
CSSStyleValue* style_value = value->ToCSSStyleValue();
|
||||
EXPECT_EQ(style_value->GetType(),
|
||||
CSSStyleValue::StyleValueType::kUnparsedType);
|
||||
- EXPECT_EQ(static_cast<CSSUnparsedValue*>(style_value)->ToUnparsedString(),
|
||||
- "Unparsed");
|
||||
+ EXPECT_EQ(style_value->toString(), "Unparsed");
|
||||
}
|
||||
|
||||
TEST_F(CrossThreadStyleValueTest, PassKeywordValueCrossThread) {
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
index 567d4fad7436c24d4c42bc36ebfd7ee3641e3b90..12d70ed096cb1c509a2acf14b7f421273d833d0e 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
@@ -137,16 +137,26 @@ IndexedPropertySetterResult CSSUnparsedValue::AnonymousIndexedSetter(
|
||||
}
|
||||
|
||||
const CSSValue* CSSUnparsedValue::ToCSSValue() const {
|
||||
- String unparsed_string = ToUnparsedString();
|
||||
- CSSParserTokenStream stream(unparsed_string);
|
||||
+ String unparsed_string = ToStringInternal();
|
||||
|
||||
- if (stream.AtEnd()) {
|
||||
+ if (unparsed_string.IsNull()) {
|
||||
return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
|
||||
MakeGarbageCollected<CSSVariableData>());
|
||||
}
|
||||
|
||||
- // The string we just parsed has /**/ inserted between every token
|
||||
- // to make sure we get back the correct sequence of tokens.
|
||||
+ // TODO(crbug.com/985028): We should probably propagate the CSSParserContext
|
||||
+ // to here.
|
||||
+ return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
|
||||
+ CSSVariableData::Create(unparsed_string, false /* is_animation_tainted */,
|
||||
+ false /* is_attr_tainted */,
|
||||
+ false /* needs_variable_resolution */));
|
||||
+}
|
||||
+
|
||||
+String CSSUnparsedValue::ToStringInternal() const {
|
||||
+ String serialized = SerializeSegments();
|
||||
+
|
||||
+ // The serialization above defensively inserted /**/ between segments
|
||||
+ // to make sure that e.g. ['foo', 'bar'] does not collapse into 'foobar'.
|
||||
// The spec mentions nothing of the sort:
|
||||
// https://drafts.css-houdini.org/css-typed-om-1/#unparsedvalue-serialization
|
||||
//
|
||||
@@ -160,6 +170,10 @@ const CSSValue* CSSUnparsedValue::ToCSSValue() const {
|
||||
// the original contents of any comments will be lost, but Typed OM does
|
||||
// not have anywhere to store that kind of data, so it is expected.
|
||||
StringBuilder builder;
|
||||
+ CSSParserTokenStream stream(serialized);
|
||||
+ if (stream.AtEnd()) {
|
||||
+ return g_null_atom;
|
||||
+ }
|
||||
CSSParserToken token = stream.ConsumeRaw();
|
||||
token.Serialize(builder);
|
||||
while (!stream.Peek().IsEOF()) {
|
||||
@@ -169,17 +183,10 @@ const CSSValue* CSSUnparsedValue::ToCSSValue() const {
|
||||
token = stream.ConsumeRaw();
|
||||
token.Serialize(builder);
|
||||
}
|
||||
- String original_text = builder.ReleaseString();
|
||||
-
|
||||
- // TODO(crbug.com/985028): We should probably propagate the CSSParserContext
|
||||
- // to here.
|
||||
- return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
|
||||
- CSSVariableData::Create(original_text, false /* is_animation_tainted */,
|
||||
- false /* is_attr_tainted */,
|
||||
- false /* needs_variable_resolution */));
|
||||
+ return builder.ReleaseString();
|
||||
}
|
||||
|
||||
-String CSSUnparsedValue::ToUnparsedString() const {
|
||||
+String CSSUnparsedValue::SerializeSegments() const {
|
||||
StringBuilder builder;
|
||||
HeapHashSet<Member<const CSSUnparsedValue>> values_on_stack;
|
||||
if (AppendUnparsedString(builder, values_on_stack)) {
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
index 5d1961b170f14ae21ca8f69b3c3cd8af28f4478a..ec7e3ed708f406d7a61fdb370b2eed8a8297cffb 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
@@ -67,15 +67,9 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
CSSStyleValue::Trace(visitor);
|
||||
}
|
||||
|
||||
- // Unlike CSSStyleValue::toString(), this returns tokens without
|
||||
- // substituting variables. There are extra /**/ inserted between
|
||||
- // every token to ensure there are no ambiguities, which is fine
|
||||
- // because this value is never presented directly to the user
|
||||
- // (ToCSSValue() will parse to a token range and then re-serialize
|
||||
- // using extra /**/ only where needed).
|
||||
- String ToUnparsedString() const;
|
||||
-
|
||||
private:
|
||||
+ String ToStringInternal() const;
|
||||
+ String SerializeSegments() const;
|
||||
// Return 'false' if there is a cycle in the serialization.
|
||||
bool AppendUnparsedString(
|
||||
StringBuilder&,
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
|
||||
index f81fa39423a9235bc58e1600ca7a250affd3d9bb..2ee4dd7e591095b8460ca559b29b78e37ab71729 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map_test.cc
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h"
|
||||
|
||||
#include <memory>
|
||||
+
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
@@ -13,7 +14,6 @@
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_paint_worklet_input.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unit_value.h"
|
||||
-#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unsupported_color.h"
|
||||
#include "third_party/blink/renderer/core/css/properties/longhands/custom_property.h"
|
||||
#include "third_party/blink/renderer/core/dom/element.h"
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
|
||||
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
@@ -66,8 +67,7 @@ class PaintWorkletStylePropertyMapTest : public PageTestBase {
|
||||
CSSStyleValue* style_value = data.at("--x")->ToCSSStyleValue();
|
||||
EXPECT_EQ(style_value->GetType(),
|
||||
CSSStyleValue::StyleValueType::kUnparsedType);
|
||||
- EXPECT_EQ(static_cast<CSSUnparsedValue*>(style_value)->ToUnparsedString(),
|
||||
- "50");
|
||||
+ EXPECT_EQ(style_value->toString(), "50");
|
||||
waitable_event->Signal();
|
||||
}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
|
||||
index 79b292f72efe32e6b56971ea577481710b0c750c..8b0c9f73656d664b04b640016391965009b667d6 100644
|
||||
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
|
||||
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
|
||||
@@ -5059,7 +5059,7 @@ ComputedStyleUtils::CrossThreadStyleValueFromCSSStyleValue(
|
||||
To<CSSUnsupportedColor>(style_value)->Value());
|
||||
case CSSStyleValue::StyleValueType::kUnparsedType:
|
||||
return std::make_unique<CrossThreadUnparsedValue>(
|
||||
- To<CSSUnparsedValue>(style_value)->ToUnparsedString());
|
||||
+ To<CSSUnparsedValue>(style_value)->toString());
|
||||
default:
|
||||
return std::make_unique<CrossThreadUnsupportedValue>(
|
||||
style_value->toString());
|
||||
296
patches/chromium/cherry-pick-074d472db745.patch
Normal file
296
patches/chromium/cherry-pick-074d472db745.patch
Normal file
@@ -0,0 +1,296 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Mikel Astiz <mastiz@chromium.org>
|
||||
Date: Tue, 10 Mar 2026 13:22:17 -0700
|
||||
Subject: [M146][base] Fix UAF in base::OnceCallbackList on re-entrant Notify()
|
||||
|
||||
Before this patch, `base::OnceCallbackList` was susceptible to a
|
||||
heap-use-after-free when `Notify()` was called re-entrantly.
|
||||
|
||||
The UAF occurred because `OnceCallbackList::RunCallback()` immediately
|
||||
spliced executed nodes out of `callbacks_` and into `null_callbacks_`.
|
||||
If a nested `Notify()` executed a node that an outer `Notify()` loop was
|
||||
already holding an iterator to, and that node's subscription was
|
||||
subsequently destroyed during the re-entrant cycle, the node would be
|
||||
physically erased from `null_callbacks_`. When control returned to the
|
||||
outer loop, it would attempt to evaluate the now-dangling iterator.
|
||||
|
||||
This CL fixes the bug by deferring list mutations until the outermost
|
||||
iteration completes:
|
||||
1. `RunCallback()` no longer splices nodes during iteration.
|
||||
2. Cancellation logic is pushed down to the subclasses via a new
|
||||
`CancelCallback()` hook, which is an extension to the pre-existing
|
||||
`CancelNullCallback()` with increased responsibilities and clearer
|
||||
semantics.
|
||||
3. If a subscription is destroyed while `is_iterating` is true,
|
||||
`OnceCallbackList` resets the node and stashes its iterator in
|
||||
`pending_erasures_`.
|
||||
4. A new `CleanUpNullCallbacksPostIteration()` phase runs at the end
|
||||
of the outermost `Notify()`, which safely splices executed nodes
|
||||
into `null_callbacks_` and physically erases the pending dead nodes.
|
||||
|
||||
As a side effect, the type-trait hack in `Notify()` based on
|
||||
`is_instantiation<CallbackType, OnceCallback>` can be removed, because
|
||||
this information is exposed directly by
|
||||
`OnceCallbackList::CleanUpNullCallbacksPostIteration()`.
|
||||
|
||||
The newly-added unit-test
|
||||
CallbackListTest.OnceCallbackListCancelDuringReentrantNotify reproduces
|
||||
the scenario and crashed before this patch.
|
||||
|
||||
(cherry picked from commit 36acd49636845be2419269acbe9a5137da3d5d96)
|
||||
|
||||
Change-Id: I6b1e2bcb97be1bc8d6a15e5ca7511992e00e1772
|
||||
Fixed: 489381399
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7627506
|
||||
Commit-Queue: Mikel Astiz <mastiz@chromium.org>
|
||||
Reviewed-by: Gabriel Charette <gab@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1594520}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7653916
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Cr-Commit-Position: refs/branch-heads/7680@{#2287}
|
||||
Cr-Branched-From: 76b7d80e5cda23fe6537eed26d68c92e995c7f39-refs/heads/main@{#1582197}
|
||||
|
||||
diff --git a/base/callback_list.h b/base/callback_list.h
|
||||
index 82cb11dc0ee02906b009cc383c41a056861199d0..d5f99cf685486f1ea74718b4e6b228a5d83f0c29 100644
|
||||
--- a/base/callback_list.h
|
||||
+++ b/base/callback_list.h
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
+#include <vector>
|
||||
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/base_export.h"
|
||||
@@ -16,7 +17,6 @@
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
-#include "base/types/is_instantiation.h"
|
||||
|
||||
// OVERVIEW:
|
||||
//
|
||||
@@ -240,17 +240,14 @@ class CallbackListBase {
|
||||
|
||||
// Any null callbacks remaining in the list were canceled due to
|
||||
// Subscription destruction during iteration, and can safely be erased now.
|
||||
- const size_t erased_callbacks =
|
||||
- std::erase_if(callbacks_, [](const auto& cb) { return cb.is_null(); });
|
||||
-
|
||||
- // Run |removal_callback_| if any callbacks were canceled. Note that we
|
||||
- // cannot simply compare list sizes before and after iterating, since
|
||||
- // notification may result in Add()ing new callbacks as well as canceling
|
||||
- // them. Also note that if this is a OnceCallbackList, the OnceCallbacks
|
||||
- // that were executed above have all been removed regardless of whether
|
||||
- // they're counted in |erased_callbacks_|.
|
||||
- if (removal_callback_ &&
|
||||
- (erased_callbacks || is_instantiation<CallbackType, OnceCallback>)) {
|
||||
+ const bool any_callbacks_erased = static_cast<CallbackListImpl*>(this)
|
||||
+ ->CleanUpNullCallbacksPostIteration();
|
||||
+
|
||||
+ // Run |removal_callback_| if any callbacks were canceled or executed. Note
|
||||
+ // that simply comparing list sizes before and after iterating cannot be
|
||||
+ // done, since notification may result in Add()ing new callbacks as well as
|
||||
+ // canceling them.
|
||||
+ if (removal_callback_ && any_callbacks_erased) {
|
||||
removal_callback_.Run(); // May delete |this|!
|
||||
}
|
||||
}
|
||||
@@ -264,21 +261,9 @@ class CallbackListBase {
|
||||
private:
|
||||
// Cancels the callback pointed to by |it|, which is guaranteed to be valid.
|
||||
void CancelCallback(const typename Callbacks::iterator& it) {
|
||||
- if (static_cast<CallbackListImpl*>(this)->CancelNullCallback(it)) {
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- if (iterating_) {
|
||||
- // Calling erase() here is unsafe, since the loop in Notify() may be
|
||||
- // referencing this same iterator, e.g. if adjacent callbacks'
|
||||
- // Subscriptions are both destroyed when the first one is Run(). Just
|
||||
- // reset the callback and let Notify() clean it up at the end.
|
||||
- it->Reset();
|
||||
- } else {
|
||||
- callbacks_.erase(it);
|
||||
- if (removal_callback_) {
|
||||
- removal_callback_.Run(); // May delete |this|!
|
||||
- }
|
||||
+ if (static_cast<CallbackListImpl*>(this)->CancelCallback(it, iterating_) &&
|
||||
+ removal_callback_) {
|
||||
+ removal_callback_.Run(); // May delete |this|!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,23 +289,71 @@ class OnceCallbackList
|
||||
// Runs the current callback, which may cancel it or any other callbacks.
|
||||
template <typename... RunArgs>
|
||||
void RunCallback(typename Traits::Callbacks::iterator it, RunArgs&&... args) {
|
||||
- // OnceCallbacks still have Subscriptions with outstanding iterators;
|
||||
- // splice() removes them from |callbacks_| without invalidating those.
|
||||
- null_callbacks_.splice(null_callbacks_.end(), this->callbacks_, it);
|
||||
+ // Do not splice here. Splicing during iteration breaks re-entrant Notify()
|
||||
+ // by invalidating the outer loop's iterator. Splicing is deferred to
|
||||
+ // CleanUpNullCallbacksPostIteration(), which is called when the outermost
|
||||
+ // Notify() finishes.
|
||||
|
||||
// NOTE: Intentionally does not call std::forward<RunArgs>(args)...; see
|
||||
// comments in Notify().
|
||||
std::move(*it).Run(args...);
|
||||
}
|
||||
|
||||
- // If |it| refers to an already-canceled callback, does any necessary cleanup
|
||||
- // and returns true. Otherwise returns false.
|
||||
- bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) {
|
||||
+ // Called during subscription destruction to cancel the callback. Returns true
|
||||
+ // if the callback was removed from the active list and the generic removal
|
||||
+ // callback should be executed. Returns false if the callback was already
|
||||
+ // executed, or if the erasure is deferred due to active iteration.
|
||||
+ bool CancelCallback(const typename Traits::Callbacks::iterator& it,
|
||||
+ bool is_iterating) {
|
||||
+ if (is_iterating) {
|
||||
+ // During iteration, nodes cannot be safely erased from |callbacks_|
|
||||
+ // without invalidating iterators. They also cannot be spliced into
|
||||
+ // |null_callbacks_| right now. Thus, the node is reset and tracked for
|
||||
+ // erasure in CleanUpNullCallbacksPostIteration().
|
||||
+ it->Reset();
|
||||
+ pending_erasures_.push_back(it);
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
if (it->is_null()) {
|
||||
+ // The callback already ran, so it's safely sitting in |null_callbacks_|.
|
||||
null_callbacks_.erase(it);
|
||||
- return true;
|
||||
+ return false;
|
||||
}
|
||||
- return false;
|
||||
+
|
||||
+ // The callback hasn't run yet, so it's still in |callbacks_|.
|
||||
+ this->callbacks_.erase(it);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // Performs post-iteration cleanup. Successfully executed callbacks (which
|
||||
+ // become null) are spliced into |null_callbacks_| to keep their
|
||||
+ // Subscriptions' iterators valid. Callbacks explicitly canceled during
|
||||
+ // iteration (tracked in |pending_erasures_|) are erased. Returns true if any
|
||||
+ // callbacks were erased or spliced out.
|
||||
+ bool CleanUpNullCallbacksPostIteration() {
|
||||
+ bool any_spliced = false;
|
||||
+ for (auto it = this->callbacks_.begin(); it != this->callbacks_.end();) {
|
||||
+ if (it->is_null()) {
|
||||
+ any_spliced = true;
|
||||
+ auto next = std::next(it);
|
||||
+ null_callbacks_.splice(null_callbacks_.end(), this->callbacks_, it);
|
||||
+ it = next;
|
||||
+ } else {
|
||||
+ ++it;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ bool any_erased = !pending_erasures_.empty();
|
||||
+ for (auto pending_it : pending_erasures_) {
|
||||
+ // Note: `pending_it` was originally an iterator into `callbacks_`, but
|
||||
+ // the node it points to has just been spliced into `null_callbacks_`. The
|
||||
+ // iterator itself remains valid and can now be used for erasure from
|
||||
+ // `null_callbacks_`.
|
||||
+ null_callbacks_.erase(pending_it);
|
||||
+ }
|
||||
+ pending_erasures_.clear();
|
||||
+ return any_spliced || any_erased;
|
||||
}
|
||||
|
||||
// Holds null callbacks whose Subscriptions are still alive, so the
|
||||
@@ -328,6 +361,11 @@ class OnceCallbackList
|
||||
// OnceCallbacks, since RepeatingCallbacks are not canceled except by
|
||||
// Subscription destruction.
|
||||
typename Traits::Callbacks null_callbacks_;
|
||||
+
|
||||
+ // Holds iterators for callbacks canceled during iteration.
|
||||
+ // Erasure is deferred to CleanUpNullCallbacksPostIteration() when iteration
|
||||
+ // completes to prevent invalidating iterators that an outer loop might hold.
|
||||
+ std::vector<typename Traits::Callbacks::iterator> pending_erasures_;
|
||||
};
|
||||
|
||||
template <typename Signature>
|
||||
@@ -344,14 +382,29 @@ class RepeatingCallbackList
|
||||
it->Run(args...);
|
||||
}
|
||||
|
||||
- // If |it| refers to an already-canceled callback, does any necessary cleanup
|
||||
- // and returns true. Otherwise returns false.
|
||||
- bool CancelNullCallback(const typename Traits::Callbacks::iterator& it) {
|
||||
- // Because at most one Subscription can point to a given callback, and
|
||||
- // RepeatingCallbacks are only reset by CancelCallback(), no one should be
|
||||
- // able to request cancellation of a canceled RepeatingCallback.
|
||||
- DCHECK(!it->is_null());
|
||||
- return false;
|
||||
+ // Called during subscription destruction to cancel the callback. Returns true
|
||||
+ // if the callback was removed from the active list and the generic removal
|
||||
+ // callback should be executed. Returns false if the callback was already
|
||||
+ // executed, or if the erasure is deferred due to active iteration.
|
||||
+ bool CancelCallback(const typename Traits::Callbacks::iterator& it,
|
||||
+ bool is_iterating) {
|
||||
+ if (is_iterating) {
|
||||
+ // During iteration, nodes cannot be safely erased from |callbacks_|
|
||||
+ // without invalidating iterators. The node is reset and will be swept up
|
||||
+ // by CleanUpNullCallbacksPostIteration().
|
||||
+ it->Reset();
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ this->callbacks_.erase(it);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // Performs post-iteration cleanup by erasing all canceled callbacks. Returns
|
||||
+ // true if any callbacks were erased.
|
||||
+ bool CleanUpNullCallbacksPostIteration() {
|
||||
+ return std::erase_if(this->callbacks_,
|
||||
+ [](const auto& cb) { return cb.is_null(); }) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
diff --git a/base/callback_list_unittest.cc b/base/callback_list_unittest.cc
|
||||
index 7474278525e5efecc0de903809a54d366896d524..a855443fbae862befbc3a2a484ea335632136e94 100644
|
||||
--- a/base/callback_list_unittest.cc
|
||||
+++ b/base/callback_list_unittest.cc
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback_helpers.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
+#include "base/test/bind.h"
|
||||
#include "base/test/test_future.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
@@ -577,6 +578,30 @@ TEST(CallbackListTest, ReentrantNotify) {
|
||||
EXPECT_EQ(1, d.total());
|
||||
}
|
||||
|
||||
+// Regression test for crbug.com/489381399: Verifies Notify() can be called
|
||||
+// reentrantly for OnceCallbackList even if a callback is canceled during the
|
||||
+// reentrant notification.
|
||||
+TEST(CallbackListTest, OnceCallbackListCancelDuringReentrantNotify) {
|
||||
+ OnceClosureList cb_reg;
|
||||
+ CallbackListSubscription sub_a, sub_b;
|
||||
+
|
||||
+ auto cb_a = base::BindLambdaForTesting([&]() {
|
||||
+ // Re-entrant notification.
|
||||
+ cb_reg.Notify();
|
||||
+ // After re-entrant notification returns, sub_b has been run. Destroying it
|
||||
+ // now should be a no-op.
|
||||
+ sub_b = {};
|
||||
+ });
|
||||
+
|
||||
+ auto cb_b = base::DoNothing();
|
||||
+
|
||||
+ sub_a = cb_reg.Add(std::move(cb_a));
|
||||
+ sub_b = cb_reg.Add(std::move(cb_b));
|
||||
+
|
||||
+ // This should not crash.
|
||||
+ cb_reg.Notify();
|
||||
+}
|
||||
+
|
||||
TEST(CallbackListTest, ClearPreventsInvocation) {
|
||||
Listener listener;
|
||||
RepeatingClosureList cb_reg;
|
||||
762
patches/chromium/cherry-pick-12f932985275.patch
Normal file
762
patches/chromium/cherry-pick-12f932985275.patch
Normal file
@@ -0,0 +1,762 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Geoff Lang <geofflang@chromium.org>
|
||||
Date: Wed, 11 Feb 2026 08:05:52 -0800
|
||||
Subject: Ensure the previous complete fbo is not deleted on IMG.
|
||||
|
||||
Change-Id: I7d84833312749fc58ecb511b276ff6bd783af1ba
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7533383
|
||||
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
|
||||
Commit-Queue: Geoff Lang <geofflang@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1583241}
|
||||
|
||||
diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc
|
||||
index 9a913cdefd0681d9972a83d8f940a3c48af0f46d..4a3a68b4415b6cc05eab01f98b1a0afffe73afc9 100644
|
||||
--- a/gpu/command_buffer/service/context_group.cc
|
||||
+++ b/gpu/command_buffer/service/context_group.cc
|
||||
@@ -120,9 +120,17 @@ ContextGroup::ContextGroup(
|
||||
|
||||
use_passthrough_cmd_decoder_ = gpu_preferences_.use_passthrough_cmd_decoder;
|
||||
}
|
||||
-
|
||||
gpu::ContextResult ContextGroup::Initialize(DecoderContext* decoder,
|
||||
ContextType context_type) {
|
||||
+ return InitializeWithCompleteFramebufferForWorkarounds(decoder, context_type,
|
||||
+ 0);
|
||||
+}
|
||||
+
|
||||
+gpu::ContextResult
|
||||
+ContextGroup::InitializeWithCompleteFramebufferForWorkarounds(
|
||||
+ DecoderContext* decoder,
|
||||
+ ContextType context_type,
|
||||
+ uint32_t complete_fbo_for_workarounds) {
|
||||
switch (context_type) {
|
||||
case CONTEXT_TYPE_WEBGL1:
|
||||
if (kGpuFeatureStatusBlocklisted ==
|
||||
@@ -156,8 +164,9 @@ gpu::ContextResult ContextGroup::Initialize(DecoderContext* decoder,
|
||||
DisallowedFeatures adjusted_disallowed_features =
|
||||
GetDisallowedFeatures(context_type);
|
||||
|
||||
- feature_info_->Initialize(context_type, use_passthrough_cmd_decoder_,
|
||||
- adjusted_disallowed_features);
|
||||
+ feature_info_->InitializeWithCompleteFramebufferForWorkarounds(
|
||||
+ context_type, use_passthrough_cmd_decoder_, adjusted_disallowed_features,
|
||||
+ complete_fbo_for_workarounds);
|
||||
|
||||
// Fail early if ES3 is requested and driver does not support it.
|
||||
if ((context_type == CONTEXT_TYPE_WEBGL2 ||
|
||||
diff --git a/gpu/command_buffer/service/context_group.h b/gpu/command_buffer/service/context_group.h
|
||||
index 78ea1ccd987cfc6f8ec0e378f906fb19fdccbc19..051d8126c128c47af71d0862cc0ec9476414825e 100644
|
||||
--- a/gpu/command_buffer/service/context_group.h
|
||||
+++ b/gpu/command_buffer/service/context_group.h
|
||||
@@ -72,7 +72,10 @@ class GPU_GLES2_EXPORT ContextGroup : public base::RefCounted<ContextGroup> {
|
||||
// call to destroy if it succeeds.
|
||||
gpu::ContextResult Initialize(DecoderContext* decoder,
|
||||
ContextType context_type);
|
||||
-
|
||||
+ gpu::ContextResult InitializeWithCompleteFramebufferForWorkarounds(
|
||||
+ DecoderContext* decoder,
|
||||
+ ContextType context_type,
|
||||
+ uint32_t complete_fbo_for_workarounds);
|
||||
// Destroys all the resources when called for the last context in the group.
|
||||
// It should only be called by DecoderContext.
|
||||
void Destroy(DecoderContext* decoder, bool have_context);
|
||||
diff --git a/gpu/command_buffer/service/decoder_context.h b/gpu/command_buffer/service/decoder_context.h
|
||||
index f00ad2459ff2b4993af37ac6fe0a5aa661fee692..96edc25e2d1ce364470b75ec1a3658ed0e217e54 100644
|
||||
--- a/gpu/command_buffer/service/decoder_context.h
|
||||
+++ b/gpu/command_buffer/service/decoder_context.h
|
||||
@@ -139,6 +139,12 @@ class GPU_GLES2_EXPORT DecoderContext : public AsyncAPIInterface,
|
||||
virtual gles2::ContextGroup* GetContextGroup() = 0;
|
||||
virtual gles2::ErrorState* GetErrorState() = 0;
|
||||
|
||||
+ //
|
||||
+ // Methods required by GLES2 Decoder helpers
|
||||
+ //
|
||||
+ // Bind the framebuffer `service_id` and perform any workarounds needed.
|
||||
+ virtual void BindFramebuffer(unsigned target, uint32_t service_id) const = 0;
|
||||
+
|
||||
//
|
||||
// Methods required by Texture.
|
||||
//
|
||||
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc
|
||||
index 14bf6c1a220f7b37fa81ddbc7ac1f2a16fb08e4c..6f1cb7f75f8bc4b80d1bd95be721285bd0bab1a8 100644
|
||||
--- a/gpu/command_buffer/service/feature_info.cc
|
||||
+++ b/gpu/command_buffer/service/feature_info.cc
|
||||
@@ -64,7 +64,8 @@ class ScopedPixelUnpackBufferOverride {
|
||||
|
||||
bool IsWebGLDrawBuffersSupported(bool webglCompatibilityContext,
|
||||
GLenum depth_texture_internal_format,
|
||||
- GLenum depth_stencil_texture_internal_format) {
|
||||
+ GLenum depth_stencil_texture_internal_format,
|
||||
+ GLuint complete_fbo_for_workarounds) {
|
||||
// This is called after we make sure GL_EXT_draw_buffers is supported.
|
||||
GLint max_draw_buffers = 0;
|
||||
GLint max_color_attachments = 0;
|
||||
@@ -81,6 +82,9 @@ bool IsWebGLDrawBuffersSupported(bool webglCompatibilityContext,
|
||||
|
||||
GLuint fbo;
|
||||
glGenFramebuffersEXT(1, &fbo);
|
||||
+ if (complete_fbo_for_workarounds) {
|
||||
+ glBindFramebufferEXT(GL_FRAMEBUFFER, complete_fbo_for_workarounds);
|
||||
+ }
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
GLuint depth_stencil_texture = 0;
|
||||
@@ -157,6 +161,9 @@ bool IsWebGLDrawBuffersSupported(bool webglCompatibilityContext,
|
||||
}
|
||||
}
|
||||
|
||||
+ if (complete_fbo_for_workarounds) {
|
||||
+ glBindFramebufferEXT(GL_FRAMEBUFFER, complete_fbo_for_workarounds);
|
||||
+ }
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER, static_cast<GLuint>(fb_binding));
|
||||
glDeleteFramebuffersEXT(1, &fbo);
|
||||
|
||||
@@ -238,6 +245,15 @@ void FeatureInfo::InitializeBasicState(const base::CommandLine* command_line) {
|
||||
void FeatureInfo::Initialize(ContextType context_type,
|
||||
bool is_passthrough_cmd_decoder,
|
||||
const DisallowedFeatures& disallowed_features) {
|
||||
+ InitializeWithCompleteFramebufferForWorkarounds(
|
||||
+ context_type, is_passthrough_cmd_decoder, disallowed_features, 0);
|
||||
+}
|
||||
+
|
||||
+void FeatureInfo::InitializeWithCompleteFramebufferForWorkarounds(
|
||||
+ ContextType context_type,
|
||||
+ bool is_passthrough_cmd_decoder,
|
||||
+ const DisallowedFeatures& disallowed_features,
|
||||
+ unsigned complete_fbo_for_workarounds) {
|
||||
if (initialized_) {
|
||||
DCHECK_EQ(context_type, context_type_);
|
||||
DCHECK_EQ(is_passthrough_cmd_decoder, is_passthrough_cmd_decoder_);
|
||||
@@ -248,14 +264,14 @@ void FeatureInfo::Initialize(ContextType context_type,
|
||||
disallowed_features_ = disallowed_features;
|
||||
context_type_ = context_type;
|
||||
is_passthrough_cmd_decoder_ = is_passthrough_cmd_decoder;
|
||||
- InitializeFeatures();
|
||||
+ InitializeFeatures(complete_fbo_for_workarounds);
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
void FeatureInfo::ForceReinitialize() {
|
||||
CHECK(initialized_);
|
||||
CHECK(is_passthrough_cmd_decoder_);
|
||||
- InitializeFeatures();
|
||||
+ InitializeFeatures(0);
|
||||
}
|
||||
|
||||
void FeatureInfo::InitializeForTesting(
|
||||
@@ -277,7 +293,7 @@ void FeatureInfo::InitializeForTesting(ContextType context_type) {
|
||||
DisallowedFeatures());
|
||||
}
|
||||
|
||||
-bool IsGL_REDSupportedOnFBOs() {
|
||||
+bool IsGL_REDSupportedOnFBOs(uint32_t complete_fbo_for_workarounds) {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
// The glTexImage2D call below can hang on Mac so skip this since it's only
|
||||
// really needed to workaround a Mesa issue. See https://crbug.com/1158744.
|
||||
@@ -311,6 +327,9 @@ bool IsGL_REDSupportedOnFBOs() {
|
||||
GL_UNSIGNED_BYTE, nullptr);
|
||||
GLuint textureFBOID = 0;
|
||||
glGenFramebuffersEXT(1, &textureFBOID);
|
||||
+ if (complete_fbo_for_workarounds) {
|
||||
+ glBindFramebufferEXT(GL_FRAMEBUFFER, complete_fbo_for_workarounds);
|
||||
+ }
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER, textureFBOID);
|
||||
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
textureId, 0);
|
||||
@@ -319,6 +338,9 @@ bool IsGL_REDSupportedOnFBOs() {
|
||||
glDeleteFramebuffersEXT(1, &textureFBOID);
|
||||
glDeleteTextures(1, &textureId);
|
||||
|
||||
+ if (complete_fbo_for_workarounds) {
|
||||
+ glBindFramebufferEXT(GL_FRAMEBUFFER, complete_fbo_for_workarounds);
|
||||
+ }
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER, static_cast<GLuint>(fb_binding));
|
||||
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(tex_binding));
|
||||
|
||||
@@ -476,7 +498,7 @@ void FeatureInfo::EnableWEBGLMultiDrawIfPossible(
|
||||
}
|
||||
}
|
||||
|
||||
-void FeatureInfo::InitializeFeatures() {
|
||||
+void FeatureInfo::InitializeFeatures(uint32_t complete_fbo_for_workarounds) {
|
||||
// Figure out what extensions to turn on.
|
||||
std::string extensions_string(gl::GetGLExtensionsFromCurrentContext());
|
||||
gfx::ExtensionSet extensions(gfx::MakeExtensionSet(extensions_string));
|
||||
@@ -1264,9 +1286,9 @@ void FeatureInfo::InitializeFeatures() {
|
||||
can_emulate_es2_draw_buffers_on_es3_nv) &&
|
||||
(context_type_ == CONTEXT_TYPE_OPENGLES2 ||
|
||||
(context_type_ == CONTEXT_TYPE_WEBGL1 &&
|
||||
- IsWebGLDrawBuffersSupported(is_webgl_compatibility_context,
|
||||
- depth_texture_format,
|
||||
- depth_stencil_texture_format)));
|
||||
+ IsWebGLDrawBuffersSupported(
|
||||
+ is_webgl_compatibility_context, depth_texture_format,
|
||||
+ depth_stencil_texture_format, complete_fbo_for_workarounds)));
|
||||
if (have_es2_draw_buffers) {
|
||||
AddExtensionString("GL_EXT_draw_buffers");
|
||||
feature_flags_.ext_draw_buffers = true;
|
||||
@@ -1387,7 +1409,7 @@ void FeatureInfo::InitializeFeatures() {
|
||||
|
||||
if ((gl_version_info_->is_es3 ||
|
||||
gfx::HasExtension(extensions, "GL_EXT_texture_rg")) &&
|
||||
- IsGL_REDSupportedOnFBOs()) {
|
||||
+ IsGL_REDSupportedOnFBOs(complete_fbo_for_workarounds)) {
|
||||
feature_flags_.ext_texture_rg = true;
|
||||
AddExtensionString("GL_EXT_texture_rg");
|
||||
|
||||
diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h
|
||||
index 2db3588ca3ed729799b113350ea8a7c449712587..83c683e900a3267061ced97ba971bf9dc0b88f4f 100644
|
||||
--- a/gpu/command_buffer/service/feature_info.h
|
||||
+++ b/gpu/command_buffer/service/feature_info.h
|
||||
@@ -163,6 +163,14 @@ class GPU_GLES2_EXPORT FeatureInfo : public base::RefCounted<FeatureInfo> {
|
||||
bool is_passthrough_cmd_decoder,
|
||||
const DisallowedFeatures& disallowed_features);
|
||||
|
||||
+ // Same as initialize but with a provided `complete_fbo_for_workarounds` to
|
||||
+ // use with the ensure_previous_framebuffer_not_deleted driver bug workaround.
|
||||
+ void InitializeWithCompleteFramebufferForWorkarounds(
|
||||
+ ContextType context_type,
|
||||
+ bool is_passthrough_cmd_decoder,
|
||||
+ const DisallowedFeatures& disallowed_features,
|
||||
+ uint32_t complete_fbo_for_workarounds);
|
||||
+
|
||||
// Same as above, but allows reinitialization.
|
||||
void ForceReinitialize();
|
||||
|
||||
@@ -250,7 +258,7 @@ class GPU_GLES2_EXPORT FeatureInfo : public base::RefCounted<FeatureInfo> {
|
||||
|
||||
void AddExtensionString(std::string_view s);
|
||||
void InitializeBasicState(const base::CommandLine* command_line);
|
||||
- void InitializeFeatures();
|
||||
+ void InitializeFeatures(uint32_t complete_fbo_for_workarounds);
|
||||
void InitializeFloatAndHalfFloatFeatures(const gfx::ExtensionSet& extensions);
|
||||
|
||||
void EnableANGLEInstancedArrayIfPossible(const gfx::ExtensionSet& extensions);
|
||||
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc b/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
|
||||
index b500acb4fe7cbff0e84a4e52e66459d5b85fdf75..a08bb3ac4ab625f220865803352e206713763d77 100644
|
||||
--- a/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
|
||||
+++ b/gpu/command_buffer/service/gles2_cmd_copy_tex_image.cc
|
||||
@@ -185,7 +185,7 @@ void CopyTexImageResourceManager::DoCopyTexSubImageToLUMACompatibilityTexture(
|
||||
// framebuffer is copying from a texture and sample directly from that texture
|
||||
// instead of doing an extra copy
|
||||
|
||||
- glBindFramebufferEXT(GL_FRAMEBUFFER, source_framebuffer);
|
||||
+ decoder->BindFramebuffer(GL_FRAMEBUFFER, source_framebuffer);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, scratch_textures_[0]);
|
||||
glCopyTexImage2D(GL_TEXTURE_2D, 0, source_framebuffer_internal_format, x, y,
|
||||
@@ -217,7 +217,7 @@ void CopyTexImageResourceManager::DoCopyTexSubImageToLUMACompatibilityTexture(
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, compatability_format, width, height, 0,
|
||||
compatability_format, luma_type, nullptr);
|
||||
|
||||
- glBindFramebufferEXT(GL_FRAMEBUFFER, scratch_fbo_);
|
||||
+ decoder->BindFramebuffer(GL_FRAMEBUFFER, scratch_fbo_);
|
||||
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
scratch_textures_[1], 0);
|
||||
|
||||
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
|
||||
index 469642028b839d490199379254a5a44c2fcd7f02..98f476de9e4d423b6ec86f2a69638e43bd0fb423 100644
|
||||
--- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
|
||||
+++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
|
||||
@@ -491,7 +491,8 @@ void DeleteShader(GLuint shader) {
|
||||
glDeleteShader(shader);
|
||||
}
|
||||
|
||||
-bool BindFramebufferTexture2D(GLenum target,
|
||||
+bool BindFramebufferTexture2D(DecoderContext* decoder,
|
||||
+ GLenum target,
|
||||
GLuint texture_id,
|
||||
GLint level,
|
||||
GLuint framebuffer) {
|
||||
@@ -511,7 +512,7 @@ bool BindFramebufferTexture2D(GLenum target,
|
||||
glTexParameterf(binding_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(binding_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(binding_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
- glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer);
|
||||
+ decoder->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target,
|
||||
texture_id, level);
|
||||
|
||||
@@ -545,7 +546,7 @@ void DoCopyTexImage2D(
|
||||
DCHECK(dest_binding_target == GL_TEXTURE_2D ||
|
||||
dest_binding_target == GL_TEXTURE_CUBE_MAP);
|
||||
DCHECK(source_level == 0 || decoder->GetFeatureInfo()->IsES3Capable());
|
||||
- if (BindFramebufferTexture2D(source_target, source_id, source_level,
|
||||
+ if (BindFramebufferTexture2D(decoder, source_target, source_id, source_level,
|
||||
framebuffer)) {
|
||||
glBindTexture(dest_binding_target, dest_id);
|
||||
glTexParameterf(dest_binding_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
@@ -603,7 +604,7 @@ void DoCopyTexSubImage2D(
|
||||
DCHECK(dest_binding_target == GL_TEXTURE_2D ||
|
||||
dest_binding_target == GL_TEXTURE_CUBE_MAP);
|
||||
DCHECK(source_level == 0 || decoder->GetFeatureInfo()->IsES3Capable());
|
||||
- if (BindFramebufferTexture2D(source_target, source_id, source_level,
|
||||
+ if (BindFramebufferTexture2D(decoder, source_target, source_id, source_level,
|
||||
framebuffer)) {
|
||||
glBindTexture(dest_binding_target, dest_id);
|
||||
glTexParameterf(dest_binding_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
@@ -767,7 +768,7 @@ void DoReadbackAndTexImage(TexImageCommandType command_type,
|
||||
DCHECK(dest_binding_target == GL_TEXTURE_2D ||
|
||||
dest_binding_target == GL_TEXTURE_CUBE_MAP);
|
||||
DCHECK(source_level == 0 || decoder->GetFeatureInfo()->IsES3Capable());
|
||||
- if (BindFramebufferTexture2D(source_target, source_id, source_level,
|
||||
+ if (BindFramebufferTexture2D(decoder, source_target, source_id, source_level,
|
||||
framebuffer)) {
|
||||
glBindTexture(dest_binding_target, dest_id);
|
||||
glTexParameterf(dest_binding_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
@@ -1341,7 +1342,7 @@ void CopyTextureResourceManagerImpl::DoCopyTextureInternal(
|
||||
(y + height / 2.f) * m_y / source_height);
|
||||
|
||||
DCHECK(dest_level == 0 || decoder->GetFeatureInfo()->IsES3Capable());
|
||||
- if (BindFramebufferTexture2D(dest_target, dest_id, dest_level,
|
||||
+ if (BindFramebufferTexture2D(decoder, dest_target, dest_id, dest_level,
|
||||
framebuffer_)) {
|
||||
#ifndef NDEBUG
|
||||
// glValidateProgram of MACOSX validates FBO unlike other platforms, so
|
||||
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
|
||||
index 0bb2581fa602217ab77aa429ed51ce0ce5a06f00..895266f5988e2446eadf3d3e1d5c4919416cba76 100644
|
||||
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
|
||||
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
|
||||
@@ -675,6 +675,9 @@ class GLES2DecoderImpl : public GLES2Decoder,
|
||||
// Implements GpuSwitchingObserver.
|
||||
void OnGpuSwitched() override;
|
||||
|
||||
+ // Bind the framebuffer `fbo` and perform any workarounds needed.
|
||||
+ void BindFramebuffer(unsigned target, uint32_t service_id) const override;
|
||||
+
|
||||
// Restores the current state to the user's settings.
|
||||
void RestoreCurrentFramebufferBindings();
|
||||
|
||||
@@ -2418,6 +2421,10 @@ class GLES2DecoderImpl : public GLES2Decoder,
|
||||
// Backbuffer attachments that are currently undefined.
|
||||
uint32_t backbuffer_needs_clear_bits_;
|
||||
|
||||
+ // An always-complete FBO to use for workarounds
|
||||
+ GLuint complete_fbo_ = 0;
|
||||
+ GLuint complete_fbo_color_texture_ = 0;
|
||||
+
|
||||
// The current decoder error communicates the decoder error through command
|
||||
// processing functions that do not return the error value. Should be set only
|
||||
// if not returning an error.
|
||||
@@ -2594,7 +2601,7 @@ ScopedFramebufferBinder::ScopedFramebufferBinder(GLES2DecoderImpl* decoder,
|
||||
: decoder_(decoder) {
|
||||
ScopedGLErrorSuppressor suppressor("ScopedFramebufferBinder::ctor",
|
||||
decoder_->error_state_.get());
|
||||
- decoder->api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, id);
|
||||
+ decoder->BindFramebuffer(GL_FRAMEBUFFER, id);
|
||||
decoder->OnFboChanged();
|
||||
}
|
||||
|
||||
@@ -2980,7 +2987,27 @@ gpu::ContextResult GLES2DecoderImpl::Initialize(
|
||||
return gpu::ContextResult::kFatalFailure;
|
||||
}
|
||||
|
||||
- auto result = group_->Initialize(this, context_type);
|
||||
+ if (workarounds().ensure_previous_framebuffer_not_deleted) {
|
||||
+ // Use a 1x1 RGBA8 framebuffer as the "always complete" framebuffer to bind
|
||||
+ // before binding other framebuffers
|
||||
+ api()->glGenTexturesFn(1, &complete_fbo_color_texture_);
|
||||
+ api()->glBindTextureFn(GL_TEXTURE_2D, complete_fbo_color_texture_);
|
||||
+ api()->glTexImage2DFn(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA,
|
||||
+ GL_UNSIGNED_BYTE, nullptr);
|
||||
+
|
||||
+ api()->glGenFramebuffersEXTFn(1, &complete_fbo_);
|
||||
+ api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, complete_fbo_);
|
||||
+ api()->glFramebufferTexture2DEXTFn(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
+ GL_TEXTURE_2D,
|
||||
+ complete_fbo_color_texture_, 0);
|
||||
+ CHECK_EQ(api()->glCheckFramebufferStatusEXTFn(GL_FRAMEBUFFER),
|
||||
+ static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE));
|
||||
+ }
|
||||
+ CHECK_GL_ERROR();
|
||||
+
|
||||
+ auto result = group_->InitializeWithCompleteFramebufferForWorkarounds(
|
||||
+ this, context_type, complete_fbo_);
|
||||
+
|
||||
if (result != gpu::ContextResult::kSuccess) {
|
||||
// Must not destroy ContextGroup if it is not initialized.
|
||||
group_ = nullptr;
|
||||
@@ -3116,7 +3143,7 @@ gpu::ContextResult GLES2DecoderImpl::Initialize(
|
||||
state_.viewport_width = initial_size.width();
|
||||
state_.viewport_height = initial_size.height();
|
||||
} else {
|
||||
- api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, GetBackbufferServiceId());
|
||||
+ BindFramebuffer(GL_FRAMEBUFFER, GetBackbufferServiceId());
|
||||
// These are NOT if the back buffer has these proprorties. They are
|
||||
// if we want the command buffer to enforce them regardless of what
|
||||
// the real backbuffer is assuming the real back buffer gives us more than
|
||||
@@ -3809,7 +3836,7 @@ void GLES2DecoderImpl::DeleteFramebuffersHelper(
|
||||
if (workarounds().unbind_attachments_on_bound_render_fbo_delete)
|
||||
framebuffer->DoUnbindGLAttachmentsForWorkaround(target);
|
||||
|
||||
- api()->glBindFramebufferEXTFn(target, GetBackbufferServiceId());
|
||||
+ BindFramebuffer(target, GetBackbufferServiceId());
|
||||
state_.UpdateWindowRectanglesForBoundDrawFramebufferClientID(0);
|
||||
framebuffer_state_.bound_draw_framebuffer = nullptr;
|
||||
framebuffer_state_.clear_state_dirty = true;
|
||||
@@ -3817,7 +3844,7 @@ void GLES2DecoderImpl::DeleteFramebuffersHelper(
|
||||
if (framebuffer == framebuffer_state_.bound_read_framebuffer.get()) {
|
||||
framebuffer_state_.bound_read_framebuffer = nullptr;
|
||||
GLenum target = GetReadFramebufferTarget();
|
||||
- api()->glBindFramebufferEXTFn(target, GetBackbufferServiceId());
|
||||
+ BindFramebuffer(target, GetBackbufferServiceId());
|
||||
}
|
||||
OnFboChanged();
|
||||
RemoveFramebuffer(client_id);
|
||||
@@ -3965,33 +3992,32 @@ void GLES2DecoderImpl::ProcessFinishedAsyncTransfers() {
|
||||
ProcessPendingReadPixels(false);
|
||||
}
|
||||
|
||||
-static void RebindCurrentFramebuffer(gl::GLApi* api,
|
||||
- GLenum target,
|
||||
- Framebuffer* framebuffer,
|
||||
- GLuint back_buffer_service_id) {
|
||||
- GLuint framebuffer_id = framebuffer ? framebuffer->service_id() : 0;
|
||||
+void GLES2DecoderImpl::RestoreCurrentFramebufferBindings() {
|
||||
+ framebuffer_state_.clear_state_dirty = true;
|
||||
|
||||
- if (framebuffer_id == 0) {
|
||||
- framebuffer_id = back_buffer_service_id;
|
||||
- }
|
||||
+ auto rebind_current_framebuffer = [this](GLenum target,
|
||||
+ Framebuffer* framebuffer,
|
||||
+ GLuint back_buffer_service_id) {
|
||||
+ GLuint framebuffer_id = framebuffer ? framebuffer->service_id() : 0;
|
||||
|
||||
- api->glBindFramebufferEXTFn(target, framebuffer_id);
|
||||
-}
|
||||
+ if (framebuffer_id == 0) {
|
||||
+ framebuffer_id = back_buffer_service_id;
|
||||
+ }
|
||||
|
||||
-void GLES2DecoderImpl::RestoreCurrentFramebufferBindings() {
|
||||
- framebuffer_state_.clear_state_dirty = true;
|
||||
+ BindFramebuffer(target, framebuffer_id);
|
||||
+ };
|
||||
|
||||
if (!SupportsSeparateFramebufferBinds()) {
|
||||
- RebindCurrentFramebuffer(api(), GL_FRAMEBUFFER,
|
||||
- framebuffer_state_.bound_draw_framebuffer.get(),
|
||||
- GetBackbufferServiceId());
|
||||
+ rebind_current_framebuffer(GL_FRAMEBUFFER,
|
||||
+ framebuffer_state_.bound_draw_framebuffer.get(),
|
||||
+ GetBackbufferServiceId());
|
||||
} else {
|
||||
- RebindCurrentFramebuffer(api(), GL_READ_FRAMEBUFFER,
|
||||
- framebuffer_state_.bound_read_framebuffer.get(),
|
||||
- GetBackbufferServiceId());
|
||||
- RebindCurrentFramebuffer(api(), GL_DRAW_FRAMEBUFFER,
|
||||
- framebuffer_state_.bound_draw_framebuffer.get(),
|
||||
- GetBackbufferServiceId());
|
||||
+ rebind_current_framebuffer(GL_READ_FRAMEBUFFER,
|
||||
+ framebuffer_state_.bound_read_framebuffer.get(),
|
||||
+ GetBackbufferServiceId());
|
||||
+ rebind_current_framebuffer(GL_DRAW_FRAMEBUFFER,
|
||||
+ framebuffer_state_.bound_draw_framebuffer.get(),
|
||||
+ GetBackbufferServiceId());
|
||||
}
|
||||
OnFboChanged();
|
||||
}
|
||||
@@ -4380,6 +4406,16 @@ void GLES2DecoderImpl::OnGpuSwitched() {
|
||||
client()->OnGpuSwitched();
|
||||
}
|
||||
|
||||
+void GLES2DecoderImpl::BindFramebuffer(unsigned target,
|
||||
+ uint32_t service_id) const {
|
||||
+ if (workarounds().ensure_previous_framebuffer_not_deleted) {
|
||||
+ DCHECK(complete_fbo_);
|
||||
+ api()->glBindFramebufferEXTFn(target, complete_fbo_);
|
||||
+ }
|
||||
+
|
||||
+ api()->glBindFramebufferEXTFn(target, service_id);
|
||||
+}
|
||||
+
|
||||
void GLES2DecoderImpl::Destroy(bool have_context) {
|
||||
if (!initialized())
|
||||
return;
|
||||
@@ -4429,6 +4465,13 @@ void GLES2DecoderImpl::Destroy(bool have_context) {
|
||||
offscreen_target_frame_buffer_->Destroy();
|
||||
if (offscreen_target_color_texture_.get())
|
||||
offscreen_target_color_texture_->Destroy();
|
||||
+
|
||||
+ if (complete_fbo_color_texture_) {
|
||||
+ api()->glDeleteTexturesFn(1, &complete_fbo_color_texture_);
|
||||
+ }
|
||||
+ if (complete_fbo_) {
|
||||
+ api()->glDeleteFramebuffersEXTFn(1, &complete_fbo_);
|
||||
+ }
|
||||
} else {
|
||||
if (offscreen_target_frame_buffer_.get())
|
||||
offscreen_target_frame_buffer_->Invalidate();
|
||||
@@ -5058,13 +5101,13 @@ void GLES2DecoderImpl::RestoreFramebufferBindings() const {
|
||||
? framebuffer_state_.bound_draw_framebuffer->service_id()
|
||||
: GetBackbufferServiceId();
|
||||
if (!SupportsSeparateFramebufferBinds()) {
|
||||
- api()->glBindFramebufferEXTFn(GL_FRAMEBUFFER, service_id);
|
||||
+ BindFramebuffer(GL_FRAMEBUFFER, service_id);
|
||||
} else {
|
||||
- api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, service_id);
|
||||
+ BindFramebuffer(GL_DRAW_FRAMEBUFFER, service_id);
|
||||
service_id = framebuffer_state_.bound_read_framebuffer.get()
|
||||
? framebuffer_state_.bound_read_framebuffer->service_id()
|
||||
: GetBackbufferServiceId();
|
||||
- api()->glBindFramebufferEXTFn(GL_READ_FRAMEBUFFER, service_id);
|
||||
+ BindFramebuffer(GL_READ_FRAMEBUFFER, service_id);
|
||||
}
|
||||
OnFboChanged();
|
||||
}
|
||||
@@ -5205,7 +5248,7 @@ void GLES2DecoderImpl::DoBindFramebuffer(GLenum target, GLuint client_id) {
|
||||
service_id = GetBackbufferServiceId();
|
||||
}
|
||||
|
||||
- api()->glBindFramebufferEXTFn(target, service_id);
|
||||
+ BindFramebuffer(target, service_id);
|
||||
OnFboChanged();
|
||||
}
|
||||
|
||||
@@ -6976,8 +7019,7 @@ void GLES2DecoderImpl::ClearUnclearedAttachments(
|
||||
if (target == GL_READ_FRAMEBUFFER && draw_framebuffer != framebuffer) {
|
||||
// TODO(zmo): There is no guarantee that an FBO that is complete on the
|
||||
// READ attachment will be complete as a DRAW attachment.
|
||||
- api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER,
|
||||
- framebuffer->service_id());
|
||||
+ BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer->service_id());
|
||||
}
|
||||
state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false);
|
||||
@@ -7024,8 +7066,7 @@ void GLES2DecoderImpl::ClearUnclearedAttachments(
|
||||
target == GL_READ_FRAMEBUFFER && draw_framebuffer != framebuffer) {
|
||||
// TODO(zmo): There is no guarantee that an FBO that is complete on the
|
||||
// READ attachment will be complete as a DRAW attachment.
|
||||
- api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER,
|
||||
- framebuffer->service_id());
|
||||
+ BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer->service_id());
|
||||
}
|
||||
state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false);
|
||||
ClearDeviceWindowRectangles();
|
||||
@@ -7043,7 +7084,7 @@ void GLES2DecoderImpl::ClearUnclearedAttachments(
|
||||
if (target == GL_READ_FRAMEBUFFER && draw_framebuffer != framebuffer) {
|
||||
GLuint service_id = draw_framebuffer ? draw_framebuffer->service_id() :
|
||||
GetBackbufferServiceId();
|
||||
- api()->glBindFramebufferEXTFn(GL_DRAW_FRAMEBUFFER, service_id);
|
||||
+ BindFramebuffer(GL_DRAW_FRAMEBUFFER, service_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7900,7 +7941,8 @@ void GLES2DecoderImpl::RenderbufferStorageMultisampleHelperAMD(
|
||||
|
||||
bool GLES2DecoderImpl::RegenerateRenderbufferIfNeeded(
|
||||
Renderbuffer* renderbuffer) {
|
||||
- if (!renderbuffer->RegenerateAndBindBackingObjectIfNeeded(workarounds())) {
|
||||
+ if (!renderbuffer->RegenerateAndBindBackingObjectIfNeeded(this,
|
||||
+ workarounds())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -12059,7 +12101,7 @@ bool GLES2DecoderImpl::ClearLevelUsingGL(Texture* texture,
|
||||
GLenum fb_target = GetDrawFramebufferTarget();
|
||||
GLuint fb = 0;
|
||||
api()->glGenFramebuffersEXTFn(1, &fb);
|
||||
- api()->glBindFramebufferEXTFn(fb_target, fb);
|
||||
+ BindFramebuffer(fb_target, fb);
|
||||
|
||||
bool have_color = (channels & GLES2Util::kRGBA) != 0;
|
||||
if (have_color) {
|
||||
@@ -12102,7 +12144,7 @@ bool GLES2DecoderImpl::ClearLevelUsingGL(Texture* texture,
|
||||
Framebuffer* framebuffer = GetFramebufferInfoForTarget(fb_target);
|
||||
GLuint fb_service_id =
|
||||
framebuffer ? framebuffer->service_id() : GetBackbufferServiceId();
|
||||
- api()->glBindFramebufferEXTFn(fb_target, fb_service_id);
|
||||
+ BindFramebuffer(fb_target, fb_service_id);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -14581,8 +14623,9 @@ error::Error GLES2DecoderImpl::HandleGetRequestableExtensionsCHROMIUM(
|
||||
new FeatureInfo(workarounds(), group_->gpu_feature_info()));
|
||||
DisallowedFeatures disallowed_features = feature_info_->disallowed_features();
|
||||
disallowed_features.AllowExtensions();
|
||||
- info->Initialize(feature_info_->context_type(),
|
||||
- false /* is_passthrough_cmd_decoder */, disallowed_features);
|
||||
+ info->InitializeWithCompleteFramebufferForWorkarounds(
|
||||
+ feature_info_->context_type(), false /* is_passthrough_cmd_decoder */,
|
||||
+ disallowed_features, complete_fbo_);
|
||||
bucket->SetFromString(gfx::MakeExtensionString(info->extensions()).c_str());
|
||||
return error::kNoError;
|
||||
}
|
||||
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
|
||||
index bd3158e174f0881f99c45a0c8b8d5640e3be2b8f..cc160b5771954b485652f4b0cdc6695bfdea9954 100644
|
||||
--- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
|
||||
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h
|
||||
@@ -147,6 +147,8 @@ class MockGLES2Decoder : public GLES2Decoder {
|
||||
int height,
|
||||
int depth));
|
||||
MOCK_METHOD0(GetErrorState, ErrorState *());
|
||||
+ MOCK_CONST_METHOD2(BindFramebuffer,
|
||||
+ void(unsigned target, uint32_t service_id));
|
||||
|
||||
MOCK_METHOD0(GetLogger, Logger*());
|
||||
|
||||
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
|
||||
index e8808f50df6d179879bc44fcacfb4154a4ac0454..de9f2f5cfb247a914606d3ba8b24f0924ac728e0 100644
|
||||
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
|
||||
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
|
||||
@@ -1541,6 +1541,11 @@ gpu::gles2::ErrorState* GLES2DecoderPassthroughImpl::GetErrorState() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+void GLES2DecoderPassthroughImpl::BindFramebuffer(unsigned target,
|
||||
+ uint32_t service_id) const {
|
||||
+ NOTREACHED();
|
||||
+}
|
||||
+
|
||||
void GLES2DecoderPassthroughImpl::WaitForReadPixels(
|
||||
base::OnceClosure callback) {}
|
||||
|
||||
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
|
||||
index 30dc611c8f0a84ec3493e3f865b6640cab157e2a..50938ca4a0086d14493ec7e0d85b99615c0c7fb7 100644
|
||||
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
|
||||
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.h
|
||||
@@ -315,6 +315,8 @@ class GPU_GLES2_EXPORT GLES2DecoderPassthroughImpl
|
||||
|
||||
ErrorState* GetErrorState() override;
|
||||
|
||||
+ void BindFramebuffer(unsigned target, uint32_t service_id) const override;
|
||||
+
|
||||
void WaitForReadPixels(base::OnceClosure callback) override;
|
||||
|
||||
// Returns true if the context was lost either by GL_ARB_robustness, forced
|
||||
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
|
||||
index 5f61aac4fc217a97ccfcdbd6634056e5f305a425..767fee8cace6dec4cfaf1ccdd46f1764a7ae5318 100644
|
||||
--- a/gpu/command_buffer/service/raster_decoder.cc
|
||||
+++ b/gpu/command_buffer/service/raster_decoder.cc
|
||||
@@ -560,6 +560,7 @@ class RasterDecoderImpl final : public RasterDecoder,
|
||||
|
||||
gles2::ContextGroup* GetContextGroup() override;
|
||||
gles2::ErrorState* GetErrorState() override;
|
||||
+ void BindFramebuffer(unsigned target, uint32_t service_id) const override;
|
||||
|
||||
bool IsCompressedTextureFormat(unsigned format) override;
|
||||
bool ClearLevel(gles2::Texture* texture,
|
||||
@@ -1598,6 +1599,11 @@ gles2::ErrorState* RasterDecoderImpl::GetErrorState() {
|
||||
return error_state_.get();
|
||||
}
|
||||
|
||||
+void RasterDecoderImpl::BindFramebuffer(unsigned target,
|
||||
+ uint32_t service_id) const {
|
||||
+ NOTREACHED();
|
||||
+}
|
||||
+
|
||||
bool RasterDecoderImpl::IsCompressedTextureFormat(unsigned format) {
|
||||
return feature_info()->validators()->compressed_texture_format.IsValid(
|
||||
format);
|
||||
diff --git a/gpu/command_buffer/service/renderbuffer_manager.cc b/gpu/command_buffer/service/renderbuffer_manager.cc
|
||||
index 8075cb3acf7204a661a6c094edc1c1a783d46dfb..6cdb9186c4e3bfe40dd437db6b92343f60830171 100644
|
||||
--- a/gpu/command_buffer/service/renderbuffer_manager.cc
|
||||
+++ b/gpu/command_buffer/service/renderbuffer_manager.cc
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "base/trace_event/memory_dump_manager.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
|
||||
+#include "gpu/command_buffer/service/decoder_context.h"
|
||||
#include "gpu/command_buffer/service/feature_info.h"
|
||||
#include "gpu/command_buffer/service/framebuffer_manager.h"
|
||||
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
|
||||
@@ -141,6 +142,7 @@ Renderbuffer::Renderbuffer(RenderbufferManager* manager,
|
||||
}
|
||||
|
||||
bool Renderbuffer::RegenerateAndBindBackingObjectIfNeeded(
|
||||
+ const DecoderContext* decoder,
|
||||
const GpuDriverBugWorkarounds& workarounds) {
|
||||
bool multisample_workaround =
|
||||
workarounds.multisample_renderbuffer_resize_emulation;
|
||||
@@ -167,7 +169,7 @@ bool Renderbuffer::RegenerateAndBindBackingObjectIfNeeded(
|
||||
|
||||
// Attach new renderbuffer to all framebuffers
|
||||
for (auto& point : framebuffer_attachment_points_) {
|
||||
- glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, point.first->service_id());
|
||||
+ decoder->BindFramebuffer(GL_DRAW_FRAMEBUFFER, point.first->service_id());
|
||||
glFramebufferRenderbufferEXT(GL_DRAW_FRAMEBUFFER, point.second,
|
||||
GL_RENDERBUFFER, service_id_);
|
||||
}
|
||||
diff --git a/gpu/command_buffer/service/renderbuffer_manager.h b/gpu/command_buffer/service/renderbuffer_manager.h
|
||||
index 7d575387ca8f6a7cb1b4bb020c52f7b53bed5d10..5eabeb9dd160122366507b7dfba53fd1c3285115 100644
|
||||
--- a/gpu/command_buffer/service/renderbuffer_manager.h
|
||||
+++ b/gpu/command_buffer/service/renderbuffer_manager.h
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
namespace gpu {
|
||||
class GpuDriverBugWorkarounds;
|
||||
+class DecoderContext;
|
||||
|
||||
namespace gles2 {
|
||||
|
||||
@@ -79,6 +80,7 @@ class GPU_GLES2_EXPORT Renderbuffer : public base::RefCounted<Renderbuffer> {
|
||||
// Regenerates the object backing this client_id, creating a new service_id.
|
||||
// Also reattaches any framebuffers using this renderbuffer.
|
||||
bool RegenerateAndBindBackingObjectIfNeeded(
|
||||
+ const DecoderContext* decoder,
|
||||
const GpuDriverBugWorkarounds& workarounds);
|
||||
|
||||
void AddFramebufferAttachmentPoint(Framebuffer* framebuffer,
|
||||
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
|
||||
index 172834911e75edb3ed1c4dceb3ec23755bf3b5f6..fb873fee0e4b1d33edd4210fad8ad74c6d563355 100644
|
||||
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
|
||||
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
|
||||
@@ -305,6 +305,9 @@ class WebGPUDecoderImpl final : public WebGPUDecoder {
|
||||
std::string_view GetLogPrefix() override { return "WebGPUDecoderImpl"; }
|
||||
gles2::ContextGroup* GetContextGroup() override { return nullptr; }
|
||||
gles2::ErrorState* GetErrorState() override { NOTREACHED(); }
|
||||
+ void BindFramebuffer(unsigned target, uint32_t service_id) const override {
|
||||
+ NOTREACHED();
|
||||
+ }
|
||||
bool IsCompressedTextureFormat(unsigned format) override { NOTREACHED(); }
|
||||
bool ClearLevel(gles2::Texture* texture,
|
||||
unsigned target,
|
||||
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
|
||||
index ebea892a523322b38a22ba6b0442262edcd6166b..2af5b0460beed7b78c00c9f2a70e14e5f7696ac0 100644
|
||||
--- a/gpu/config/gpu_driver_bug_list.json
|
||||
+++ b/gpu/config/gpu_driver_bug_list.json
|
||||
@@ -3818,6 +3818,31 @@
|
||||
"features": [
|
||||
"disable_d3d12_video_encoder"
|
||||
]
|
||||
+ },
|
||||
+ {
|
||||
+ "id": 470,
|
||||
+ "description": "Disable D3D12 video encoder on Windows versions older 11 24H2",
|
||||
+ "os": {
|
||||
+ "type": "win",
|
||||
+ "version": {
|
||||
+ "op": "<",
|
||||
+ "value": "10.0.26100.2033"
|
||||
+ }
|
||||
+ },
|
||||
+ "features": [
|
||||
+ "disable_d3d12_video_encoder"
|
||||
+ ]
|
||||
+ },
|
||||
+ {
|
||||
+ "id": 471,
|
||||
+ "description": "IMG drivers can sometimes reference previously bound complete framebuffers.",
|
||||
+ "os": {
|
||||
+ "type": "android"
|
||||
+ },
|
||||
+ "gl_vendor": "Imagination.*",
|
||||
+ "features": [
|
||||
+ "ensure_previous_framebuffer_not_deleted"
|
||||
+ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt
|
||||
index 7f8b6e019f9b1986411b17c4ef1a2e863eb689f0..30ee7799cdd0a344e433e95cd74e4630a7c87aff 100644
|
||||
--- a/gpu/config/gpu_workaround_list.txt
|
||||
+++ b/gpu/config/gpu_workaround_list.txt
|
||||
@@ -78,6 +78,7 @@ dont_delete_source_texture_for_egl_image
|
||||
dont_use_loops_to_initialize_variables
|
||||
enable_bgra8_overlays_with_yuv_overlay_support
|
||||
enable_webgl_timer_query_extensions
|
||||
+ensure_previous_framebuffer_not_deleted
|
||||
etc1_power_of_two_only
|
||||
exit_on_context_lost
|
||||
flush_before_create_fence
|
||||
199
patches/chromium/cherry-pick-45c5a70d984d.patch
Normal file
199
patches/chromium/cherry-pick-45c5a70d984d.patch
Normal file
@@ -0,0 +1,199 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Date: Wed, 25 Feb 2026 03:24:19 -0800
|
||||
Subject: Describe a vector of segments as "segments", not "tokens"
|
||||
|
||||
The specification uses the term "tokens" to refer to a sequence
|
||||
of V8CSSUnparsedSegment objects, and CSSUnparsedValue has adopted
|
||||
this terminology. While it is usually a good idea for Blink
|
||||
to mirror the language used in specifications, "tokens" is very
|
||||
confusing here, since it always means CSSParserTokens in every other
|
||||
place in the style code.
|
||||
|
||||
Bug: 487117772
|
||||
Change-Id: I2dc132c4e618e398e1f8bdabc03a8d2ab6c118e7
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7606599
|
||||
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1590040}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
index 486e9d10c1e0a682ec239f4df696f4133300eebb..567d4fad7436c24d4c42bc36ebfd7ee3641e3b90 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
@@ -28,12 +28,12 @@ String FindVariableName(CSSParserTokenStream& stream) {
|
||||
|
||||
V8CSSUnparsedSegment* VariableReferenceValue(
|
||||
const StringView& variable_name,
|
||||
- const HeapVector<Member<V8CSSUnparsedSegment>>& tokens) {
|
||||
+ const HeapVector<Member<V8CSSUnparsedSegment>>& segments) {
|
||||
CSSUnparsedValue* unparsed_value;
|
||||
- if (tokens.size() == 0) {
|
||||
+ if (segments.size() == 0) {
|
||||
unparsed_value = nullptr;
|
||||
} else {
|
||||
- unparsed_value = CSSUnparsedValue::Create(tokens);
|
||||
+ unparsed_value = CSSUnparsedValue::Create(segments);
|
||||
}
|
||||
|
||||
CSSStyleVariableReferenceValue* variable_reference =
|
||||
@@ -50,13 +50,13 @@ V8CSSUnparsedSegment* VariableReferenceValue(
|
||||
HeapVector<Member<V8CSSUnparsedSegment>> ParserTokenStreamToTokens(
|
||||
CSSParserTokenStream& stream) {
|
||||
int nesting_level = 0;
|
||||
- HeapVector<Member<V8CSSUnparsedSegment>> tokens;
|
||||
+ HeapVector<Member<V8CSSUnparsedSegment>> segments;
|
||||
StringBuilder builder;
|
||||
while (stream.Peek().GetType() != kEOFToken) {
|
||||
if (stream.Peek().FunctionId() == CSSValueID::kVar ||
|
||||
stream.Peek().FunctionId() == CSSValueID::kEnv) {
|
||||
if (!builder.empty()) {
|
||||
- tokens.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(
|
||||
+ segments.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(
|
||||
builder.ReleaseString()));
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ HeapVector<Member<V8CSSUnparsedSegment>> ParserTokenStreamToTokens(
|
||||
if (!ref) {
|
||||
break;
|
||||
}
|
||||
- tokens.push_back(ref);
|
||||
+ segments.push_back(ref);
|
||||
} else {
|
||||
if (stream.Peek().GetBlockType() == CSSParserToken::kBlockStart) {
|
||||
++nesting_level;
|
||||
@@ -86,10 +86,10 @@ HeapVector<Member<V8CSSUnparsedSegment>> ParserTokenStreamToTokens(
|
||||
}
|
||||
}
|
||||
if (!builder.empty()) {
|
||||
- tokens.push_back(
|
||||
+ segments.push_back(
|
||||
MakeGarbageCollected<V8CSSUnparsedSegment>(builder.ReleaseString()));
|
||||
}
|
||||
- return tokens;
|
||||
+ return segments;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -109,8 +109,8 @@ CSSUnparsedValue* CSSUnparsedValue::FromCSSVariableData(
|
||||
V8CSSUnparsedSegment* CSSUnparsedValue::AnonymousIndexedGetter(
|
||||
uint32_t index,
|
||||
ExceptionState& exception_state) const {
|
||||
- if (index < tokens_.size()) {
|
||||
- return tokens_[index].Get();
|
||||
+ if (index < segments_.size()) {
|
||||
+ return segments_[index].Get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -119,20 +119,20 @@ IndexedPropertySetterResult CSSUnparsedValue::AnonymousIndexedSetter(
|
||||
uint32_t index,
|
||||
V8CSSUnparsedSegment* segment,
|
||||
ExceptionState& exception_state) {
|
||||
- if (index < tokens_.size()) {
|
||||
- tokens_[index] = segment;
|
||||
+ if (index < segments_.size()) {
|
||||
+ segments_[index] = segment;
|
||||
return IndexedPropertySetterResult::kIntercepted;
|
||||
}
|
||||
|
||||
- if (index == tokens_.size()) {
|
||||
- tokens_.push_back(segment);
|
||||
+ if (index == segments_.size()) {
|
||||
+ segments_.push_back(segment);
|
||||
return IndexedPropertySetterResult::kIntercepted;
|
||||
}
|
||||
|
||||
exception_state.ThrowRangeError(
|
||||
ExceptionMessages::IndexOutsideRange<unsigned>(
|
||||
- "index", index, 0, ExceptionMessages::kInclusiveBound, tokens_.size(),
|
||||
- ExceptionMessages::kInclusiveBound));
|
||||
+ "index", index, 0, ExceptionMessages::kInclusiveBound,
|
||||
+ segments_.size(), ExceptionMessages::kInclusiveBound));
|
||||
return IndexedPropertySetterResult::kIntercepted;
|
||||
}
|
||||
|
||||
@@ -195,14 +195,14 @@ bool CSSUnparsedValue::AppendUnparsedString(
|
||||
return false; // Cycle.
|
||||
}
|
||||
values_on_stack.insert(this);
|
||||
- for (unsigned i = 0; i < tokens_.size(); i++) {
|
||||
+ for (unsigned i = 0; i < segments_.size(); i++) {
|
||||
if (i) {
|
||||
builder.Append("/**/");
|
||||
}
|
||||
- switch (tokens_[i]->GetContentType()) {
|
||||
+ switch (segments_[i]->GetContentType()) {
|
||||
case V8CSSUnparsedSegment::ContentType::kCSSVariableReferenceValue: {
|
||||
const auto* reference_value =
|
||||
- tokens_[i]->GetAsCSSVariableReferenceValue();
|
||||
+ segments_[i]->GetAsCSSVariableReferenceValue();
|
||||
builder.Append("var(");
|
||||
builder.Append(reference_value->variable());
|
||||
if (reference_value->fallback()) {
|
||||
@@ -216,7 +216,7 @@ bool CSSUnparsedValue::AppendUnparsedString(
|
||||
break;
|
||||
}
|
||||
case V8CSSUnparsedSegment::ContentType::kString:
|
||||
- builder.Append(tokens_[i]->GetAsString());
|
||||
+ builder.Append(segments_[i]->GetAsString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
index c9dab7a0b3ffeaeb6b5d2ab50d876d40c38a760e..5d1961b170f14ae21ca8f69b3c3cd8af28f4478a 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
@@ -26,8 +26,8 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
|
||||
public:
|
||||
static CSSUnparsedValue* Create(
|
||||
- const HeapVector<Member<V8CSSUnparsedSegment>>& tokens) {
|
||||
- return MakeGarbageCollected<CSSUnparsedValue>(tokens);
|
||||
+ const HeapVector<Member<V8CSSUnparsedSegment>>& segments) {
|
||||
+ return MakeGarbageCollected<CSSUnparsedValue>(segments);
|
||||
}
|
||||
|
||||
// Blink-internal constructor
|
||||
@@ -37,14 +37,14 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
static CSSUnparsedValue* FromCSSValue(const CSSUnparsedDeclarationValue&);
|
||||
static CSSUnparsedValue* FromCSSVariableData(const CSSVariableData&);
|
||||
static CSSUnparsedValue* FromString(const String& string) {
|
||||
- HeapVector<Member<V8CSSUnparsedSegment>> tokens;
|
||||
- tokens.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(string));
|
||||
- return Create(tokens);
|
||||
+ HeapVector<Member<V8CSSUnparsedSegment>> segments;
|
||||
+ segments.push_back(MakeGarbageCollected<V8CSSUnparsedSegment>(string));
|
||||
+ return Create(segments);
|
||||
}
|
||||
|
||||
explicit CSSUnparsedValue(
|
||||
- const HeapVector<Member<V8CSSUnparsedSegment>>& tokens)
|
||||
- : tokens_(tokens) {}
|
||||
+ const HeapVector<Member<V8CSSUnparsedSegment>>& segments)
|
||||
+ : segments_(segments) {}
|
||||
CSSUnparsedValue(const CSSUnparsedValue&) = delete;
|
||||
CSSUnparsedValue& operator=(const CSSUnparsedValue&) = delete;
|
||||
|
||||
@@ -60,10 +60,10 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
V8CSSUnparsedSegment* segment,
|
||||
ExceptionState& exception_state);
|
||||
|
||||
- wtf_size_t length() const { return tokens_.size(); }
|
||||
+ wtf_size_t length() const { return segments_.size(); }
|
||||
|
||||
void Trace(Visitor* visitor) const override {
|
||||
- visitor->Trace(tokens_);
|
||||
+ visitor->Trace(segments_);
|
||||
CSSStyleValue::Trace(visitor);
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
StringBuilder&,
|
||||
HeapHashSet<Member<const CSSUnparsedValue>>& values_on_stack) const;
|
||||
|
||||
- HeapVector<Member<V8CSSUnparsedSegment>> tokens_;
|
||||
+ HeapVector<Member<V8CSSUnparsedSegment>> segments_;
|
||||
|
||||
FRIEND_TEST_ALL_PREFIXES(CSSUnparsedDeclarationValueTest, MixedList);
|
||||
};
|
||||
149
patches/chromium/cherry-pick-50b057660b4d.patch
Normal file
149
patches/chromium/cherry-pick-50b057660b4d.patch
Normal file
@@ -0,0 +1,149 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Kai Ninomiya <kainino@chromium.org>
|
||||
Date: Wed, 11 Mar 2026 14:52:44 -0700
|
||||
Subject: [M146] Increment WebGL context generation number on context restore
|
||||
|
||||
Objects created while the context is lost should not be valid to use
|
||||
after the context is restored.
|
||||
- Replace number_of_context_losses_ with a "context generation number"
|
||||
which increments on both context loss and context restore.
|
||||
- Technically, it would make sense to increment it only on context
|
||||
restore, but just in case any logic is relying on the current
|
||||
behavior, increment it in both places.
|
||||
- It's uint64_t just in case someone figures out how to increment it 4
|
||||
billion times.
|
||||
- Remove unused WebGLRenderingContextBase::number_of_context_losses_,
|
||||
left over from before it was moved into WebGLContextObjectSupport.
|
||||
|
||||
(cherry picked from commit c1433740f3ea902fd6b15d63c4865ad60a3761f9)
|
||||
|
||||
Bug: 485935305
|
||||
Change-Id: I1007217c8e69cfb8de4f117e0b7845ca574579c4
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7630664
|
||||
Reviewed-by: Kenneth Russell <kbr@chromium.org>
|
||||
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1593726}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7658823
|
||||
Auto-Submit: Kai Ninomiya <kainino@chromium.org>
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Cr-Commit-Position: refs/branch-heads/7680@{#2370}
|
||||
Cr-Branched-From: 76b7d80e5cda23fe6537eed26d68c92e995c7f39-refs/heads/main@{#1582197}
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc
|
||||
index 6a3b1416354e7993e7a9ebd25c4ca08593105d9a..83941f8163a5e9425f2df8fd3bb98e1fd37537ad 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.cc
|
||||
@@ -22,7 +22,10 @@ WebGLContextObjectSupport::WebGLContextObjectSupport(
|
||||
|
||||
void WebGLContextObjectSupport::OnContextLost() {
|
||||
DCHECK(!is_lost_);
|
||||
- number_of_context_losses_++;
|
||||
+ // Invalidate all past objects.
|
||||
+ // (It may not be strictly necessary to do this here, since it's also done in
|
||||
+ // OnContextRestored, but we did it historically, and there's no harm in it.)
|
||||
+ context_generation_++;
|
||||
is_lost_ = true;
|
||||
gles2_interface_ = nullptr;
|
||||
extensions_enabled_.reset();
|
||||
@@ -31,6 +34,8 @@ void WebGLContextObjectSupport::OnContextLost() {
|
||||
void WebGLContextObjectSupport::OnContextRestored(
|
||||
gpu::gles2::GLES2Interface* gl) {
|
||||
DCHECK(is_lost_);
|
||||
+ // Invalidate all past objects.
|
||||
+ context_generation_++;
|
||||
is_lost_ = false;
|
||||
gles2_interface_ = gl;
|
||||
}
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h
|
||||
index 907866bb21acf9647d1c0ecd791e642e96b734fc..ba8b79f8bb9db12058614982a625baaff5546af7 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_context_object_support.h
|
||||
@@ -33,10 +33,10 @@ class MODULES_EXPORT WebGLContextObjectSupport : public ScriptWrappable {
|
||||
bool IsWebGL2() const { return is_webgl2_; }
|
||||
bool IsLost() const { return is_lost_; }
|
||||
|
||||
- // How many context losses there were, to check whether a WebGLObject was
|
||||
- // created since the last context resoration or before that (and hence invalid
|
||||
- // to use).
|
||||
- uint32_t NumberOfContextLosses() const { return number_of_context_losses_; }
|
||||
+ // Which "generation" the context is on (essentially, how many times it has
|
||||
+ // been restored), to check whether a WebGLObject was created since the last
|
||||
+ // context restoration, or before that (and hence invalid to use).
|
||||
+ uint64_t GetContextGeneration() const { return context_generation_; }
|
||||
|
||||
bool ExtensionEnabled(WebGLExtensionName name) const {
|
||||
return extensions_enabled_.test(name);
|
||||
@@ -65,7 +65,7 @@ class MODULES_EXPORT WebGLContextObjectSupport : public ScriptWrappable {
|
||||
std::bitset<kWebGLExtensionNameCount> extensions_enabled_ = {};
|
||||
raw_ptr<gpu::gles2::GLES2Interface> gles2_interface_ = nullptr;
|
||||
|
||||
- uint32_t number_of_context_losses_ = 0;
|
||||
+ uint64_t context_generation_ = 0;
|
||||
bool is_lost_ = true;
|
||||
bool is_webgl2_;
|
||||
};
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_object.cc b/third_party/blink/renderer/modules/webgl/webgl_object.cc
|
||||
index 9d984de0073796f23a5038bfc0a51ec676179765..07e0a9a4aa3406a1298a677a3159edadc5f2cbb5 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_object.cc
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_object.cc
|
||||
@@ -33,9 +33,9 @@ namespace blink {
|
||||
|
||||
WebGLObject::WebGLObject(WebGLContextObjectSupport* context)
|
||||
: context_(context),
|
||||
- cached_number_of_context_losses_(std::numeric_limits<uint32_t>::max()) {
|
||||
+ context_generation_at_creation_(std::numeric_limits<uint64_t>::max()) {
|
||||
if (context_) {
|
||||
- cached_number_of_context_losses_ = context->NumberOfContextLosses();
|
||||
+ context_generation_at_creation_ = context->GetContextGeneration();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ bool WebGLObject::Validate(const WebGLContextObjectSupport* context) const {
|
||||
// the objects they ever created, so there's no way to invalidate them
|
||||
// eagerly during context loss. The invalidation is discovered lazily.
|
||||
return (context == context_ && context_ != nullptr &&
|
||||
- cached_number_of_context_losses_ == context->NumberOfContextLosses());
|
||||
+ context_generation_at_creation_ == context->GetContextGeneration());
|
||||
}
|
||||
|
||||
void WebGLObject::SetObject(GLuint object) {
|
||||
@@ -71,7 +71,7 @@ void WebGLObject::DeleteObject(gpu::gles2::GLES2Interface* gl) {
|
||||
return;
|
||||
}
|
||||
|
||||
- if (context_->NumberOfContextLosses() != cached_number_of_context_losses_) {
|
||||
+ if (context_->GetContextGeneration() != context_generation_at_creation_) {
|
||||
// This object has been invalidated.
|
||||
return;
|
||||
}
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_object.h b/third_party/blink/renderer/modules/webgl/webgl_object.h
|
||||
index bb56df0f99e8e8432e03442feb9302b8dde27d01..97caa90e34288911b1a827e60c2569544d2b8f69 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_object.h
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_object.h
|
||||
@@ -123,9 +123,9 @@ class WebGLObject : public ScriptWrappable {
|
||||
|
||||
GLuint object_ = 0;
|
||||
|
||||
- // This was the number of context losses of the object's associated
|
||||
- // WebGLContext at the time this object was created.
|
||||
- uint32_t cached_number_of_context_losses_;
|
||||
+ // The context generation number of the associated WebGLContext when the
|
||||
+ // object was created, to prevent reuse in later generations.
|
||||
+ uint64_t context_generation_at_creation_;
|
||||
|
||||
unsigned attachment_count_ = 0;
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
|
||||
index 060563a9955a8564d176177fc389c4f98aa64e9f..f24221cb2f47cfde515179ff945c13756487ebfc 100644
|
||||
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
|
||||
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
|
||||
@@ -2073,8 +2073,6 @@ class MODULES_EXPORT WebGLRenderingContextBase
|
||||
|
||||
bool has_been_drawn_to_ = false;
|
||||
|
||||
- uint32_t number_of_context_losses_ = 0;
|
||||
-
|
||||
// Tracks if the context has ever called glBeginPixelLocalStorageANGLE. If it
|
||||
// has, we need to start using the pixel local storage interrupt mechanism
|
||||
// when we take over the client's context.
|
||||
219
patches/chromium/cherry-pick-5efc7a0127a6.patch
Normal file
219
patches/chromium/cherry-pick-5efc7a0127a6.patch
Normal file
@@ -0,0 +1,219 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Date: Wed, 25 Feb 2026 06:21:21 -0800
|
||||
Subject: Validate CSSUnparsedValues upon assignment
|
||||
|
||||
CSS Typed OM has a concept of a value "matching a grammar" (or not)
|
||||
upon assignment to a property [1]. For CSSUnparsedValues, we currently
|
||||
don't perform any significant validation, and as a consequence
|
||||
we allow "invalid" CSSUnparsedDeclarationValues to be created
|
||||
(causing DCHECKs later in the pipeline).
|
||||
|
||||
This CL makes sure values can be parsed using CSSVariableParser::
|
||||
ConsumeUnparsedDeclaration before assignment.
|
||||
|
||||
We're still not handling the value in the context of the destination
|
||||
property, which we probably should. This is also a problem with
|
||||
current state of things, however, so for now the goal is primarily
|
||||
to avoid the DCHECKs in Issue 484751092.
|
||||
|
||||
Finally, I opened an issue against the specification [2], which
|
||||
currently doesn't define any of this.
|
||||
|
||||
[1] https://drafts.css-houdini.org/css-typed-om-1/#create-an-internal-representation
|
||||
[2] https://github.com/w3c/csswg-drafts/issues/13547
|
||||
|
||||
Fixed: 484751092
|
||||
Change-Id: Id7f888a6df8c02ade24910900f5d01909cb2dfad
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7595347
|
||||
Reviewed-by: Steinar H Gunderson <sesse@chromium.org>
|
||||
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1590110}
|
||||
|
||||
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl
|
||||
index edfa73a57d30ebd4f9a7147702df42b836f7d82b..4442ba0872ca4c739596b546e6d3b600c5a31598 100644
|
||||
--- a/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl
|
||||
+++ b/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_keyword_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_numeric_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_style_value.h"
|
||||
+#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unsupported_style_value.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/cssom_keywords.h"
|
||||
#include "third_party/blink/renderer/core/css/properties/css_property.h"
|
||||
@@ -105,8 +106,8 @@ bool CSSOMTypes::PropertyCanTake(CSSPropertyID id,
|
||||
: CSSPropertyName(id);
|
||||
return unsupported_style_value->IsValidFor(name);
|
||||
}
|
||||
- if (value.GetType() == CSSStyleValue::kUnparsedType) {
|
||||
- return true;
|
||||
+ if (auto* unparsed_value = DynamicTo<CSSUnparsedValue>(value)) {
|
||||
+ return unparsed_value->IsValidDeclarationValue();
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
index 12d70ed096cb1c509a2acf14b7f421273d833d0e..5f9d6a39effe207e44dd84cececebdb6c666f011 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.cc
|
||||
@@ -4,11 +4,13 @@
|
||||
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_unparsed_value.h"
|
||||
|
||||
+#include "css_style_value.h"
|
||||
#include "third_party/blink/renderer/core/css/css_unparsed_declaration_value.h"
|
||||
#include "third_party/blink/renderer/core/css/css_variable_data.h"
|
||||
#include "third_party/blink/renderer/core/css/cssom/css_style_variable_reference_value.h"
|
||||
#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
|
||||
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
|
||||
+#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
|
||||
#include "third_party/blink/renderer/core/css_value_keywords.h"
|
||||
#include "third_party/blink/renderer/platform/bindings/exception_messages.h"
|
||||
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
|
||||
@@ -136,6 +138,10 @@ IndexedPropertySetterResult CSSUnparsedValue::AnonymousIndexedSetter(
|
||||
return IndexedPropertySetterResult::kIntercepted;
|
||||
}
|
||||
|
||||
+bool CSSUnparsedValue::IsValidDeclarationValue() const {
|
||||
+ return IsValidDeclarationValue(ToStringInternal());
|
||||
+}
|
||||
+
|
||||
const CSSValue* CSSUnparsedValue::ToCSSValue() const {
|
||||
String unparsed_string = ToStringInternal();
|
||||
|
||||
@@ -144,12 +150,40 @@ const CSSValue* CSSUnparsedValue::ToCSSValue() const {
|
||||
MakeGarbageCollected<CSSVariableData>());
|
||||
}
|
||||
|
||||
+ CHECK(IsValidDeclarationValue(unparsed_string));
|
||||
+ // The call to IsValidDeclarationValue() above also creates a CSSVariableData
|
||||
+ // to carry out its check. It would be nice to use that here, but WPTs
|
||||
+ // expect leading whitespace to be preserved, even though it's not possible
|
||||
+ // to create such declaration values normally.
|
||||
+ CSSVariableData* variable_data =
|
||||
+ CSSVariableData::Create(unparsed_string,
|
||||
+ /*is_animation_tainted=*/false,
|
||||
+ /*is_attr_tainted=*/false,
|
||||
+ /*needs_variable_resolution=*/false);
|
||||
+
|
||||
// TODO(crbug.com/985028): We should probably propagate the CSSParserContext
|
||||
// to here.
|
||||
- return MakeGarbageCollected<CSSUnparsedDeclarationValue>(
|
||||
- CSSVariableData::Create(unparsed_string, false /* is_animation_tainted */,
|
||||
- false /* is_attr_tainted */,
|
||||
- false /* needs_variable_resolution */));
|
||||
+ return MakeGarbageCollected<CSSUnparsedDeclarationValue>(variable_data);
|
||||
+}
|
||||
+
|
||||
+bool CSSUnparsedValue::IsValidDeclarationValue(const String& string) {
|
||||
+ CSSParserTokenStream stream(string);
|
||||
+ bool important_unused;
|
||||
+ // This checks that the value does not violate the "argument grammar" [1]
|
||||
+ // of any substitution functions, and that it is a valid <declaration-value>
|
||||
+ // otherwise.
|
||||
+ //
|
||||
+ // [1] https://drafts.csswg.org/css-values-5/#argument-grammar
|
||||
+ //
|
||||
+ // TODO(andruud): 'restricted_value' depends on the destination property.
|
||||
+ return CSSVariableParser::ConsumeUnparsedDeclaration(
|
||||
+ stream,
|
||||
+ /*allow_important_annotation=*/false,
|
||||
+ /*is_animation_tainted=*/false,
|
||||
+ /*must_contain_variable_reference=*/false,
|
||||
+ /*restricted_value=*/false,
|
||||
+ /*comma_ends_declaration=*/false, important_unused,
|
||||
+ *StrictCSSParserContext(SecureContextMode::kInsecureContext));
|
||||
}
|
||||
|
||||
String CSSUnparsedValue::ToStringInternal() const {
|
||||
diff --git a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
index ec7e3ed708f406d7a61fdb370b2eed8a8297cffb..7fd66aed677e31046a1bd206854b2cbeac07c25b 100644
|
||||
--- a/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
+++ b/third_party/blink/renderer/core/css/cssom/css_unparsed_value.h
|
||||
@@ -48,6 +48,14 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
CSSUnparsedValue(const CSSUnparsedValue&) = delete;
|
||||
CSSUnparsedValue& operator=(const CSSUnparsedValue&) = delete;
|
||||
|
||||
+ // True if this CSSUnparsedValue can be converted into
|
||||
+ // a CSSUnparsedDeclarationValue.
|
||||
+ //
|
||||
+ // We may want to ban some invalid values earlier, see:
|
||||
+ // https://github.com/w3c/csswg-drafts/issues/13547
|
||||
+ bool IsValidDeclarationValue() const;
|
||||
+
|
||||
+ // Requires IsValidDeclarationValue()==true.
|
||||
const CSSValue* ToCSSValue() const override;
|
||||
|
||||
StyleValueType GetType() const override { return kUnparsedType; }
|
||||
@@ -68,6 +76,7 @@ class CORE_EXPORT CSSUnparsedValue final : public CSSStyleValue {
|
||||
}
|
||||
|
||||
private:
|
||||
+ static bool IsValidDeclarationValue(const String&);
|
||||
String ToStringInternal() const;
|
||||
String SerializeSegments() const;
|
||||
// Return 'false' if there is a cycle in the serialization.
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/missing-variable-in-unparsed-value-crash.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/missing-variable-in-unparsed-value-crash.html
|
||||
deleted file mode 100644
|
||||
index b92bd62deb71f2623b0265bed099d739cd1fce3a..0000000000000000000000000000000000000000
|
||||
--- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/missing-variable-in-unparsed-value-crash.html
|
||||
+++ /dev/null
|
||||
@@ -1,12 +0,0 @@
|
||||
-<!DOCTYPE html>
|
||||
-<title>Crash Test: Missing variable name in CSSUnparsedValue</title>
|
||||
-<link rel="help" href="https://issues.chromium.org/issues/484811719">
|
||||
-<div id="div"></div>
|
||||
-<script>
|
||||
- for (let i = 0; i < 5000; ++i) {
|
||||
- const bad = new CSSUnparsedValue(['var(,)']);
|
||||
- div.attributeStyleMap.set('--x', bad);
|
||||
- div.attributeStyleMap.get('--x');
|
||||
- }
|
||||
-</script>
|
||||
-<p>PASS if no crash</p>
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/set-invalid-untyped-value-crash.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/set-invalid-untyped-value-crash.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..ce618bf38fe651297b969ffdc16e212dee6a3688
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/set-invalid-untyped-value-crash.html
|
||||
@@ -0,0 +1,39 @@
|
||||
+<!DOCTYPE html>
|
||||
+<title>Crash when setting invalid CSSUnparsedValue</title>
|
||||
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/13547">
|
||||
+<div id=target></div>
|
||||
+<script>
|
||||
+ let examples = [
|
||||
+ 'var()',
|
||||
+ 'var(,)',
|
||||
+ 'var(0)',
|
||||
+ 'env()',
|
||||
+ 'env(,)',
|
||||
+ 'env(0)',
|
||||
+ 'attr()',
|
||||
+ 'attr(,)',
|
||||
+ 'attr(0)',
|
||||
+ 'if()',
|
||||
+ 'if(,)',
|
||||
+ 'if(0)',
|
||||
+ '--f()',
|
||||
+ '--f(,)',
|
||||
+ '--f(0)',
|
||||
+ 'thing!!!',
|
||||
+ 'var(--x) !important',
|
||||
+ ];
|
||||
+ // Some of the above cases may be valid. That's fine; just don't crash.
|
||||
+
|
||||
+ for (let e of examples) {
|
||||
+ try {
|
||||
+ let value = new CSSUnparsedValue([e]);
|
||||
+ target.attributeStyleMap.set('width', value);
|
||||
+ // One of the two above statements should likely throw an exception.
|
||||
+ // If they don't, then we should at least not crash on get():
|
||||
+ target.attributeStyleMap.get('width');
|
||||
+ } catch (e) {
|
||||
+ // Intentionally empty.
|
||||
+ }
|
||||
+ target.offsetTop;
|
||||
+ }
|
||||
+</script>
|
||||
@@ -1,132 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dominik=20R=C3=B6ttsches?= <drott@chromium.org>
|
||||
Date: Thu, 12 Feb 2026 06:35:36 -0800
|
||||
Subject: Avoid stale iteration in CSSFontFeatureValuesMap
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
To avoid invalid iterator state, take a snapshot of the
|
||||
map when creating the iteration source. This addresses
|
||||
the immediate problem of iterating while modifying.
|
||||
|
||||
Remaining work tracked in https://crbug.com/483936078
|
||||
|
||||
Fixed: 483569511
|
||||
Change-Id: Ie29cfdf7ed94bbe189b44c842a5efce571bb2cee
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7566570
|
||||
Commit-Queue: Dominik Röttsches <drott@chromium.org>
|
||||
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1583927}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/css/css_font_feature_values_map.cc b/third_party/blink/renderer/core/css/css_font_feature_values_map.cc
|
||||
index 0c5990799fbfdff5f1d1e04a9038a471217ad0d2..2ea27901e3ba503e7e1acc5dacf90dc60d52ac1a 100644
|
||||
--- a/third_party/blink/renderer/core/css/css_font_feature_values_map.cc
|
||||
+++ b/third_party/blink/renderer/core/css/css_font_feature_values_map.cc
|
||||
@@ -13,16 +13,15 @@ class FontFeatureValuesMapIterationSource final
|
||||
: public PairSyncIterable<CSSFontFeatureValuesMap>::IterationSource {
|
||||
public:
|
||||
FontFeatureValuesMapIterationSource(const CSSFontFeatureValuesMap& map,
|
||||
- const FontFeatureAliases* aliases)
|
||||
- : map_(map), aliases_(aliases), iterator_(aliases->begin()) {}
|
||||
+ const FontFeatureAliases aliases)
|
||||
+ : map_(map),
|
||||
+ aliases_(std::move(aliases)),
|
||||
+ iterator_(aliases_.begin()) {}
|
||||
|
||||
bool FetchNextItem(ScriptState* script_state,
|
||||
String& map_key,
|
||||
Vector<uint32_t>& map_value) override {
|
||||
- if (!aliases_) {
|
||||
- return false;
|
||||
- }
|
||||
- if (iterator_ == aliases_->end()) {
|
||||
+ if (iterator_ == aliases_.end()) {
|
||||
return false;
|
||||
}
|
||||
map_key = iterator_->key;
|
||||
@@ -37,9 +36,13 @@ class FontFeatureValuesMapIterationSource final
|
||||
}
|
||||
|
||||
private:
|
||||
- // Needs to be kept alive while we're iterating over it.
|
||||
const Member<const CSSFontFeatureValuesMap> map_;
|
||||
- const FontFeatureAliases* aliases_;
|
||||
+ // Create a copy to keep the iterator from becoming invalid if there are
|
||||
+ // modifications to the aliases HashMap while iterating.
|
||||
+ // TODO(https://crbug.com/483936078): Implement live/stable iteration over
|
||||
+ // FontFeatureAliases by changing its storage type, avoiding taking a copy
|
||||
+ // here.
|
||||
+ const FontFeatureAliases aliases_;
|
||||
FontFeatureAliases::const_iterator iterator_;
|
||||
};
|
||||
|
||||
@@ -49,8 +52,8 @@ uint32_t CSSFontFeatureValuesMap::size() const {
|
||||
|
||||
PairSyncIterable<CSSFontFeatureValuesMap>::IterationSource*
|
||||
CSSFontFeatureValuesMap::CreateIterationSource(ScriptState*) {
|
||||
- return MakeGarbageCollected<FontFeatureValuesMapIterationSource>(*this,
|
||||
- aliases_);
|
||||
+ return MakeGarbageCollected<FontFeatureValuesMapIterationSource>(
|
||||
+ *this, aliases_ ? *aliases_ : FontFeatureAliases());
|
||||
}
|
||||
|
||||
bool CSSFontFeatureValuesMap::GetMapEntry(ScriptState*,
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/font_feature_values_map_iteration.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/font_feature_values_map_iteration.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..eac7198b0b4a58007cbcc77ad3e9357a1009117c
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/font_feature_values_map_iteration.html
|
||||
@@ -0,0 +1,52 @@
|
||||
+<!DOCTYPE html>
|
||||
+<html>
|
||||
+ <head>
|
||||
+ <title>CSSFontFeatureValuesMap Iteration and Modification</title>
|
||||
+ <link
|
||||
+ rel="help"
|
||||
+ href="https://drafts.csswg.org/css-fonts-4/#om-fontfeaturevalues"
|
||||
+ />
|
||||
+ <meta
|
||||
+ name="assert"
|
||||
+ content="Iteration while modifying CSSFontFeatureValuesMap does not crash."
|
||||
+ />
|
||||
+ <script type="text/javascript" src="/resources/testharness.js"></script>
|
||||
+ <script
|
||||
+ type="text/javascript"
|
||||
+ src="/resources/testharnessreport.js"
|
||||
+ ></script>
|
||||
+ </head>
|
||||
+ <body>
|
||||
+ <style>
|
||||
+ @font-feature-values TestFont {
|
||||
+ @styleset {
|
||||
+ a: 1;
|
||||
+ b: 2;
|
||||
+ c: 3;
|
||||
+ }
|
||||
+ }
|
||||
+ </style>
|
||||
+ <script>
|
||||
+ test(() => {
|
||||
+ const rule = document.styleSheets[0].cssRules[0];
|
||||
+ const map = rule.styleset;
|
||||
+ const iterator = map.entries();
|
||||
+ let count = 0;
|
||||
+
|
||||
+ while (count < 10) {
|
||||
+ const { value: entry, done } = iterator.next();
|
||||
+ if (done) break;
|
||||
+
|
||||
+ const [key, value] = entry;
|
||||
+
|
||||
+ map.delete(key);
|
||||
+ for (let i = 0; i < 100; i++) {
|
||||
+ map.set(`newkey_${count}_${i}`, i);
|
||||
+ }
|
||||
+
|
||||
+ count++;
|
||||
+ }
|
||||
+ }, "Iteration of the CSSFontFeatureValuesMap does not crash.");
|
||||
+ </script>
|
||||
+ </body>
|
||||
+</html>
|
||||
65
patches/chromium/cherry-pick-fbfb27470bf6.patch
Normal file
65
patches/chromium/cherry-pick-fbfb27470bf6.patch
Normal file
@@ -0,0 +1,65 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Geoff Lang <geofflang@chromium.org>
|
||||
Date: Wed, 18 Feb 2026 13:54:37 -0800
|
||||
Subject: Validate uniform block count limits at compile time on IMG.
|
||||
|
||||
Normally these limits are validated at link time but the IMG compiler
|
||||
has issues when these limits are exceeded. Validate at compile time
|
||||
instead.
|
||||
|
||||
Bug: chromium:475877320
|
||||
Change-Id: Ieeed6914b8cdd2b5e50242d06facae62badddefd
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7568129
|
||||
Auto-Submit: Geoff Lang <geofflang@chromium.org>
|
||||
Reviewed-by: Kyle Charbonneau <kylechar@chromium.org>
|
||||
Commit-Queue: Kyle Charbonneau <kylechar@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1586673}
|
||||
|
||||
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
|
||||
index 895266f5988e2446eadf3d3e1d5c4919416cba76..aeafafece78faa6ece837386fe170592589d1b10 100644
|
||||
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
|
||||
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
|
||||
@@ -3678,6 +3678,9 @@ bool GLES2DecoderImpl::InitializeShaderTranslator() {
|
||||
driver_bug_workarounds.dontUseLoopsToInitializeVariables = true;
|
||||
if (workarounds().remove_dynamic_indexing_of_swizzled_vector)
|
||||
driver_bug_workarounds.removeDynamicIndexingOfSwizzledVector = true;
|
||||
+ if (workarounds().validate_max_per_stage_uniform_blocks_at_compile_time) {
|
||||
+ driver_bug_workarounds.validatePerStageMaxUniformBlocks = true;
|
||||
+ }
|
||||
|
||||
// Initialize uninitialized locals by default
|
||||
driver_bug_workarounds.initializeUninitializedLocals = true;
|
||||
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
|
||||
index 2af5b0460beed7b78c00c9f2a70e14e5f7696ac0..3cc48e4a79576f93bfa22b8f8d1b74e5ba5baae7 100644
|
||||
--- a/gpu/config/gpu_driver_bug_list.json
|
||||
+++ b/gpu/config/gpu_driver_bug_list.json
|
||||
@@ -3843,6 +3843,17 @@
|
||||
"features": [
|
||||
"ensure_previous_framebuffer_not_deleted"
|
||||
]
|
||||
+ },
|
||||
+ {
|
||||
+ "id": 472,
|
||||
+ "description": "Validate GL_MAX_*_UNIFORM_BLOCKS at compile time instead of link time to work around compiler bugs.",
|
||||
+ "os": {
|
||||
+ "type": "android"
|
||||
+ },
|
||||
+ "gl_vendor": "Imagination.*",
|
||||
+ "features": [
|
||||
+ "validate_max_per_stage_uniform_blocks_at_compile_time"
|
||||
+ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt
|
||||
index 30ee7799cdd0a344e433e95cd74e4630a7c87aff..6d43d0b1937cd1eeaa044fb9949a52ba2e07d446 100644
|
||||
--- a/gpu/config/gpu_workaround_list.txt
|
||||
+++ b/gpu/config/gpu_workaround_list.txt
|
||||
@@ -126,6 +126,7 @@ use_first_valid_ref_for_av1_invalid_ref
|
||||
use_gpu_driver_workaround_for_testing
|
||||
use_non_zero_size_for_client_side_stream_buffers
|
||||
use_virtualized_gl_contexts
|
||||
+validate_max_per_stage_uniform_blocks_at_compile_time
|
||||
wake_up_gpu_before_drawing
|
||||
webgl_or_caps_max_texture_size_limit_4096
|
||||
webgl_or_caps_max_texture_size_limit_8192
|
||||
@@ -65,7 +65,7 @@ index 2748dd196fe1f56357348a204e24f0b8a28b97dd..5800dd00b47c657d9e6766f3fc5a3065
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
bool EscapeVirtualization(const base::FilePath& user_data_dir);
|
||||
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
|
||||
index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0aa3183a0b 100644
|
||||
index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..144788ceadea85c9d1fae12d1ba4dbc1fc7cd699 100644
|
||||
--- a/chrome/browser/process_singleton_posix.cc
|
||||
+++ b/chrome/browser/process_singleton_posix.cc
|
||||
@@ -619,6 +619,7 @@ class ProcessSingleton::LinuxWatcher
|
||||
@@ -106,22 +106,41 @@ index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0a
|
||||
const size_t kMinMessageLength = kStartToken.length() + 4;
|
||||
if (bytes_read_ < kMinMessageLength) {
|
||||
buf_[bytes_read_] = 0;
|
||||
@@ -745,10 +751,26 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
|
||||
@@ -745,10 +751,45 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
|
||||
tokens.erase(tokens.begin());
|
||||
tokens.erase(tokens.begin());
|
||||
|
||||
+ size_t num_args;
|
||||
+ base::StringToSizeT(tokens[0], &num_args);
|
||||
+ std::vector<std::string> command_line(tokens.begin() + 1, tokens.begin() + 1 + num_args);
|
||||
+ if (!base::StringToSizeT(tokens[0], &num_args) ||
|
||||
+ num_args > tokens.size() - 1) {
|
||||
+ LOG(ERROR) << "Invalid num_args in socket message";
|
||||
+ CleanupAndDeleteSelf();
|
||||
+ return;
|
||||
+ }
|
||||
+ std::vector<std::string> command_line(tokens.begin() + 1,
|
||||
+ tokens.begin() + 1 + num_args);
|
||||
+
|
||||
+ std::vector<uint8_t> additional_data;
|
||||
+ if (tokens.size() >= 3 + num_args) {
|
||||
+ // After consuming [num_args, argv...], two more tokens are needed for
|
||||
+ // additional data: [size, payload]. Subtract to avoid overflow when
|
||||
+ // num_args is large.
|
||||
+ if (tokens.size() - 1 - num_args >= 2) {
|
||||
+ size_t additional_data_size;
|
||||
+ base::StringToSizeT(tokens[1 + num_args], &additional_data_size);
|
||||
+ if (!base::StringToSizeT(tokens[1 + num_args], &additional_data_size)) {
|
||||
+ LOG(ERROR) << "Invalid additional_data_size in socket message";
|
||||
+ CleanupAndDeleteSelf();
|
||||
+ return;
|
||||
+ }
|
||||
+ std::string remaining_args = base::JoinString(
|
||||
+ base::span(tokens).subspan(2 + num_args),
|
||||
+ std::string(1, kTokenDelimiter));
|
||||
+ const auto adspan = base::as_byte_span(remaining_args).first(additional_data_size);
|
||||
+ if (additional_data_size > remaining_args.size()) {
|
||||
+ LOG(ERROR) << "additional_data_size exceeds payload length";
|
||||
+ CleanupAndDeleteSelf();
|
||||
+ return;
|
||||
+ }
|
||||
+ const auto adspan =
|
||||
+ base::as_byte_span(remaining_args).first(additional_data_size);
|
||||
+ additional_data.assign(adspan.begin(), adspan.end());
|
||||
+ }
|
||||
+
|
||||
@@ -134,7 +153,7 @@ index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0a
|
||||
fd_watch_controller_.reset();
|
||||
|
||||
// LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
|
||||
@@ -777,8 +799,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
|
||||
@@ -777,8 +818,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
|
||||
//
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const base::FilePath& user_data_dir,
|
||||
@@ -145,7 +164,7 @@ index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0a
|
||||
current_pid_(base::GetCurrentProcId()) {
|
||||
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
|
||||
lock_path_ = user_data_dir.Append(chrome::kSingletonLockFilename);
|
||||
@@ -899,7 +923,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
@@ -899,7 +942,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
sizeof(socket_timeout));
|
||||
|
||||
// Found another process, prepare our command line
|
||||
@@ -155,7 +174,7 @@ index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0a
|
||||
std::string to_send(kStartToken);
|
||||
to_send.push_back(kTokenDelimiter);
|
||||
|
||||
@@ -909,11 +934,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
@@ -909,11 +953,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
to_send.append(current_dir.value());
|
||||
|
||||
const std::vector<std::string>& argv = cmd_line.argv();
|
||||
@@ -178,10 +197,18 @@ index 73aa4cb9652870b0bff4684d7c72ae7dbd852db8..b55c942a8ccb326e4898172a7b4f6c0a
|
||||
if (!WriteToSocket(socket.fd(), to_send)) {
|
||||
// Try to kill the other process, because it might have been dead.
|
||||
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
|
||||
index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f947e6bfc 100644
|
||||
index ae659d84a5ae2f2e87ce288477506575f8d86839..274887d62ff8d008bb86815a11205fcaa5f2c2ff 100644
|
||||
--- a/chrome/browser/process_singleton_win.cc
|
||||
+++ b/chrome/browser/process_singleton_win.cc
|
||||
@@ -81,10 +81,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <shellapi.h>
|
||||
#include <stddef.h>
|
||||
|
||||
+#include "base/base64.h"
|
||||
#include "base/base_paths.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
@@ -81,10 +82,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
|
||||
|
||||
bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||
base::CommandLine* parsed_command_line,
|
||||
@@ -196,7 +223,7 @@ index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f
|
||||
static const int min_message_size = 7;
|
||||
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
|
||||
cds->cbData % sizeof(wchar_t) != 0) {
|
||||
@@ -134,6 +136,23 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||
@@ -134,6 +137,25 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||
const std::wstring cmd_line =
|
||||
msg.substr(second_null + 1, third_null - second_null);
|
||||
*parsed_command_line = base::CommandLine::FromString(cmd_line);
|
||||
@@ -209,18 +236,20 @@ index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // Get the actual additional data.
|
||||
+ const std::wstring additional_data =
|
||||
+ msg.substr(third_null + 1, fourth_null - third_null);
|
||||
+ base::span<const uint8_t> additional_data_bytes =
|
||||
+ base::as_byte_span(additional_data);
|
||||
+ *parsed_additional_data = std::vector<uint8_t>(
|
||||
+ additional_data_bytes.begin(), additional_data_bytes.end());
|
||||
+ // Get the actual additional data. It is base64-encoded so it can
|
||||
+ // safely traverse the null-delimited wchar_t buffer.
|
||||
+ const std::wstring encoded_w =
|
||||
+ msg.substr(third_null + 1, fourth_null - third_null - 1);
|
||||
+ std::string encoded = base::WideToASCII(encoded_w);
|
||||
+ std::optional<std::vector<uint8_t>> decoded = base::Base64Decode(encoded);
|
||||
+ if (decoded) {
|
||||
+ *parsed_additional_data = std::move(*decoded);
|
||||
+ }
|
||||
+
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -155,13 +174,14 @@ bool ProcessLaunchNotification(
|
||||
@@ -155,13 +177,14 @@ bool ProcessLaunchNotification(
|
||||
|
||||
base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
|
||||
base::FilePath current_directory;
|
||||
@@ -238,7 +267,7 @@ index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -265,9 +285,11 @@ bool ProcessSingleton::EscapeVirtualization(
|
||||
@@ -265,9 +288,11 @@ bool ProcessSingleton::EscapeVirtualization(
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const std::string& program_name,
|
||||
const base::FilePath& user_data_dir,
|
||||
@@ -250,7 +279,7 @@ index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f
|
||||
program_name_(program_name),
|
||||
is_app_sandboxed_(is_app_sandboxed),
|
||||
is_virtualized_(false),
|
||||
@@ -294,7 +316,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
||||
@@ -294,7 +319,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
||||
return PROCESS_NONE;
|
||||
}
|
||||
|
||||
@@ -260,10 +289,18 @@ index ae659d84a5ae2f2e87ce288477506575f8d86839..d93c7e8487ab1a2bbb5f56f2ca44868f
|
||||
return PROCESS_NOTIFIED;
|
||||
case NotifyChromeResult::kFailed:
|
||||
diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
|
||||
index 594f3bc08a4385c177fb488123cef79448e94850..5a1dde19a4bc2bf728eba4c738f831c3e5b73942 100644
|
||||
index 594f3bc08a4385c177fb488123cef79448e94850..28e5a18a19718b2e748ada6882341413a1ab0705 100644
|
||||
--- a/chrome/browser/win/chrome_process_finder.cc
|
||||
+++ b/chrome/browser/win/chrome_process_finder.cc
|
||||
@@ -39,7 +39,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
+#include "base/base64.h"
|
||||
#include "base/check.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
@@ -39,7 +40,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
|
||||
return base::win::MessageWindow::FindWindow(user_data_dir.value());
|
||||
}
|
||||
|
||||
@@ -274,7 +311,7 @@ index 594f3bc08a4385c177fb488123cef79448e94850..5a1dde19a4bc2bf728eba4c738f831c3
|
||||
TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome");
|
||||
|
||||
DCHECK(remote_window);
|
||||
@@ -70,12 +72,24 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
@@ -70,12 +73,22 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
new_command_line.AppendSwitch(switches::kSourceAppId);
|
||||
}
|
||||
// Send the command line to the remote chrome window.
|
||||
@@ -286,14 +323,12 @@ index 594f3bc08a4385c177fb488123cef79448e94850..5a1dde19a4bc2bf728eba4c738f831c3
|
||||
std::wstring_view{L"\0", 1}, new_command_line.GetCommandLineString(),
|
||||
std::wstring_view{L"\0", 1}});
|
||||
|
||||
+ size_t additional_data_size = additional_data.size_bytes();
|
||||
+ if (additional_data_size) {
|
||||
+ size_t padded_size = additional_data_size / sizeof(wchar_t);
|
||||
+ if (additional_data_size % sizeof(wchar_t) != 0) {
|
||||
+ padded_size++;
|
||||
+ }
|
||||
+ to_send.append(reinterpret_cast<const wchar_t*>(additional_data.data()),
|
||||
+ padded_size);
|
||||
+ if (!additional_data.empty()) {
|
||||
+ // Base64-encode so the payload survives the null-delimited wchar_t
|
||||
+ // framing; raw serialized bytes can contain 0x0000 sequences which
|
||||
+ // would otherwise terminate the field early.
|
||||
+ std::string encoded = base::Base64Encode(additional_data);
|
||||
+ to_send.append(base::ASCIIToWide(encoded));
|
||||
+ to_send.append(L"\0", 1); // Null separator.
|
||||
+ }
|
||||
+
|
||||
|
||||
@@ -12,7 +12,7 @@ We attempt to migrate the safe storage key from the old account, if that migrati
|
||||
Existing apps that aren't built for the app store should be unimpacted, there is one edge case where a user uses BOTH an AppStore and a darwin build of the same app only one will keep it's access to the safestorage key as during the migration we delete the old account. This is an acceptable edge case as no one should be actively using two versions of the same app.
|
||||
|
||||
diff --git a/components/os_crypt/common/keychain_password_mac.mm b/components/os_crypt/common/keychain_password_mac.mm
|
||||
index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50ab1e8f05d 100644
|
||||
index f19628cc0cdba39b232f55935e8eee9786b02a77..ab6ce9b1cf85029050a7497cd53813a03a46d408 100644
|
||||
--- a/components/os_crypt/common/keychain_password_mac.mm
|
||||
+++ b/components/os_crypt/common/keychain_password_mac.mm
|
||||
@@ -27,6 +27,12 @@
|
||||
@@ -28,14 +28,13 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
|
||||
namespace {
|
||||
|
||||
// These two strings ARE indeed user facing. But they are used to access
|
||||
@@ -96,11 +102,51 @@
|
||||
@@ -96,18 +102,56 @@
|
||||
uma_result);
|
||||
};
|
||||
|
||||
+ const std::string account_name_suffix = kAccountNameSuffix;
|
||||
+ const std::string suffixed_account_name = GetAccountName() + account_name_suffix;
|
||||
auto password =
|
||||
- keychain_->FindGenericPassword(GetServiceName(), GetAccountName());
|
||||
+ keychain_->FindGenericPassword(GetServiceName(), suffixed_account_name);
|
||||
+
|
||||
+ if (password.has_value()) {
|
||||
@@ -53,10 +52,8 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
|
||||
+
|
||||
+ // If the suffixed account didn't exist, we should check if the legacy non-suffixed account
|
||||
+ // exists. If it does we can use that key and migrate it to the new account
|
||||
+ base::apple::ScopedCFTypeRef<SecKeychainItemRef> item_ref;
|
||||
+ password =
|
||||
+ keychain_->FindGenericPassword(GetServiceName(), GetAccountName(),
|
||||
+ item_ref.InitializeInto());
|
||||
keychain_->FindGenericPassword(GetServiceName(), GetAccountName());
|
||||
|
||||
if (password.has_value()) {
|
||||
uma_result = FindGenericPasswordResult::kPasswordFound;
|
||||
@@ -70,7 +67,7 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
|
||||
+ // If we successfully made the suffixed account we can delete the old
|
||||
+ // account to ensure new apps don't try to use it and run into IAM
|
||||
+ // issues
|
||||
+ error = keychain_->ItemDelete(item_ref.get());
|
||||
+ error = keychain_->ItemDelete(GetServiceName(), GetAccountName());
|
||||
+ if (error != noErr) {
|
||||
+ OSSTATUS_DLOG(ERROR, error) << "Keychain delete for legacy key failed";
|
||||
+ }
|
||||
@@ -81,115 +78,79 @@ index f19628cc0cdba39b232f55935e8eee9786b02a77..036b50f53e78bc21ed1e1d6dd876b50a
|
||||
return std::string(base::as_string_view(*password));
|
||||
}
|
||||
|
||||
if (password.error() == errSecItemNotFound) {
|
||||
uma_result = FindGenericPasswordResult::kPasswordNotFound;
|
||||
return AddRandomPasswordToKeychain(*keychain_, GetServiceName(),
|
||||
- GetAccountName());
|
||||
+ suffixed_account_name);
|
||||
}
|
||||
|
||||
OSSTATUS_LOG(ERROR, password.error()) << "Keychain lookup failed";
|
||||
diff --git a/crypto/apple/keychain.h b/crypto/apple/keychain.h
|
||||
index 1d2264a5229206f45d1a9bcb009d47180efa6a8b..1dcf2b1d09831012c7f5768a5c6193d529efc821 100644
|
||||
index 1d2264a5229206f45d1a9bcb009d47180efa6a8b..4472e5b605e09659bd75cd4797f073775fe4b354 100644
|
||||
--- a/crypto/apple/keychain.h
|
||||
+++ b/crypto/apple/keychain.h
|
||||
@@ -17,6 +17,14 @@
|
||||
|
||||
namespace crypto::apple {
|
||||
|
||||
+// TODO(smaddock): Migrate to SecItem* as part of
|
||||
+// https://issues.chromium.org/issues/40233280
|
||||
+#if BUILDFLAG(IS_IOS)
|
||||
+using AppleSecKeychainItemRef = void*;
|
||||
+#else
|
||||
+using AppleSecKeychainItemRef = SecKeychainItemRef;
|
||||
+#endif
|
||||
+
|
||||
// Wraps the KeychainServices API in a very thin layer, to allow it to be
|
||||
// mocked out for testing.
|
||||
|
||||
@@ -44,13 +52,18 @@ class CRYPTO_EXPORT Keychain {
|
||||
// std::vector<uint8_t> arm is populated instead.
|
||||
virtual base::expected<std::vector<uint8_t>, OSStatus> FindGenericPassword(
|
||||
std::string_view service_name,
|
||||
- std::string_view account_name) const = 0;
|
||||
+ std::string_view account_name,
|
||||
+ AppleSecKeychainItemRef* item = nullptr) const = 0;
|
||||
|
||||
virtual OSStatus AddGenericPassword(
|
||||
std::string_view service_name,
|
||||
@@ -51,6 +51,11 @@ class CRYPTO_EXPORT Keychain {
|
||||
std::string_view account_name,
|
||||
base::span<const uint8_t> password) const = 0;
|
||||
|
||||
+#if BUILDFLAG(IS_MAC)
|
||||
+ virtual OSStatus ItemDelete(AppleSecKeychainItemRef item) const = 0;
|
||||
+#endif // !BUILDFLAG(IS_MAC)
|
||||
+ virtual OSStatus ItemDelete(std::string_view service_name,
|
||||
+ std::string_view account_name) const = 0;
|
||||
+#endif // BUILDFLAG(IS_MAC)
|
||||
+
|
||||
protected:
|
||||
Keychain();
|
||||
};
|
||||
diff --git a/crypto/apple/keychain_secitem.h b/crypto/apple/keychain_secitem.h
|
||||
index eb74282adaba24ebd667f0ab3fc34dbe4cd8b527..7b91eb27489cece38eca719986255c5ec01c4bac 100644
|
||||
index eb74282adaba24ebd667f0ab3fc34dbe4cd8b527..0d25e49e2fa1b374d6867b8c602f7685a7f9498d 100644
|
||||
--- a/crypto/apple/keychain_secitem.h
|
||||
+++ b/crypto/apple/keychain_secitem.h
|
||||
@@ -17,12 +17,17 @@ class CRYPTO_EXPORT KeychainSecItem : public Keychain {
|
||||
|
||||
base::expected<std::vector<uint8_t>, OSStatus> FindGenericPassword(
|
||||
std::string_view service_name,
|
||||
- std::string_view account_name) const override;
|
||||
+ std::string_view account_name,
|
||||
+ AppleSecKeychainItemRef* item) const override;
|
||||
|
||||
OSStatus AddGenericPassword(
|
||||
@@ -23,6 +23,11 @@ class CRYPTO_EXPORT KeychainSecItem : public Keychain {
|
||||
std::string_view service_name,
|
||||
std::string_view account_name,
|
||||
base::span<const uint8_t> password) const override;
|
||||
+
|
||||
+#if BUILDFLAG(IS_MAC)
|
||||
+ OSStatus ItemDelete(AppleSecKeychainItemRef item) const override;
|
||||
+#endif // !BUILDFLAG(IS_MAC)
|
||||
+ OSStatus ItemDelete(std::string_view service_name,
|
||||
+ std::string_view account_name) const override;
|
||||
+#endif // BUILDFLAG(IS_MAC)
|
||||
};
|
||||
|
||||
} // namespace crypto::apple
|
||||
diff --git a/crypto/apple/keychain_secitem.mm b/crypto/apple/keychain_secitem.mm
|
||||
index a8d50dd27db52526b0635c2b97f076df1994a6aa..e45f0d1079c8acfae55cf873e66ab3d9a10ad8ee 100644
|
||||
index a8d50dd27db52526b0635c2b97f076df1994a6aa..464c17909b9a554b269a70ea08771da6ec7ac011 100644
|
||||
--- a/crypto/apple/keychain_secitem.mm
|
||||
+++ b/crypto/apple/keychain_secitem.mm
|
||||
@@ -138,7 +138,8 @@
|
||||
|
||||
base::expected<std::vector<uint8_t>, OSStatus>
|
||||
KeychainSecItem::FindGenericPassword(std::string_view service_name,
|
||||
- std::string_view account_name) const {
|
||||
+ std::string_view account_name,
|
||||
+ AppleSecKeychainItemRef* item) const {
|
||||
base::apple::ScopedCFTypeRef<CFDictionaryRef> query =
|
||||
MakeGenericPasswordQuery(service_name, account_name);
|
||||
|
||||
@@ -165,4 +166,13 @@
|
||||
@@ -165,4 +165,18 @@
|
||||
return base::ToVector(base::apple::CFDataToSpan(password_data));
|
||||
}
|
||||
|
||||
+#if BUILDFLAG(IS_MAC)
|
||||
+OSStatus KeychainSecItem::ItemDelete(AppleSecKeychainItemRef item) const {
|
||||
+ // TODO(smaddock): AppleSecKeychainItemRef aliases the deprecated
|
||||
+ // SecKeychainItemRef. Need to update this to accept a CFDictionary in the
|
||||
+ // case of SecItemDelete.
|
||||
+ return noErr;
|
||||
+OSStatus KeychainSecItem::ItemDelete(std::string_view service_name,
|
||||
+ std::string_view account_name) const {
|
||||
+ NSDictionary* query = @{
|
||||
+ CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassGenericPassword),
|
||||
+ CFToNSPtrCast(kSecAttrService) : base::SysUTF8ToNSString(service_name),
|
||||
+ CFToNSPtrCast(kSecAttrAccount) : base::SysUTF8ToNSString(account_name),
|
||||
+ };
|
||||
+ base::apple::ScopedCFTypeRef<CFDictionaryRef> cf_query(
|
||||
+ NSToCFOwnershipCast(query));
|
||||
+ return SecItemDelete(cf_query.get());
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
} // namespace crypto::apple
|
||||
diff --git a/crypto/apple/mock_keychain.cc b/crypto/apple/mock_keychain.cc
|
||||
index 080806aaf3fc10548b160850ad36ef3519ea2b6f..21f04059d67ba41118face6ee9327aa05e854362 100644
|
||||
index 080806aaf3fc10548b160850ad36ef3519ea2b6f..98625524b668b86c857d5a8910bfb53b3ab40575 100644
|
||||
--- a/crypto/apple/mock_keychain.cc
|
||||
+++ b/crypto/apple/mock_keychain.cc
|
||||
@@ -32,7 +32,8 @@ MockKeychain::~MockKeychain() = default;
|
||||
|
||||
base::expected<std::vector<uint8_t>, OSStatus>
|
||||
MockKeychain::FindGenericPassword(std::string_view service_name,
|
||||
- std::string_view account_name) const {
|
||||
+ std::string_view account_name,
|
||||
+ AppleSecKeychainItemRef* item) const {
|
||||
IncrementKeychainAccessHistogram();
|
||||
|
||||
// When simulating |noErr|, return canned |passwordData| and
|
||||
@@ -56,6 +57,10 @@ OSStatus MockKeychain::AddGenericPassword(
|
||||
@@ -56,6 +56,11 @@ OSStatus MockKeychain::AddGenericPassword(
|
||||
return noErr;
|
||||
}
|
||||
|
||||
+OSStatus MockKeychain::ItemDelete(SecKeychainItemRef itemRef) const {
|
||||
+OSStatus MockKeychain::ItemDelete(std::string_view service_name,
|
||||
+ std::string_view account_name) const {
|
||||
+ return noErr;
|
||||
+}
|
||||
+
|
||||
@@ -197,25 +158,17 @@ index 080806aaf3fc10548b160850ad36ef3519ea2b6f..21f04059d67ba41118face6ee9327aa0
|
||||
IncrementKeychainAccessHistogram();
|
||||
return kPassword;
|
||||
diff --git a/crypto/apple/mock_keychain.h b/crypto/apple/mock_keychain.h
|
||||
index 680efe0312c81449e069c19d9c6ef146da7834db..b49c2ba5f639344ab57e9f14c098effc38729d1f 100644
|
||||
index 680efe0312c81449e069c19d9c6ef146da7834db..102db6013b505fed32db176a90f5176118f62773 100644
|
||||
--- a/crypto/apple/mock_keychain.h
|
||||
+++ b/crypto/apple/mock_keychain.h
|
||||
@@ -36,13 +36,18 @@ class CRYPTO_EXPORT MockKeychain : public Keychain {
|
||||
// Keychain implementation.
|
||||
base::expected<std::vector<uint8_t>, OSStatus> FindGenericPassword(
|
||||
std::string_view service_name,
|
||||
- std::string_view account_name) const override;
|
||||
+ std::string_view account_name,
|
||||
+ AppleSecKeychainItemRef* item) const override;
|
||||
|
||||
OSStatus AddGenericPassword(
|
||||
std::string_view service_name,
|
||||
@@ -43,6 +43,11 @@ class CRYPTO_EXPORT MockKeychain : public Keychain {
|
||||
std::string_view account_name,
|
||||
base::span<const uint8_t> password) const override;
|
||||
|
||||
+#if !BUILDFLAG(IS_IOS)
|
||||
+ OSStatus ItemDelete(SecKeychainItemRef itemRef) const override;
|
||||
+#endif // !BUILDFLAG(IS_IOS)
|
||||
+#if BUILDFLAG(IS_MAC)
|
||||
+ OSStatus ItemDelete(std::string_view service_name,
|
||||
+ std::string_view account_name) const override;
|
||||
+#endif // BUILDFLAG(IS_MAC)
|
||||
+
|
||||
// Returns the password that OSCrypt uses to generate its encryption key.
|
||||
std::string GetEncryptionPassword() const;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samuel Attard <sattard@anthropic.com>
|
||||
Date: Sat, 7 Mar 2026 23:07:30 -0800
|
||||
Subject: feat: plumb node_integration_in_worker through WorkerSettings
|
||||
|
||||
Copy the node_integration_in_worker flag from the initiating frame's
|
||||
WebPreferences into WorkerSettings at dedicated worker creation time,
|
||||
so the value is readable per-worker on the worker thread rather than
|
||||
relying on a process-wide command line switch. The value is also
|
||||
propagated to nested workers via WorkerSettings::Copy.
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.cc b/third_party/blink/renderer/core/workers/dedicated_worker.cc
|
||||
index a0f78583334fdf4912b897e88d8ce518773dbfb1..300c5a3b806222e46388d2f0d906737cf282e52e 100644
|
||||
--- a/third_party/blink/renderer/core/workers/dedicated_worker.cc
|
||||
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.cc
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "third_party/blink/renderer/core/frame/local_frame_client.h"
|
||||
#include "third_party/blink/renderer/core/frame/web_frame_widget_impl.h"
|
||||
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
|
||||
+#include "third_party/blink/renderer/core/exported/web_view_impl.h"
|
||||
#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
|
||||
#include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
|
||||
#include "third_party/blink/renderer/core/loader/document_loader.h"
|
||||
@@ -555,6 +556,12 @@ DedicatedWorker::CreateGlobalScopeCreationParams(
|
||||
auto* frame = window->GetFrame();
|
||||
parent_devtools_token = frame->GetDevToolsFrameToken();
|
||||
settings = std::make_unique<WorkerSettings>(frame->GetSettings());
|
||||
+ if (auto* web_local_frame = WebLocalFrameImpl::FromFrame(frame)) {
|
||||
+ if (auto* web_view = web_local_frame->ViewImpl()) {
|
||||
+ settings->SetNodeIntegrationInWorker(
|
||||
+ web_view->GetWebPreferences().node_integration_in_worker);
|
||||
+ }
|
||||
+ }
|
||||
agent_group_scheduler_compositor_task_runner =
|
||||
execution_context->GetScheduler()
|
||||
->ToFrameScheduler()
|
||||
diff --git a/third_party/blink/renderer/core/workers/worker_settings.cc b/third_party/blink/renderer/core/workers/worker_settings.cc
|
||||
index 45680c5f6ea0c7e89ccf43eb88f8a11e3318c02e..3fa3af62f4e7ba8186441c5e3184b1c04fe32d12 100644
|
||||
--- a/third_party/blink/renderer/core/workers/worker_settings.cc
|
||||
+++ b/third_party/blink/renderer/core/workers/worker_settings.cc
|
||||
@@ -40,6 +40,8 @@ std::unique_ptr<WorkerSettings> WorkerSettings::Copy(
|
||||
old_settings->strictly_block_blockable_mixed_content_;
|
||||
new_settings->generic_font_family_settings_ =
|
||||
old_settings->generic_font_family_settings_;
|
||||
+ new_settings->node_integration_in_worker_ =
|
||||
+ old_settings->node_integration_in_worker_;
|
||||
return new_settings;
|
||||
}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/workers/worker_settings.h b/third_party/blink/renderer/core/workers/worker_settings.h
|
||||
index 45c60dd2c44b05fdd279f759069383479823c7f2..33a2a0337efb9a46293e11d0d09b3fc182ab9618 100644
|
||||
--- a/third_party/blink/renderer/core/workers/worker_settings.h
|
||||
+++ b/third_party/blink/renderer/core/workers/worker_settings.h
|
||||
@@ -43,6 +43,11 @@ class CORE_EXPORT WorkerSettings {
|
||||
return generic_font_family_settings_;
|
||||
}
|
||||
|
||||
+ bool NodeIntegrationInWorker() const { return node_integration_in_worker_; }
|
||||
+ void SetNodeIntegrationInWorker(bool value) {
|
||||
+ node_integration_in_worker_ = value;
|
||||
+ }
|
||||
+
|
||||
private:
|
||||
void CopyFlagValuesFromSettings(Settings*);
|
||||
|
||||
@@ -54,6 +59,7 @@ class CORE_EXPORT WorkerSettings {
|
||||
bool strict_mixed_content_checking_ = false;
|
||||
bool allow_running_of_insecure_content_ = false;
|
||||
bool strictly_block_blockable_mixed_content_ = false;
|
||||
+ bool node_integration_in_worker_ = false;
|
||||
|
||||
GenericFontFamilySettings generic_font_family_settings_;
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Keeley Hammond <khammond@slack-corp.com>
|
||||
Date: Thu, 19 Mar 2026 00:34:37 -0700
|
||||
Subject: fix: fire MENU_POPUP_START for dynamically created ARIA menus
|
||||
|
||||
When an ARIA menu element is dynamically created (e.g. via appendChild)
|
||||
rather than being shown by toggling visibility, the AXMenuOpened event
|
||||
was not fired. The OnIgnoredChanged path handles the visibility toggle
|
||||
case, but OnAtomicUpdateFinished did not fire MENU_POPUP_START for
|
||||
newly created menu nodes.
|
||||
|
||||
Previous attempts to fix this (crbug.com/1254875) were reverted because
|
||||
they fired the event too eagerly in OnNodeCreated (before the tree was
|
||||
fully formed) and without filtering, causing regressions with screen
|
||||
readers on pages that misused role="menu".
|
||||
|
||||
This fix addresses both issues:
|
||||
1. Fires MENU_POPUP_START in OnAtomicUpdateFinished (after the tree
|
||||
update is complete) rather than in OnNodeCreated.
|
||||
2. Only fires if the menu has at least one menuitem child, filtering
|
||||
out false positives from misused role="menu" elements.
|
||||
|
||||
MENU_POPUP_END for deleted menus is already handled by
|
||||
AXTreeManager::OnNodeWillBeDeleted, which fires the event directly
|
||||
on the menu node before destruction.
|
||||
|
||||
The change is behind the DynamicMenuPopupEvents feature flag, disabled
|
||||
by default, to allow stabilization before enabling by default. Enable
|
||||
with --enable-features=DynamicMenuPopupEvents.
|
||||
|
||||
This patch can be removed when a CL containing the fix is accepted
|
||||
into Chromium.
|
||||
|
||||
Bug: 40794596
|
||||
|
||||
diff --git a/ui/accessibility/ax_event_generator.cc b/ui/accessibility/ax_event_generator.cc
|
||||
index 597b68bccc041a6431e35817669450e38fd56153..396820b148be04b91207e2359f9e441d331ccc10 100644
|
||||
--- a/ui/accessibility/ax_event_generator.cc
|
||||
+++ b/ui/accessibility/ax_event_generator.cc
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "ui/accessibility/ax_event_generator.h"
|
||||
|
||||
#include "base/containers/contains.h"
|
||||
+#include "base/feature_list.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "ui/accessibility/ax_enums.mojom.h"
|
||||
#include "ui/accessibility/ax_event.h"
|
||||
@@ -13,6 +14,12 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
+// Feature flag for firing MENU_POPUP_START for dynamically created ARIA menus.
|
||||
+// Disabled by default to allow stabilization before enabling globally.
|
||||
+BASE_FEATURE(kDynamicMenuPopupEvents,
|
||||
+ "DynamicMenuPopupEvents",
|
||||
+ base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
+
|
||||
namespace {
|
||||
|
||||
bool HasEvent(const std::set<AXEventGenerator::EventParams>& node_events,
|
||||
@@ -907,12 +914,31 @@ void AXEventGenerator::OnAtomicUpdateFinished(
|
||||
/*new_value*/ true);
|
||||
}
|
||||
|
||||
- if (IsAlert(change.node->GetRole()))
|
||||
+ if (IsAlert(change.node->GetRole())) {
|
||||
AddEvent(change.node, Event::ALERT);
|
||||
- else if (change.node->data().IsActiveLiveRegionRoot())
|
||||
+ } else if (change.node->data().IsActiveLiveRegionRoot()) {
|
||||
AddEvent(change.node, Event::LIVE_REGION_CREATED);
|
||||
- else if (change.node->data().IsContainedInActiveLiveRegion())
|
||||
+ } else if (change.node->data().IsContainedInActiveLiveRegion()) {
|
||||
FireLiveRegionEvents(change.node, /* is_removal */ false);
|
||||
+ }
|
||||
+
|
||||
+ // Fire MENU_POPUP_START when a menu is dynamically created (e.g. via
|
||||
+ // appendChild). The OnIgnoredChanged path handles menus that already exist
|
||||
+ // in the DOM and are shown/hidden. This handles the case where the menu
|
||||
+ // element itself is created on the fly.
|
||||
+ // Only fire if the menu has at least one menuitem child, to avoid false
|
||||
+ // positives from elements that misuse role="menu".
|
||||
+ if (base::FeatureList::IsEnabled(kDynamicMenuPopupEvents) &&
|
||||
+ change.node->GetRole() == ax::mojom::Role::kMenu &&
|
||||
+ !change.node->IsInvisibleOrIgnored()) {
|
||||
+ for (auto iter = change.node->UnignoredChildrenBegin();
|
||||
+ iter != change.node->UnignoredChildrenEnd(); ++iter) {
|
||||
+ if (IsMenuItem(iter->GetRole())) {
|
||||
+ AddEvent(change.node, Event::MENU_POPUP_START);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
FireActiveDescendantEvents();
|
||||
@@ -4,7 +4,7 @@ Date: Wed, 28 Jun 2023 21:11:40 +0900
|
||||
Subject: fix: harden blink::ScriptState::MaybeFrom
|
||||
|
||||
NOTE: since https://chromium-review.googlesource.com/c/chromium/src/+/6973697
|
||||
the patch is only needed for 32-bit builds.
|
||||
the patch is only needed for 32-bit builds or builds where the V8 sandbox is disabled.
|
||||
|
||||
This is needed as side effect of https://chromium-review.googlesource.com/c/chromium/src/+/4609446
|
||||
which now gets blink::ExecutionContext from blink::ScriptState
|
||||
@@ -56,18 +56,18 @@ index cecf528475cb832ed1876381878eade582bc83d6..71308b2d963c2d083328aad6be356dc5
|
||||
|
||||
enum EmbedderDataTag : uint16_t {
|
||||
diff --git a/third_party/blink/renderer/platform/bindings/script_state.cc b/third_party/blink/renderer/platform/bindings/script_state.cc
|
||||
index 8b6522c9299bef5ab766795b64a1ba30bc382a12..a714aeb8a62886bedb3820b33b49b1ebdb9c7cc0 100644
|
||||
index 8b6522c9299bef5ab766795b64a1ba30bc382a12..4615dc04a3814a096898a36c7bbeb30f960a8b4d 100644
|
||||
--- a/third_party/blink/renderer/platform/bindings/script_state.cc
|
||||
+++ b/third_party/blink/renderer/platform/bindings/script_state.cc
|
||||
@@ -14,6 +14,12 @@ namespace blink {
|
||||
|
||||
ScriptState::CreateCallback ScriptState::s_create_callback_ = nullptr;
|
||||
|
||||
+#if defined(ARCH_CPU_32_BITS)
|
||||
+#if !defined(V8_ENABLE_SANDBOX)
|
||||
+int const ScriptState::kScriptStateTag = 0x6e6f64;
|
||||
+void* const ScriptState::kScriptStateTagPtr = const_cast<void*>(
|
||||
+ static_cast<const void*>(&ScriptState::kScriptStateTag));
|
||||
+#endif // defined(ARCH_CPU_32_BITS)
|
||||
+#endif // !defined(V8_ENABLE_SANDBOX)
|
||||
+
|
||||
// static
|
||||
void ScriptState::SetCreateCallback(CreateCallback create_callback) {
|
||||
@@ -76,10 +76,10 @@ index 8b6522c9299bef5ab766795b64a1ba30bc382a12..a714aeb8a62886bedb3820b33b49b1eb
|
||||
context_.SetWeak(this, &OnV8ContextCollectedCallback);
|
||||
context->SetAlignedPointerInEmbedderData(kV8ContextPerContextDataIndex, this,
|
||||
gin::kBlinkScriptState);
|
||||
+#if defined(ARCH_CPU_32_BITS)
|
||||
+#if !defined(V8_ENABLE_SANDBOX)
|
||||
+ context->SetAlignedPointerInEmbedderData(
|
||||
+ kV8ContextPerContextDataTagIndex, ScriptState::kScriptStateTagPtr, v8::kEmbedderDataTypeTagDefault);
|
||||
+#endif // defined(ARCH_CPU_32_BITS)
|
||||
+#endif // !defined(V8_ENABLE_SANDBOX)
|
||||
RendererResourceCoordinator::Get()->OnScriptStateCreated(this,
|
||||
execution_context);
|
||||
}
|
||||
@@ -87,15 +87,15 @@ index 8b6522c9299bef5ab766795b64a1ba30bc382a12..a714aeb8a62886bedb3820b33b49b1eb
|
||||
// Cut the reference from V8 context to ScriptState.
|
||||
GetContext()->SetAlignedPointerInEmbedderData(
|
||||
kV8ContextPerContextDataIndex, nullptr, gin::kBlinkScriptState);
|
||||
+#if defined(ARCH_CPU_32_BITS)
|
||||
+#if !defined(V8_ENABLE_SANDBOX)
|
||||
+ GetContext()->SetAlignedPointerInEmbedderData(
|
||||
+ kV8ContextPerContextDataTagIndex, nullptr, v8::kEmbedderDataTypeTagDefault);
|
||||
+#endif // defined(ARCH_CPU_32_BITS)
|
||||
+#endif // !defined(V8_ENABLE_SANDBOX)
|
||||
reference_from_v8_context_.Clear();
|
||||
|
||||
// Cut the reference from ScriptState to V8 context.
|
||||
diff --git a/third_party/blink/renderer/platform/bindings/script_state.h b/third_party/blink/renderer/platform/bindings/script_state.h
|
||||
index 5ccdf26cead17031d510589b74288cbe79692779..bf3023d5305c05c5d92953b5bf5f655f964e5c28 100644
|
||||
index 5ccdf26cead17031d510589b74288cbe79692779..54ede003ebe0a46e624c9d67f7272b8898bbc83e 100644
|
||||
--- a/third_party/blink/renderer/platform/bindings/script_state.h
|
||||
+++ b/third_party/blink/renderer/platform/bindings/script_state.h
|
||||
@@ -6,6 +6,7 @@
|
||||
@@ -110,7 +110,7 @@ index 5ccdf26cead17031d510589b74288cbe79692779..bf3023d5305c05c5d92953b5bf5f655f
|
||||
kV8ContextPerContextDataIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
+#if defined(ARCH_CPU_32_BITS)
|
||||
+#if !defined(V8_ENABLE_SANDBOX)
|
||||
+ if (context->GetNumberOfEmbedderDataFields() <=
|
||||
+ kV8ContextPerContextDataTagIndex ||
|
||||
+ context->GetAlignedPointerFromEmbedderData(
|
||||
@@ -119,7 +119,7 @@ index 5ccdf26cead17031d510589b74288cbe79692779..bf3023d5305c05c5d92953b5bf5f655f
|
||||
+ ScriptState::kScriptStateTagPtr) {
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+#endif // defined(ARCH_CPU_32_BITS)
|
||||
+#endif // !defined(V8_ENABLE_SANDBOX)
|
||||
ScriptState* script_state =
|
||||
static_cast<ScriptState*>(context->GetAlignedPointerFromEmbedderData(
|
||||
isolate, kV8ContextPerContextDataIndex, gin::kBlinkScriptState));
|
||||
@@ -127,13 +127,13 @@ index 5ccdf26cead17031d510589b74288cbe79692779..bf3023d5305c05c5d92953b5bf5f655f
|
||||
static_cast<int>(gin::kPerContextDataStartIndex) +
|
||||
static_cast<int>(gin::kEmbedderBlink);
|
||||
|
||||
+#if defined(ARCH_CPU_32_BITS)
|
||||
+#if !defined(V8_ENABLE_SANDBOX)
|
||||
+ static void* const kScriptStateTagPtr;
|
||||
+ static int const kScriptStateTag;
|
||||
+ static constexpr int kV8ContextPerContextDataTagIndex =
|
||||
+ static_cast<int>(gin::kPerContextDataStartIndex) +
|
||||
+ static_cast<int>(gin::kEmbedderBlinkTag);
|
||||
+#endif // defined(ARCH_CPU_32_BITS)
|
||||
+#endif // !defined(V8_ENABLE_SANDBOX)
|
||||
+
|
||||
// For accessing information about the last script compilation via
|
||||
// internals.idl.
|
||||
|
||||
80
patches/chromium/fix_mac_high_res_icons.patch
Normal file
80
patches/chromium/fix_mac_high_res_icons.patch
Normal file
@@ -0,0 +1,80 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Kanishk Ranjan <kanishkranjan17@gmail.com>
|
||||
Date: Thu, 11 Dec 2025 10:03:47 -0800
|
||||
Subject: Mac: Fix WebRTC window icon conversion via gfx::Image
|
||||
|
||||
The current WebRTC window picker implementation tries to manually convert
|
||||
NSImages to ImageSkia, but it does this incorrectly. As a result, the
|
||||
icons can appear corrupted or blank.
|
||||
|
||||
This CL resolves the issue by using gfx::Image for the conversion. This
|
||||
method offers a reliable and standard way to change an NSImage into an
|
||||
ImageSkia.
|
||||
|
||||
Feature-Flag: kUseGfxImageForMacWindowIcons
|
||||
Bug: 465028835
|
||||
Change-Id: Ib69bc151e9542d2402c1cd7d282e5f3298581862
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7239386
|
||||
Reviewed-by: Elad Alon <eladalon@chromium.org>
|
||||
Commit-Queue: Avi Drissman <avi@chromium.org>
|
||||
Reviewed-by: Avi Drissman <avi@chromium.org>
|
||||
Reviewed-by: Tove Petersson <tovep@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1557501}
|
||||
|
||||
diff --git a/AUTHORS b/AUTHORS
|
||||
index 7eb8f26120a23539b0780eb3f7e1d6a7ac52b102..fb0796cabdec4419e953306608a5b816ea1f2662 100644
|
||||
--- a/AUTHORS
|
||||
+++ b/AUTHORS
|
||||
@@ -824,6 +824,7 @@ Kamil Rytarowski <krytarowski@gmail.com>
|
||||
Kanaru Sato <i.am.kanaru.sato@gmail.com>
|
||||
Kangil Han <kangil.han@samsung.com>
|
||||
Kangyuan Shu <kangyuan.shu@intel.com>
|
||||
+Kanishk Ranjan <kanishkranjan17@gmail.com>
|
||||
Karan Thakkar <karanjthakkar@gmail.com>
|
||||
Karel Král <kralkareliv@gmail.com>
|
||||
Karl <karlpolicechromium@gmail.com>
|
||||
diff --git a/chrome/browser/media/webrtc/window_icon_util_mac.mm b/chrome/browser/media/webrtc/window_icon_util_mac.mm
|
||||
index 8bd216b9da864c9a8b82231ce6613cc120b32de7..c37b753c6aaf3b5036aacc74b310343fc7379188 100644
|
||||
--- a/chrome/browser/media/webrtc/window_icon_util_mac.mm
|
||||
+++ b/chrome/browser/media/webrtc/window_icon_util_mac.mm
|
||||
@@ -8,9 +8,19 @@
|
||||
|
||||
#include "base/apple/foundation_util.h"
|
||||
#include "base/apple/scoped_cftyperef.h"
|
||||
+#include "base/feature_list.h"
|
||||
+#include "ui/gfx/image/image.h"
|
||||
+#include "ui/gfx/image/image_skia.h"
|
||||
+
|
||||
+// TODO(crbug.com/465028835): Remove these includes and the fallback code once
|
||||
+// kUseGfxImageForMacWindowIcons is stable and the feature flag is removed
|
||||
#include "third_party/libyuv/include/libyuv/convert_argb.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
|
||||
+BASE_FEATURE(kUseGfxImageForMacWindowIcons,
|
||||
+ "UseGfxImageForMacWindowIcons",
|
||||
+ base::FEATURE_ENABLED_BY_DEFAULT);
|
||||
+
|
||||
gfx::ImageSkia GetWindowIcon(content::DesktopMediaID id) {
|
||||
DCHECK(id.type == content::DesktopMediaID::TYPE_WINDOW);
|
||||
|
||||
@@ -35,6 +45,20 @@
|
||||
NSImage* icon_image =
|
||||
[[NSRunningApplication runningApplicationWithProcessIdentifier:pid] icon];
|
||||
|
||||
+ // TODO(crbug.com/465028835): Remove this feature check and the fallback
|
||||
+ // path once kUseGfxImageForMacWindowIcons is stable and the flag is removed
|
||||
+ if (base::FeatureList::IsEnabled(kUseGfxImageForMacWindowIcons)) {
|
||||
+ // The app may have terminated, resulting in a nil icon.
|
||||
+ if (!icon_image) {
|
||||
+ return gfx::ImageSkia();
|
||||
+ }
|
||||
+
|
||||
+ return gfx::Image(icon_image).AsImageSkia();
|
||||
+ }
|
||||
+
|
||||
+ // TODO(crbug.com/465028835): Remove the code below this line once
|
||||
+ // kUseGfxImageForMacWindowIcons is stable and the flag is removed.
|
||||
+
|
||||
// Icon's NSImage defaults to the smallest which can be only 32x32.
|
||||
NSRect proposed_rect = NSMakeRect(0, 0, 128, 128);
|
||||
CGImageRef cg_icon_image =
|
||||
@@ -0,0 +1,58 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Mon, 16 Feb 2026 22:33:57 +0100
|
||||
Subject: fix: update DBus signal signature for XDG GlobalShortcuts portal
|
||||
|
||||
Refs https://chromium-review.googlesource.com/c/chromium/src/+/7143562
|
||||
|
||||
The Activated signal from the XDG GlobalShortcuts portal has signature "osta{sv}",
|
||||
but ConnectToSignal was declared with "ost". The strict ReadMessage parser
|
||||
fails on the extra trailing options vardict. Fix by updating the signature
|
||||
to match the spec.
|
||||
|
||||
This should be upstreamed.
|
||||
|
||||
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc
|
||||
index 505e59edad7acb41f272f1e323cfd90744e4701b..ead80cfac902a7630cf502b03cb7d2ee048944ac 100644
|
||||
--- a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc
|
||||
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc
|
||||
@@ -78,7 +78,7 @@ void GlobalAcceleratorListenerLinux::OnServiceStarted(bool service_started) {
|
||||
global_shortcuts_proxy_ = bus_->GetObjectProxy(
|
||||
kPortalServiceName, dbus::ObjectPath(kPortalObjectPath));
|
||||
|
||||
- dbus_utils::ConnectToSignal<"ost">(
|
||||
+ dbus_utils::ConnectToSignal<"osta{sv}">(
|
||||
global_shortcuts_proxy_, kGlobalShortcutsInterface, kSignalActivated,
|
||||
base::BindRepeating(&GlobalAcceleratorListenerLinux::OnActivatedSignal,
|
||||
weak_ptr_factory_.GetWeakPtr()),
|
||||
@@ -299,13 +299,14 @@ void GlobalAcceleratorListenerLinux::OnBindShortcuts(
|
||||
}
|
||||
|
||||
void GlobalAcceleratorListenerLinux::OnActivatedSignal(
|
||||
- dbus_utils::ConnectToSignalResultSig<"ost"> result) {
|
||||
+ dbus_utils::ConnectToSignalResultSig<"osta{sv}"> result) {
|
||||
if (!result.has_value()) {
|
||||
LOG(ERROR) << "Failed to parse Activated signal.";
|
||||
return;
|
||||
}
|
||||
|
||||
- auto [session_handle, shortcut_id, timestamp] = std::move(result.value());
|
||||
+ auto [session_handle, shortcut_id, timestamp, options] =
|
||||
+ std::move(result.value());
|
||||
|
||||
// Only process the signal if it comes from our current session.
|
||||
if (!session_proxy_ || session_proxy_->object_path() != session_handle) {
|
||||
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.h b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.h
|
||||
index 3838de75a0a3791f774e2ddedec0f6c7f2f30157..91a205e6c526aa02b46b1590fa427e8875a67280 100644
|
||||
--- a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.h
|
||||
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.h
|
||||
@@ -97,7 +97,8 @@ class GlobalAcceleratorListenerLinux : public GlobalAcceleratorListener {
|
||||
base::expected<dbus_xdg::Dictionary, dbus_xdg::ResponseError> results);
|
||||
|
||||
// Callbacks for DBus signals.
|
||||
- void OnActivatedSignal(dbus_utils::ConnectToSignalResultSig<"ost"> result);
|
||||
+ void OnActivatedSignal(
|
||||
+ dbus_utils::ConnectToSignalResultSig<"osta{sv}"> result);
|
||||
|
||||
void OnSignalConnected(const std::string& interface_name,
|
||||
const std::string& signal_name,
|
||||
@@ -0,0 +1,52 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Niklas Wenzel <dev@nikwen.de>
|
||||
Date: Wed, 4 Feb 2026 06:02:40 -0800
|
||||
Subject: LoAF: Add feature to enable sourceURL for all protocols
|
||||
|
||||
Backports https://crrev.com/c/7510894 (minus the test changes).
|
||||
|
||||
Can be removed when that CL is included via a Chromium roll.
|
||||
|
||||
Change-Id: Id5e58a151b13cc0ac054f4ec237b038255d683fd
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7510894
|
||||
Commit-Queue: Noam Rosenthal <nrosenthal@google.com>
|
||||
Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
|
||||
Reviewed-by: Noam Rosenthal <nrosenthal@google.com>
|
||||
Cr-Commit-Position: refs/heads/main@{#1579397}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc b/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc
|
||||
index 0d621b6b397a2f8c33732a7bc1a68830539438be..f0749e9a9db7c5cd6b9d12440800241a14640cdd 100644
|
||||
--- a/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc
|
||||
+++ b/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc
|
||||
@@ -520,8 +520,15 @@ void AnimationFrameTimingMonitor::Trace(Visitor* visitor) const {
|
||||
visitor->Trace(frame_handling_input_);
|
||||
}
|
||||
|
||||
+BASE_FEATURE(kAlwaysLogLOAFURL, base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
+
|
||||
namespace {
|
||||
+
|
||||
bool ShouldAllowScriptURL(const String& url) {
|
||||
+ if (base::FeatureList::IsEnabled(kAlwaysLogLOAFURL)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
KURL kurl(url);
|
||||
return kurl.ProtocolIsData() || kurl.ProtocolIsInHTTPFamily() ||
|
||||
kurl.ProtocolIs("blob") || kurl.IsEmpty();
|
||||
diff --git a/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.h b/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.h
|
||||
index f2fe39be2db525d89fcd9787c2ae9285babab26d..c395cf39d404f6c4f6f6e23c9fb8dfe92151a7d2 100644
|
||||
--- a/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.h
|
||||
+++ b/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.h
|
||||
@@ -22,6 +22,11 @@ class TimeTicks;
|
||||
|
||||
namespace blink {
|
||||
|
||||
+// When enabled, long-animation-frame events will always include the sourceURL,
|
||||
+// regardless of protocol. This is useful during development when using `file:`
|
||||
+// URLs or custom protocols defined by embedders.
|
||||
+CORE_EXPORT BASE_DECLARE_FEATURE(kAlwaysLogLOAFURL);
|
||||
+
|
||||
class LocalFrame;
|
||||
|
||||
// Monitors long-animation-frame timing (LoAF).
|
||||
@@ -1,13 +1,13 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Thu, 7 Apr 2022 20:30:16 +0900
|
||||
Subject: Make gtk::GetLibGdkPixbuf public
|
||||
Subject: Make gtk::GetLibGdkPixbuf and gtk::GetLibGdk public
|
||||
|
||||
Allows embedders to get a handle to the gdk_pixbuf
|
||||
library already loaded in the process.
|
||||
Allows embedders to get handles to the gdk_pixbuf
|
||||
and gdk libraries already loaded in the process.
|
||||
|
||||
diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc
|
||||
index e05b4f2eb1b22d5a647cb020bae4e4052a2e735c..c06af1c03487fafe76fde3bfa157a7d265e2f3a0 100644
|
||||
index e05b4f2eb1b22d5a647cb020bae4e4052a2e735c..86524a419606bea3e7d090415fda8f2d8ce24df2 100644
|
||||
--- a/ui/gtk/gtk_compat.cc
|
||||
+++ b/ui/gtk/gtk_compat.cc
|
||||
@@ -78,11 +78,6 @@ void* GetLibGio() {
|
||||
@@ -22,7 +22,7 @@ index e05b4f2eb1b22d5a647cb020bae4e4052a2e735c..c06af1c03487fafe76fde3bfa157a7d2
|
||||
void* GetLibGdk3() {
|
||||
static void* libgdk3 = DlOpen("libgdk-3.so.0");
|
||||
return libgdk3;
|
||||
@@ -175,6 +170,11 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
@@ -175,6 +170,15 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -30,20 +30,27 @@ index e05b4f2eb1b22d5a647cb020bae4e4052a2e735c..c06af1c03487fafe76fde3bfa157a7d2
|
||||
+ static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
|
||||
+ return libgdk_pixbuf;
|
||||
+}
|
||||
+
|
||||
+void* GetLibGdk() {
|
||||
+ return GtkCheckVersion(4) ? GetLibGtk4() : GetLibGdk3();
|
||||
+}
|
||||
+
|
||||
bool LoadGtk(ui::LinuxUiBackend backend) {
|
||||
static bool loaded = LoadGtkImpl(backend);
|
||||
return loaded;
|
||||
diff --git a/ui/gtk/gtk_compat.h b/ui/gtk/gtk_compat.h
|
||||
index 841e2e8fcdbe2da4aac487badd4d352476e461a2..e458df649546fa3bee10e24f0edac147186cc152 100644
|
||||
index 841e2e8fcdbe2da4aac487badd4d352476e461a2..043c3ab4dde02ca71798034e8cb2b3f2d2677af7 100644
|
||||
--- a/ui/gtk/gtk_compat.h
|
||||
+++ b/ui/gtk/gtk_compat.h
|
||||
@@ -42,6 +42,9 @@ using SkColor = uint32_t;
|
||||
@@ -42,6 +42,12 @@ using SkColor = uint32_t;
|
||||
|
||||
namespace gtk {
|
||||
|
||||
+// Get handle to the currently loaded gdk_pixbuf library in the process.
|
||||
+void* GetLibGdkPixbuf();
|
||||
+
|
||||
+// Get handle to the currently loaded gdk library in the process.
|
||||
+void* GetLibGdk();
|
||||
+
|
||||
// Loads libgtk and related libraries and returns true on success.
|
||||
bool LoadGtk(ui::LinuxUiBackend backend);
|
||||
|
||||
@@ -516,7 +516,7 @@ index 020050de162705651b4eb8378880cd4eb017d46c..2d3554861a570271d6f9b9a2c8b1de53
|
||||
// The NSWindow used by BridgedNativeWidget. Provides hooks into AppKit that
|
||||
// can only be accomplished by overriding methods.
|
||||
diff --git a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
|
||||
index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6b192912c 100644
|
||||
index a474e70cf3c10405b6f94f129f5a7312bb81fd73..adaff0f3d676eeaef93ac9a4a3b55fc0e572d33b 100644
|
||||
--- a/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
|
||||
+++ b/components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm
|
||||
@@ -21,6 +21,7 @@
|
||||
@@ -553,7 +553,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
@end
|
||||
|
||||
struct NSEdgeAndCornerThicknesses {
|
||||
@@ -158,13 +163,17 @@ - (void)cr_mouseDownOnFrameView:(NSEvent*)event;
|
||||
@@ -158,13 +163,30 @@ - (void)cr_mouseDownOnFrameView:(NSEvent*)event;
|
||||
@implementation NSView (CRFrameViewAdditions)
|
||||
// If a mouseDown: falls through to the frame view, turn it into a window drag.
|
||||
- (void)cr_mouseDownOnFrameView:(NSEvent*)event {
|
||||
@@ -561,6 +561,19 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
if ([self.window _resizeDirectionForMouseLocation:event.locationInWindow] !=
|
||||
-1)
|
||||
return;
|
||||
+#else
|
||||
+ // For MAS builds, approximate the resize direction check.
|
||||
+ if (self.window.styleMask & NSWindowStyleMaskResizable) {
|
||||
+ constexpr CGFloat kResizeThreshold = 5.0;
|
||||
+ NSPoint location = event.locationInWindow;
|
||||
+ NSRect frame = self.window.frame;
|
||||
+ CGFloat width = NSWidth(frame);
|
||||
+ CGFloat height = NSHeight(frame);
|
||||
+ if (location.x < kResizeThreshold || location.x > width - kResizeThreshold ||
|
||||
+ location.y < kResizeThreshold || location.y > height - kResizeThreshold) {
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+#endif
|
||||
[self.window performWindowDragWithEvent:event];
|
||||
}
|
||||
@@ -571,7 +584,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
@implementation NativeWidgetMacNSWindowTitledFrame
|
||||
- (void)mouseDown:(NSEvent*)event {
|
||||
if (self.window.isMovable)
|
||||
@@ -192,6 +201,8 @@ - (BOOL)usesCustomDrawing {
|
||||
@@ -192,6 +214,8 @@ - (BOOL)usesCustomDrawing {
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -580,7 +593,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
@implementation NativeWidgetMacNSWindow {
|
||||
@private
|
||||
CommandDispatcher* __strong _commandDispatcher;
|
||||
@@ -241,6 +252,7 @@ - (instancetype)initWithContentRect:(NSRect)contentRect
|
||||
@@ -241,6 +265,7 @@ - (instancetype)initWithContentRect:(NSRect)contentRect
|
||||
// bubbles and the find bar, but these should not be movable.
|
||||
// Instead, let's push this up to the parent window which should be
|
||||
// the browser.
|
||||
@@ -588,7 +601,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
- (void)_zoomToScreenEdge:(NSUInteger)edge {
|
||||
if (self.parentWindow) {
|
||||
[self.parentWindow _zoomToScreenEdge:edge];
|
||||
@@ -248,6 +260,7 @@ - (void)_zoomToScreenEdge:(NSUInteger)edge {
|
||||
@@ -248,6 +273,7 @@ - (void)_zoomToScreenEdge:(NSUInteger)edge {
|
||||
[super _zoomToScreenEdge:edge];
|
||||
}
|
||||
}
|
||||
@@ -596,7 +609,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
|
||||
// This override helps diagnose lifetime issues in crash stacktraces by
|
||||
// inserting a symbol on NativeWidgetMacNSWindow and should be kept even if it
|
||||
@@ -393,6 +406,8 @@ - (NSAccessibilityRole)accessibilityRole {
|
||||
@@ -393,6 +419,8 @@ - (NSAccessibilityRole)accessibilityRole {
|
||||
|
||||
// NSWindow overrides.
|
||||
|
||||
@@ -605,7 +618,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
+ (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle {
|
||||
if (windowStyle & NSWindowStyleMaskTitled) {
|
||||
if (Class customFrame = [NativeWidgetMacNSWindowTitledFrame class])
|
||||
@@ -404,6 +419,8 @@ + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle {
|
||||
@@ -404,6 +432,8 @@ + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle {
|
||||
return [super frameViewClassForStyleMask:windowStyle];
|
||||
}
|
||||
|
||||
@@ -614,7 +627,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
- (BOOL)_isTitleHidden {
|
||||
bool shouldShowWindowTitle = YES;
|
||||
if (_bridge)
|
||||
@@ -428,12 +445,14 @@ - (BOOL)_usesCustomDrawing {
|
||||
@@ -428,12 +458,14 @@ - (BOOL)_usesCustomDrawing {
|
||||
// if it were valid to set that style for windows, setting the window style
|
||||
// recalculates and re-caches a bunch of stuff, so a surgical override is the
|
||||
// cleanest approach.
|
||||
@@ -629,7 +642,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
|
||||
+ (void)_getExteriorResizeEdgeThicknesses:
|
||||
(NSEdgeAndCornerThicknesses*)outThicknesses
|
||||
@@ -687,9 +706,11 @@ - (id)archiver:(NSKeyedArchiver*)archiver willEncodeObject:(id)object {
|
||||
@@ -687,9 +719,11 @@ - (id)archiver:(NSKeyedArchiver*)archiver willEncodeObject:(id)object {
|
||||
}
|
||||
|
||||
- (void)saveRestorableState {
|
||||
@@ -641,7 +654,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
|
||||
// Certain conditions, such as in the Speedometer 3 benchmark, can trigger a
|
||||
// rapid succession of calls to saveRestorableState. If there's no pending
|
||||
@@ -756,6 +777,7 @@ - (void)reallySaveRestorableState {
|
||||
@@ -756,6 +790,7 @@ - (void)reallySaveRestorableState {
|
||||
// affects its restorable state changes.
|
||||
- (void)invalidateRestorableState {
|
||||
[super invalidateRestorableState];
|
||||
@@ -649,7 +662,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
if ([self _isConsideredOpenForPersistentState]) {
|
||||
if (_willUpdateRestorableState)
|
||||
return;
|
||||
@@ -768,6 +790,7 @@ - (void)invalidateRestorableState {
|
||||
@@ -768,6 +803,7 @@ - (void)invalidateRestorableState {
|
||||
_willUpdateRestorableState = NO;
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
}
|
||||
@@ -657,7 +670,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
}
|
||||
|
||||
// On newer SDKs, _canMiniaturize respects NSWindowStyleMaskMiniaturizable in
|
||||
@@ -932,6 +955,7 @@ - (void)maybeRemoveTreeFromOrderingGroups {
|
||||
@@ -932,6 +968,7 @@ - (void)maybeRemoveTreeFromOrderingGroups {
|
||||
// Since _removeFromGroups: is not documented it could go away in newer
|
||||
// versions of macOS. If the selector does not exist, DumpWithoutCrashing() so
|
||||
// we hear about the change.
|
||||
@@ -665,7 +678,7 @@ index a474e70cf3c10405b6f94f129f5a7312bb81fd73..00f6719bd80c8fdf31f910af3b93b5c6
|
||||
if (![NSWindow instancesRespondToSelector:@selector(_removeFromGroups:)]) {
|
||||
base::debug::DumpWithoutCrashing();
|
||||
return;
|
||||
@@ -949,6 +973,7 @@ - (void)maybeRemoveTreeFromOrderingGroups {
|
||||
@@ -949,6 +986,7 @@ - (void)maybeRemoveTreeFromOrderingGroups {
|
||||
[currentWindow _removeFromGroups:child];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?V=C3=A4in=C3=B6=20M=C3=A4kel=C3=A4?=
|
||||
<vaino.o.makela@gmail.com>
|
||||
Date: Mon, 5 Jan 2026 11:27:05 -0800
|
||||
Subject: Move Wayland pointer lock overrides to common code
|
||||
|
||||
Since the Wayland-specific pointer lock implementation overrides were
|
||||
placed in Chrome-specific code, they could not be used by other projects
|
||||
depending on Chromium source like Electron, making pointer lock not work
|
||||
on Wayland for them without special care. Moving the function
|
||||
implementations to the more generic DesktopWindowTreeHostLinux class
|
||||
allows Electron to benefit from this code without having to override the
|
||||
functions itself.
|
||||
|
||||
Change-Id: Ideb7dca9fd3dfb491df8f68296ba2d21069901cd
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7362747
|
||||
Reviewed-by: Thomas Anderson <thomasanderson@chromium.org>
|
||||
Reviewed-by: Kramer Ge <fangzhoug@chromium.org>
|
||||
Commit-Queue: Kramer Ge <fangzhoug@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1564501}
|
||||
|
||||
diff --git a/AUTHORS b/AUTHORS
|
||||
index 2384448120f5e5ac6b0315625e897d961bfc19b8..7eb8f26120a23539b0780eb3f7e1d6a7ac52b102 100644
|
||||
--- a/AUTHORS
|
||||
+++ b/AUTHORS
|
||||
@@ -1604,6 +1604,7 @@ Vitaliy Kharin <kvserr@gmail.com>
|
||||
Vivek Galatage <vivek.vg@samsung.com>
|
||||
Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
Volker Sorge <volker.sorge@gmail.com>
|
||||
+Väinö Mäkelä <vaino.o.makela@gmail.com>
|
||||
Waihung Fu <fufranci@amazon.com>
|
||||
wafuwafu13 <mariobaske@i.softbank.jp>
|
||||
Wojciech Bielawski <wojciech.bielawski@gmail.com>
|
||||
diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc
|
||||
index 87d1f5c0f8f9e286a58e809ae9cfc2b84dd97ed2..e2afae41f0b2fce9eb0428119b66ff09f4811932 100644
|
||||
--- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc
|
||||
+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc
|
||||
@@ -126,35 +126,6 @@ void BrowserDesktopWindowTreeHostLinux::FrameTypeChanged() {
|
||||
UpdateFrameHints();
|
||||
}
|
||||
|
||||
-bool BrowserDesktopWindowTreeHostLinux::SupportsMouseLock() {
|
||||
- auto* wayland_extension = ui::GetWaylandToplevelExtension(*platform_window());
|
||||
- if (!wayland_extension) {
|
||||
- return false;
|
||||
- }
|
||||
-
|
||||
- return wayland_extension->SupportsPointerLock();
|
||||
-}
|
||||
-
|
||||
-void BrowserDesktopWindowTreeHostLinux::LockMouse(aura::Window* window) {
|
||||
- DesktopWindowTreeHostLinux::LockMouse(window);
|
||||
-
|
||||
- if (SupportsMouseLock()) {
|
||||
- auto* wayland_extension =
|
||||
- ui::GetWaylandToplevelExtension(*platform_window());
|
||||
- wayland_extension->LockPointer(true /*enabled*/);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-void BrowserDesktopWindowTreeHostLinux::UnlockMouse(aura::Window* window) {
|
||||
- DesktopWindowTreeHostLinux::UnlockMouse(window);
|
||||
-
|
||||
- if (SupportsMouseLock()) {
|
||||
- auto* wayland_extension =
|
||||
- ui::GetWaylandToplevelExtension(*platform_window());
|
||||
- wayland_extension->LockPointer(false /*enabled*/);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
void BrowserDesktopWindowTreeHostLinux::TabDraggingKindChanged(
|
||||
TabDragKind tab_drag_kind) {
|
||||
CHECK(browser_widget_);
|
||||
diff --git a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.h b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.h
|
||||
index 01ef9a93657f9401191adb4b8bd17528da790127..12ca597be51480e45acf4490cf6e533c23c30556 100644
|
||||
--- a/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.h
|
||||
+++ b/chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.h
|
||||
@@ -80,9 +80,6 @@ class BrowserDesktopWindowTreeHostLinux
|
||||
void CloseNow() override;
|
||||
void Show(ui::mojom::WindowShowState show_state,
|
||||
const gfx::Rect& restore_bounds) override;
|
||||
- bool SupportsMouseLock() override;
|
||||
- void LockMouse(aura::Window* window) override;
|
||||
- void UnlockMouse(aura::Window* window) override;
|
||||
void ClientDestroyedWidget() override;
|
||||
|
||||
// ui::X11ExtensionDelegate:
|
||||
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
|
||||
index dfc327588c74d43893820a97056780ece2b22de5..1c5c18a8fb57376f53f8659c99384b4919b10db8 100644
|
||||
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
|
||||
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
|
||||
@@ -348,6 +348,35 @@ DesktopWindowTreeHostLinux::GetKeyboardLayoutMap() {
|
||||
return WindowTreeHostPlatform::GetKeyboardLayoutMap();
|
||||
}
|
||||
|
||||
+bool DesktopWindowTreeHostLinux::SupportsMouseLock() {
|
||||
+ auto* wayland_extension = ui::GetWaylandToplevelExtension(*platform_window());
|
||||
+ if (!wayland_extension) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ return wayland_extension->SupportsPointerLock();
|
||||
+}
|
||||
+
|
||||
+void DesktopWindowTreeHostLinux::LockMouse(aura::Window* window) {
|
||||
+ DesktopWindowTreeHostPlatform::LockMouse(window);
|
||||
+
|
||||
+ if (SupportsMouseLock()) {
|
||||
+ auto* wayland_extension =
|
||||
+ ui::GetWaylandToplevelExtension(*platform_window());
|
||||
+ wayland_extension->LockPointer(true /*enabled*/);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void DesktopWindowTreeHostLinux::UnlockMouse(aura::Window* window) {
|
||||
+ DesktopWindowTreeHostPlatform::UnlockMouse(window);
|
||||
+
|
||||
+ if (SupportsMouseLock()) {
|
||||
+ auto* wayland_extension =
|
||||
+ ui::GetWaylandToplevelExtension(*platform_window());
|
||||
+ wayland_extension->LockPointer(false /*enabled*/);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void DesktopWindowTreeHostLinux::OnCompleteSwapWithNewSize(
|
||||
const gfx::Size& size) {
|
||||
if (GetX11Extension()) {
|
||||
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
|
||||
index 5c57268b37e2acdb30b09dd525a0eefc11f39112..c773bbe351260d958de222cd4e7942d757a53c25 100644
|
||||
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
|
||||
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
|
||||
@@ -93,6 +93,11 @@ class VIEWS_EXPORT DesktopWindowTreeHostLinux
|
||||
// DesktopWindowTreeHostPlatform:
|
||||
base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override;
|
||||
|
||||
+ // WindowTreeHost:
|
||||
+ bool SupportsMouseLock() override;
|
||||
+ void LockMouse(aura::Window* window) override;
|
||||
+ void UnlockMouse(aura::Window* window) override;
|
||||
+
|
||||
// Called back by compositor_observer_ if the latter is set.
|
||||
virtual void OnCompleteSwapWithNewSize(const gfx::Size& size);
|
||||
|
||||
22
patches/chromium/patch_osr_control_screen_info.patch
Normal file
22
patches/chromium/patch_osr_control_screen_info.patch
Normal file
@@ -0,0 +1,22 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: reito <reito@chromium.org>
|
||||
Date: Wed, 29 Oct 2025 00:50:03 +0800
|
||||
Subject: patch: osr control screen info
|
||||
|
||||
We need to override GetNewScreenInfosForUpdate to ensure the screen info
|
||||
is updated correctly, instead of overriding GetScreenInfo which seems not
|
||||
working.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
|
||||
index 2fc206be84d3d41379bb26540cbf0fa4b1ba95fb..cd85325d60f0c47bbd1e15624e96e62a7f51cd47 100644
|
||||
--- a/content/browser/renderer_host/render_widget_host_view_base.h
|
||||
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
|
||||
@@ -685,7 +685,7 @@ class CONTENT_EXPORT RenderWidgetHostViewBase
|
||||
|
||||
// Generates the most current set of ScreenInfos from the current set of
|
||||
// displays in the system for use in UpdateScreenInfo.
|
||||
- display::ScreenInfos GetNewScreenInfosForUpdate();
|
||||
+ virtual display::ScreenInfos GetNewScreenInfosForUpdate();
|
||||
|
||||
// Called when display properties that need to be synchronized with the
|
||||
// renderer process changes. This method is called before notifying
|
||||
@@ -15,10 +15,10 @@ Note that we also need to manually update embedder's
|
||||
`api::WebContents::IsFullscreenForTabOrPending` value.
|
||||
|
||||
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
index 7d27b076c1947d2cd08364f87286ed6d9f460cdc..55d1609ba46abe9433d22a894fc2b498735ff778 100644
|
||||
index 3feca12a6185afef139a0cb4a8148b5a3ca9e32f..393808a7959d684fe4e46f86eff687ec15258fa9 100644
|
||||
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
||||
@@ -8973,6 +8973,17 @@ void RenderFrameHostImpl::EnterFullscreen(
|
||||
@@ -8974,6 +8974,17 @@ void RenderFrameHostImpl::EnterFullscreen(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,5 +13,6 @@
|
||||
{ "patch_dir": "src/electron/patches/webrtc", "repo": "src/third_party/webrtc" },
|
||||
{ "patch_dir": "src/electron/patches/reclient-configs", "repo": "src/third_party/engflow-reclient-configs" },
|
||||
{ "patch_dir": "src/electron/patches/sqlite", "repo": "src/third_party/sqlite/src" },
|
||||
{ "patch_dir": "src/electron/patches/skia", "repo": "src/third_party/skia" }
|
||||
{ "patch_dir": "src/electron/patches/skia", "repo": "src/third_party/skia" },
|
||||
{ "patch_dir": "src/electron/patches/angle", "repo": "src/third_party/angle/src" }
|
||||
]
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
chore_expose_ui_to_allow_electron_to_set_dock_side.patch
|
||||
fix_prefer_browser_runtime_over_node_in_hostruntime_detection.patch
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Thu, 12 Mar 2026 17:03:29 +0100
|
||||
Subject: fix: prefer browser runtime over node in HostRuntime detection
|
||||
|
||||
In Electron, the `process` global is available in renderer processes,
|
||||
including the DevTools renderer. This causes the IS_NODE check to pass,
|
||||
leading DevTools to attempt importing the Node.js platform runtime
|
||||
(which uses `node:worker_threads`). However, DevTools Web Workers
|
||||
running under the `devtools://` protocol don't have access to Node.js
|
||||
built-in modules, resulting in a failed dynamic import.
|
||||
|
||||
Fix by checking IS_BROWSER first, since DevTools always runs in a
|
||||
browser-like environment. The Node.js runtime is only needed when
|
||||
DevTools runs under pure Node.js (e.g., CLI tooling or testing).
|
||||
|
||||
diff --git a/front_end/core/platform/HostRuntime.ts b/front_end/core/platform/HostRuntime.ts
|
||||
index 91adba7c966a9c4c0e5315d2cfee07f8f622b731..16822b8d4ea74a4ffd6870e5e95948d75918f5d2 100644
|
||||
--- a/front_end/core/platform/HostRuntime.ts
|
||||
+++ b/front_end/core/platform/HostRuntime.ts
|
||||
@@ -14,12 +14,12 @@ export const IS_BROWSER =
|
||||
typeof window !== 'undefined' || (typeof self !== 'undefined' && typeof self.postMessage === 'function');
|
||||
|
||||
export const HOST_RUNTIME = await (async(): Promise<Api.HostRuntime.HostRuntime> => {
|
||||
- if (IS_NODE) {
|
||||
- return (await import('./node/node.js')).HostRuntime.HOST_RUNTIME;
|
||||
- }
|
||||
if (IS_BROWSER) {
|
||||
return (await import('./browser/browser.js')).HostRuntime.HOST_RUNTIME;
|
||||
}
|
||||
+ if (IS_NODE) {
|
||||
+ return (await import('./node/node.js')).HostRuntime.HOST_RUNTIME;
|
||||
+ }
|
||||
|
||||
throw new Error('Unknown runtime!');
|
||||
})();
|
||||
@@ -28,7 +28,6 @@ fix_adjust_wpt_and_webidl_tests_for_enabled_float16array.patch
|
||||
refactor_attach_cppgc_heap_on_v8_isolate_creation.patch
|
||||
fix_cppgc_initializing_twice.patch
|
||||
fix_expose_readfilesync_override_for_modules.patch
|
||||
fix_array_out-of-bounds_read_in_boyer-moore_search.patch
|
||||
test_accomodate_v8_thenable_stack_trace_change_in_snapshot.patch
|
||||
chore_exclude_electron_node_folder_from_exit-time-destructors.patch
|
||||
api_remove_deprecated_getisolate.patch
|
||||
@@ -40,6 +39,5 @@ api_delete_deprecated_fields_on_v8_isolate.patch
|
||||
api_promote_deprecation_of_v8_context_and_v8_object_api_methods.patch
|
||||
fix_ensure_traverseparent_bails_on_resource_path_exit.patch
|
||||
reland_temporal_unflag_temporal.patch
|
||||
src_handle_der_decoding_errors_from_system_certificates.patch
|
||||
build_restore_macos_deployment_target_to_12_0.patch
|
||||
test_make_buffer_sizes_32bit-aware_in.patch
|
||||
fix_generate_config_gypi_needs_to_generate_valid_json.patch
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user