mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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 }}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -9,4 +9,8 @@ npmMinimalAgeGate: 10080
|
||||
npmPreapprovedPackages:
|
||||
- "@electron/*"
|
||||
|
||||
httpProxy: "${HTTP_PROXY:-}"
|
||||
|
||||
httpsProxy: "${HTTPS_PROXY:-}"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.12.0.cjs
|
||||
|
||||
2
DEPS
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'144.0.7559.225',
|
||||
'144.0.7559.236',
|
||||
'node_version':
|
||||
'v24.14.0',
|
||||
'nan_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);
|
||||
|
||||
@@ -94,7 +94,7 @@ 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]
|
||||
|
||||
<!-- 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`][].
|
||||
|
||||
@@ -109,30 +109,41 @@ Returns `Promise<DesktopCapturerSource[]>` - Resolves with an array of [`Desktop
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
### macOS versions 14.2 or higher
|
||||
|
||||
### MacOS versions 14.2 or higher
|
||||
|
||||
`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.
|
||||
`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.
|
||||
> 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.
|
||||
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:
|
||||
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
|
||||
|
||||
### 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.
|
||||
|
||||
`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`.
|
||||
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`.
|
||||
|
||||
@@ -111,7 +111,8 @@ app.whenReady().then(() => {
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -115,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",
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
cherry-pick-a08731cf6d70.patch
|
||||
|
||||
240
patches/angle/cherry-pick-a08731cf6d70.patch
Normal file
240
patches/angle/cherry-pick-a08731cf6d70.patch
Normal file
@@ -0,0 +1,240 @@
|
||||
From a08731cf6d70c60fd74b1d75f2e8b94c52e18140 Mon Sep 17 00:00:00 2001
|
||||
From: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
Date: Thu, 19 Feb 2026 14:42:08 -0500
|
||||
Subject: [PATCH] Vulkan: Avoid overflow in texture size calculation
|
||||
|
||||
Bug: chromium:485622239
|
||||
Change-Id: Idf9847afa0aa2e72b6433ac8348ae2820c1ad8c5
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7595734
|
||||
Reviewed-by: Amirali Abdolrashidi <abdolrashidi@google.com>
|
||||
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
---
|
||||
|
||||
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp
|
||||
index 9e208f9..e2185a4 100644
|
||||
--- a/src/libANGLE/renderer/vulkan/TextureVk.cpp
|
||||
+++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp
|
||||
@@ -3164,8 +3164,17 @@
|
||||
// invalidate must be called after wait for finish.
|
||||
ANGLE_TRY(srcBuffer->invalidate(renderer));
|
||||
|
||||
- size_t dstBufferSize = sourceBox.width * sourceBox.height * sourceBox.depth *
|
||||
- dstFormat.pixelBytes * layerCount;
|
||||
+ // Use size_t calculations to avoid 32-bit overflows. Note that the dimensions are bound by
|
||||
+ // the maximums specified in Constants.h, and that gl::Box members are signed 32-bit
|
||||
+ // integers.
|
||||
+ static_assert(gl::IMPLEMENTATION_MAX_2D_TEXTURE_SIZE *
|
||||
+ gl::IMPLEMENTATION_MAX_2D_TEXTURE_SIZE <
|
||||
+ std::numeric_limits<int32_t>::max());
|
||||
+ size_t dstBufferSize = sourceBox.width * sourceBox.height;
|
||||
+ static_assert(gl::IMPLEMENTATION_MAX_3D_TEXTURE_SIZE *
|
||||
+ gl::IMPLEMENTATION_MAX_2D_ARRAY_TEXTURE_LAYERS * 16 <
|
||||
+ std::numeric_limits<int32_t>::max());
|
||||
+ dstBufferSize *= sourceBox.depth * dstFormat.pixelBytes * layerCount;
|
||||
|
||||
// Allocate memory in the destination texture for the copy/conversion.
|
||||
uint8_t *dstData = nullptr;
|
||||
diff --git a/src/tests/gl_tests/FramebufferTest.cpp b/src/tests/gl_tests/FramebufferTest.cpp
|
||||
index 020a041..f72f1a7 100644
|
||||
--- a/src/tests/gl_tests/FramebufferTest.cpp
|
||||
+++ b/src/tests/gl_tests/FramebufferTest.cpp
|
||||
@@ -8894,6 +8894,62 @@
|
||||
ASSERT_GL_NO_ERROR();
|
||||
}
|
||||
|
||||
+// Test that 2D array texture size calculation doesn't overflow internally when rendering to it. An
|
||||
+// RGB format is used which is often emualted with RGBA.
|
||||
+//
|
||||
+// Practically we cannot run this test. On most configurations, allocating a 4GB texture fails due
|
||||
+// to internal driver limitations. On the few configs that the test actually runs, allocating such
|
||||
+// large memory leads to instability.
|
||||
+TEST_P(FramebufferTest_ES3, DISABLED_MaxSize2DArrayNoOverflow)
|
||||
+{
|
||||
+ GLint maxTexture2DSize;
|
||||
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexture2DSize);
|
||||
+
|
||||
+ maxTexture2DSize = std::min(maxTexture2DSize, 16384);
|
||||
+
|
||||
+ // Create a 2D array texture with RGB format. Every layer is going to take 1GB of memory (if
|
||||
+ // emulated with RGBA), so only create 4 layers of it (for a total of 4GB of memory). If 32-bit
|
||||
+ // math is involved when calculating sizes related to this texture, they will overflow.
|
||||
+ constexpr uint32_t kLayers = 4;
|
||||
+ GLTexture tex;
|
||||
+ glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
|
||||
+ glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGB8, maxTexture2DSize, maxTexture2DSize, kLayers);
|
||||
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
+
|
||||
+ // Initialize the texture so its content is considered valid and worth preserving.
|
||||
+ constexpr int kValidSubsectionWidth = 16;
|
||||
+ constexpr int kValidSubsectionHeight = 20;
|
||||
+ std::vector<GLColorRGB> data(kValidSubsectionWidth * kValidSubsectionHeight,
|
||||
+ GLColorRGB(0, 255, 0));
|
||||
+ for (uint32_t layer = 0; layer < kLayers; ++layer)
|
||||
+ {
|
||||
+ glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, layer, kValidSubsectionWidth,
|
||||
+ kValidSubsectionHeight, 1, GL_RGB, GL_UNSIGNED_BYTE, data.data());
|
||||
+ }
|
||||
+
|
||||
+ // Draw with the texture, making sure it's initialized and data is flushed.
|
||||
+ ANGLE_GL_PROGRAM(drawTex2DArray, essl3_shaders::vs::Texture2DArray(),
|
||||
+ essl3_shaders::fs::Texture2DArray());
|
||||
+ drawQuad(drawTex2DArray, essl3_shaders::PositionAttrib(), 0.5f);
|
||||
+
|
||||
+ // Bind a framebuffer to the texture and render into it. In some backends, the texture is
|
||||
+ // recreated to RGBA to be renderable.
|
||||
+ GLFramebuffer fbo;
|
||||
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 1);
|
||||
+
|
||||
+ ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
|
||||
+ glViewport(0, 0, kValidSubsectionWidth / 2, kValidSubsectionHeight);
|
||||
+ drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0.5f);
|
||||
+
|
||||
+ EXPECT_PIXEL_RECT_EQ(0, 0, kValidSubsectionWidth / 2, kValidSubsectionHeight, GLColor::red);
|
||||
+ EXPECT_PIXEL_RECT_EQ(kValidSubsectionWidth / 2, 0,
|
||||
+ kValidSubsectionWidth - kValidSubsectionWidth / 2, kValidSubsectionHeight,
|
||||
+ GLColor::green);
|
||||
+ ASSERT_GL_NO_ERROR();
|
||||
+}
|
||||
+
|
||||
ANGLE_INSTANTIATE_TEST_ES2_AND(AddMockTextureNoRenderTargetTest,
|
||||
ES2_D3D9().enable(Feature::AddMockTextureNoRenderTarget),
|
||||
ES2_D3D11().enable(Feature::AddMockTextureNoRenderTarget));
|
||||
diff --git a/src/tests/gl_tests/VulkanImageTest.cpp b/src/tests/gl_tests/VulkanImageTest.cpp
|
||||
index 2f06e2d..87e7482 100644
|
||||
--- a/src/tests/gl_tests/VulkanImageTest.cpp
|
||||
+++ b/src/tests/gl_tests/VulkanImageTest.cpp
|
||||
@@ -677,8 +677,8 @@
|
||||
kTextureHeight, 1, GL_RGBA, GL_UNSIGNED_BYTE, textureColor.data());
|
||||
}
|
||||
|
||||
- ANGLE_GL_PROGRAM(drawTex2DArray, essl1_shaders::vs::Texture2DArray(),
|
||||
- essl1_shaders::fs::Texture2DArray());
|
||||
+ ANGLE_GL_PROGRAM(drawTex2DArray, essl3_shaders::vs::Texture2DArray(),
|
||||
+ essl3_shaders::fs::Texture2DArray());
|
||||
drawQuad(drawTex2DArray, essl1_shaders::PositionAttrib(), 0.5f);
|
||||
|
||||
// Fill up the device memory until we start allocating on the system memory.
|
||||
diff --git a/util/shader_utils.cpp b/util/shader_utils.cpp
|
||||
index 275e261..8994612 100644
|
||||
--- a/util/shader_utils.cpp
|
||||
+++ b/util/shader_utils.cpp
|
||||
@@ -580,18 +580,6 @@
|
||||
})";
|
||||
}
|
||||
|
||||
-const char *Texture2DArray()
|
||||
-{
|
||||
- return R"(#version 300 es
|
||||
-out vec2 v_texCoord;
|
||||
-in vec4 a_position;
|
||||
-void main()
|
||||
-{
|
||||
- gl_Position = vec4(a_position.xy, 0.0, 1.0);
|
||||
- v_texCoord = (a_position.xy * 0.5) + 0.5;
|
||||
-})";
|
||||
-}
|
||||
-
|
||||
} // namespace vs
|
||||
|
||||
namespace fs
|
||||
@@ -689,20 +677,6 @@
|
||||
})";
|
||||
}
|
||||
|
||||
-const char *Texture2DArray()
|
||||
-{
|
||||
- return R"(#version 300 es
|
||||
-precision highp float;
|
||||
-uniform highp sampler2DArray tex2DArray;
|
||||
-uniform int slice;
|
||||
-in vec2 v_texCoord;
|
||||
-out vec4 fragColor;
|
||||
-void main()
|
||||
-{
|
||||
- fragColor = texture(tex2DArray, vec3(v_texCoord, float(slice)));
|
||||
-})";
|
||||
-}
|
||||
-
|
||||
} // namespace fs
|
||||
} // namespace essl1_shaders
|
||||
|
||||
@@ -787,6 +761,18 @@
|
||||
})";
|
||||
}
|
||||
|
||||
+const char *Texture2DArray()
|
||||
+{
|
||||
+ return R"(#version 300 es
|
||||
+out vec2 v_texCoord;
|
||||
+in vec4 a_position;
|
||||
+void main()
|
||||
+{
|
||||
+ gl_Position = vec4(a_position.xy, 0.0, 1.0);
|
||||
+ v_texCoord = (a_position.xy * 0.5) + 0.5;
|
||||
+})";
|
||||
+}
|
||||
+
|
||||
} // namespace vs
|
||||
|
||||
namespace fs
|
||||
@@ -844,6 +830,20 @@
|
||||
})";
|
||||
}
|
||||
|
||||
+const char *Texture2DArray()
|
||||
+{
|
||||
+ return R"(#version 300 es
|
||||
+precision highp float;
|
||||
+uniform highp sampler2DArray tex2DArray;
|
||||
+uniform int slice;
|
||||
+in vec2 v_texCoord;
|
||||
+out vec4 fragColor;
|
||||
+void main()
|
||||
+{
|
||||
+ fragColor = texture(tex2DArray, vec3(v_texCoord, float(slice)));
|
||||
+})";
|
||||
+}
|
||||
+
|
||||
} // namespace fs
|
||||
} // namespace essl3_shaders
|
||||
|
||||
diff --git a/util/shader_utils.h b/util/shader_utils.h
|
||||
index 676341e..cf211cf 100644
|
||||
--- a/util/shader_utils.h
|
||||
+++ b/util/shader_utils.h
|
||||
@@ -90,7 +90,6 @@
|
||||
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
|
||||
// texcoord.
|
||||
ANGLE_UTIL_EXPORT const char *Texture2D();
|
||||
-ANGLE_UTIL_EXPORT const char *Texture2DArray();
|
||||
|
||||
} // namespace vs
|
||||
|
||||
@@ -120,7 +119,6 @@
|
||||
|
||||
// A shader that samples the texture
|
||||
ANGLE_UTIL_EXPORT const char *Texture2D();
|
||||
-ANGLE_UTIL_EXPORT const char *Texture2DArray();
|
||||
|
||||
} // namespace fs
|
||||
} // namespace essl1_shaders
|
||||
@@ -151,6 +149,7 @@
|
||||
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
|
||||
// texcoord.
|
||||
ANGLE_UTIL_EXPORT const char *Texture2DLod();
|
||||
+ANGLE_UTIL_EXPORT const char *Texture2DArray();
|
||||
|
||||
} // namespace vs
|
||||
|
||||
@@ -169,6 +168,9 @@
|
||||
// A shader that samples the texture at a given lod.
|
||||
ANGLE_UTIL_EXPORT const char *Texture2DLod();
|
||||
|
||||
+// A shader that samples the texture at a given slice.
|
||||
+ANGLE_UTIL_EXPORT const char *Texture2DArray();
|
||||
+
|
||||
} // namespace fs
|
||||
} // namespace essl3_shaders
|
||||
|
||||
@@ -149,3 +149,11 @@ 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
|
||||
|
||||
@@ -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>
|
||||
@@ -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.
|
||||
+ }
|
||||
+
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
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 =
|
||||
@@ -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(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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!');
|
||||
})();
|
||||
@@ -17,7 +17,7 @@ Upstreams:
|
||||
- https://github.com/nodejs/node/pull/39136
|
||||
|
||||
diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc
|
||||
index 461819ce0fa732048e4365c40a86ef55d984c35f..fa55c980a9c4f373723a867fd41276d67b0b9413 100644
|
||||
index 461819ce0fa732048e4365c40a86ef55d984c35f..f1c85e94cf526d0255f47c003664680d26413ec3 100644
|
||||
--- a/deps/ncrypto/ncrypto.cc
|
||||
+++ b/deps/ncrypto/ncrypto.cc
|
||||
@@ -11,6 +11,7 @@
|
||||
@@ -28,38 +28,6 @@ index 461819ce0fa732048e4365c40a86ef55d984c35f..fa55c980a9c4f373723a867fd41276d6
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
#include <openssl/core_names.h>
|
||||
#include <openssl/params.h>
|
||||
@@ -1130,7 +1131,9 @@ int64_t X509View::getValidToTime() const {
|
||||
return tp;
|
||||
#else
|
||||
struct tm tp;
|
||||
- ASN1_TIME_to_tm(X509_get0_notAfter(cert_), &tp);
|
||||
+#ifndef OPENSSL_IS_BORINGSSL
|
||||
+ ASN1_TIME_to_tm(X509_get0_notAfter(cert_), &tp);
|
||||
+#endif
|
||||
return PortableTimeGM(&tp);
|
||||
#endif
|
||||
}
|
||||
@@ -1142,7 +1145,9 @@ int64_t X509View::getValidFromTime() const {
|
||||
return tp;
|
||||
#else
|
||||
struct tm tp;
|
||||
+#ifndef OPENSSL_IS_BORINGSSL
|
||||
ASN1_TIME_to_tm(X509_get0_notBefore(cert_), &tp);
|
||||
+#endif
|
||||
return PortableTimeGM(&tp);
|
||||
#endif
|
||||
}
|
||||
@@ -2886,10 +2891,6 @@ std::optional<uint32_t> SSLPointer::verifyPeerCertificate() const {
|
||||
const char* SSLPointer::getClientHelloAlpn() const {
|
||||
if (ssl_ == nullptr) return {};
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
- const unsigned char* buf;
|
||||
- size_t len;
|
||||
- size_t rem;
|
||||
-
|
||||
if (!SSL_client_hello_get0_ext(
|
||||
get(),
|
||||
TLSEXT_TYPE_application_layer_protocol_negotiation,
|
||||
@@ -3090,9 +3091,11 @@ const Cipher Cipher::AES_256_GCM = Cipher::FromNid(NID_aes_256_gcm);
|
||||
const Cipher Cipher::AES_128_KW = Cipher::FromNid(NID_id_aes128_wrap);
|
||||
const Cipher Cipher::AES_192_KW = Cipher::FromNid(NID_id_aes192_wrap);
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
graphite_add_insertstatus_koutoforderrecording.patch
|
||||
cherry-pick-7911bee5d90e.patch
|
||||
|
||||
539
patches/skia/cherry-pick-7911bee5d90e.patch
Normal file
539
patches/skia/cherry-pick-7911bee5d90e.patch
Normal file
@@ -0,0 +1,539 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Greg Daniel <egdaniel@google.com>
|
||||
Date: Wed, 11 Mar 2026 15:29:58 -0400
|
||||
Subject: Make sure we are getting the correct atlas for glyph mask format.
|
||||
|
||||
Bug: b/491421267
|
||||
Change-Id: I4eacd46599eca2df8c10a3fc894b9ce890fae1e2
|
||||
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1184076
|
||||
Commit-Queue: Greg Daniel <egdaniel@google.com>
|
||||
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
|
||||
(cherry picked from commit 0cab3e4ee34b3bca6ba7df676639d73ffe4b2135)
|
||||
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1184917
|
||||
|
||||
diff --git a/bench/GlyphQuadFillBench.cpp b/bench/GlyphQuadFillBench.cpp
|
||||
index 6793512e216b00e1f8112f8e681eecf5beee8fe8..4fd0965185f8bab5a55ec63329bf6aa36ad56ed0 100644
|
||||
--- a/bench/GlyphQuadFillBench.cpp
|
||||
+++ b/bench/GlyphQuadFillBench.cpp
|
||||
@@ -68,7 +68,7 @@ class DirectMaskGlyphVertexFillBenchmark : public Benchmark {
|
||||
const sktext::gpu::AtlasSubRun* subRun =
|
||||
sktext::gpu::TextBlobTools::FirstSubRun(fBlob.get());
|
||||
SkASSERT_RELEASE(subRun);
|
||||
- subRun->testingOnly_packedGlyphIDToGlyph(&fCache);
|
||||
+ subRun->testingOnly_packedGlyphIDToGlyph(&fCache, subRun->maskFormat());
|
||||
fVertices.reset(new char[subRun->vertexStride(drawMatrix) * subRun->glyphCount() * 4]);
|
||||
}
|
||||
|
||||
diff --git a/gn/tests.gni b/gn/tests.gni
|
||||
index 8ae89364ce33f62ced5e8ff5b417a0cf69a3afb1..6286969c91fa9dff8d1e83413ab5b9fd514c5ae9 100644
|
||||
--- a/gn/tests.gni
|
||||
+++ b/gn/tests.gni
|
||||
@@ -424,6 +424,7 @@ pathops_tests_sources = [
|
||||
ganesh_tests_sources = [
|
||||
"$_tests/AdvancedBlendTest.cpp",
|
||||
"$_tests/ApplyGammaTest.cpp",
|
||||
+ "$_tests/AtlasOobTest.cpp",
|
||||
"$_tests/BackendAllocationTest.cpp",
|
||||
"$_tests/BackendSurfaceMutableStateTest.cpp",
|
||||
"$_tests/BlendTest.cpp",
|
||||
diff --git a/src/gpu/ganesh/text/GrAtlasManager.cpp b/src/gpu/ganesh/text/GrAtlasManager.cpp
|
||||
index 403bfe274e56293bfe2382b02525ae742ba541a7..1e7d9aa0ce14f19e09d79544730c6aa922ae37d6 100644
|
||||
--- a/src/gpu/ganesh/text/GrAtlasManager.cpp
|
||||
+++ b/src/gpu/ganesh/text/GrAtlasManager.cpp
|
||||
@@ -178,8 +178,7 @@ GrDrawOpAtlas::ErrorCode GrAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
|
||||
}
|
||||
SkASSERT(glyph != nullptr);
|
||||
|
||||
- MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
|
||||
- MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
|
||||
+ MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyph->fGlyphEntryKey.fFormat);
|
||||
int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
|
||||
|
||||
int padding;
|
||||
@@ -299,7 +298,7 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGanesh(
|
||||
|
||||
uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
|
||||
|
||||
- this->packedGlyphIDToGlyph(target->strikeCache());
|
||||
+ this->packedGlyphIDToGlyph(target->strikeCache(), maskFormat);
|
||||
|
||||
if (fAtlasGeneration != currentAtlasGen) {
|
||||
// Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
|
||||
@@ -316,9 +315,10 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGanesh(
|
||||
for (const Variant& variant : glyphs) {
|
||||
Glyph* gpuGlyph = variant.glyph;
|
||||
SkASSERT(gpuGlyph != nullptr);
|
||||
-
|
||||
+ SkASSERT(gpuGlyph->fGlyphEntryKey.fFormat == maskFormat);
|
||||
if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
|
||||
- const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
|
||||
+ const SkGlyph& skGlyph =
|
||||
+ *metricsAndImages.glyph(gpuGlyph->fGlyphEntryKey.fPackedID);
|
||||
auto code = atlasManager->addGlyphToAtlas(
|
||||
skGlyph, gpuGlyph, srcPadding, target->resourceProvider(), uploadTarget);
|
||||
if (code != GrDrawOpAtlas::ErrorCode::kSucceeded) {
|
||||
diff --git a/src/gpu/graphite/Device.cpp b/src/gpu/graphite/Device.cpp
|
||||
index 1163eacd741d059b5a782112d9dbeed7080e3207..b069ba5e84bf113f3e1bcff1cd7c8e9ef570722d 100644
|
||||
--- a/src/gpu/graphite/Device.cpp
|
||||
+++ b/src/gpu/graphite/Device.cpp
|
||||
@@ -1427,6 +1427,7 @@ void Device::drawAtlasSubRun(const sktext::gpu::AtlasSubRun* subRun,
|
||||
int padding) {
|
||||
return glyphs->regenerateAtlasForGraphite(begin, end, maskFormat, padding, fRecorder);
|
||||
};
|
||||
+
|
||||
for (int subRunCursor = 0; subRunCursor < subRunEnd;) {
|
||||
// For the remainder of the run, add any atlas uploads to the Recorder's TextAtlasManager
|
||||
auto[ok, glyphsRegenerated] = subRun->regenerateAtlas(subRunCursor, subRunEnd,
|
||||
diff --git a/src/gpu/graphite/text/TextAtlasManager.cpp b/src/gpu/graphite/text/TextAtlasManager.cpp
|
||||
index 6602a76c150bff077666fb91b990d3e45d528ce2..cbb51a66846922995912c3159afba879a2487313 100644
|
||||
--- a/src/gpu/graphite/text/TextAtlasManager.cpp
|
||||
+++ b/src/gpu/graphite/text/TextAtlasManager.cpp
|
||||
@@ -207,8 +207,7 @@ DrawAtlas::ErrorCode TextAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
|
||||
}
|
||||
SkASSERT(glyph != nullptr);
|
||||
|
||||
- MaskFormat glyphFormat = Glyph::FormatFromSkGlyph(skGlyph.maskFormat());
|
||||
- MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyphFormat);
|
||||
+ MaskFormat expectedMaskFormat = this->resolveMaskFormat(glyph->fGlyphEntryKey.fFormat);
|
||||
int bytesPerPixel = MaskFormatBytesPerPixel(expectedMaskFormat);
|
||||
|
||||
int padding;
|
||||
@@ -359,7 +358,7 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGraphite(int begin,
|
||||
|
||||
uint64_t currentAtlasGen = atlasManager->atlasGeneration(maskFormat);
|
||||
|
||||
- this->packedGlyphIDToGlyph(recorder->priv().strikeCache());
|
||||
+ this->packedGlyphIDToGlyph(recorder->priv().strikeCache(), maskFormat);
|
||||
|
||||
if (fAtlasGeneration != currentAtlasGen) {
|
||||
// Calculate the texture coordinates for the vertexes during first use (fAtlasGeneration
|
||||
@@ -375,9 +374,10 @@ std::tuple<bool, int> GlyphVector::regenerateAtlasForGraphite(int begin,
|
||||
for (const Variant& variant : glyphs) {
|
||||
Glyph* gpuGlyph = variant.glyph;
|
||||
SkASSERT(gpuGlyph != nullptr);
|
||||
-
|
||||
+ SkASSERT(gpuGlyph->fGlyphEntryKey.fFormat == maskFormat);
|
||||
if (!atlasManager->hasGlyph(maskFormat, gpuGlyph)) {
|
||||
- const SkGlyph& skGlyph = *metricsAndImages.glyph(gpuGlyph->fPackedID);
|
||||
+ const SkGlyph& skGlyph =
|
||||
+ *metricsAndImages.glyph(gpuGlyph->fGlyphEntryKey.fPackedID);
|
||||
auto code = atlasManager->addGlyphToAtlas(skGlyph, gpuGlyph, srcPadding);
|
||||
if (code != DrawAtlas::ErrorCode::kSucceeded) {
|
||||
success = code != DrawAtlas::ErrorCode::kError;
|
||||
diff --git a/src/text/gpu/Glyph.h b/src/text/gpu/Glyph.h
|
||||
index 821612d68cecfe9dae9518e376e09fdf233395ad..7942006a563bcab925ea2129ab6f6beea438a4c8 100644
|
||||
--- a/src/text/gpu/Glyph.h
|
||||
+++ b/src/text/gpu/Glyph.h
|
||||
@@ -14,6 +14,25 @@
|
||||
|
||||
namespace sktext::gpu {
|
||||
|
||||
+struct GlyphEntryKey {
|
||||
+ explicit GlyphEntryKey(SkPackedGlyphID id, skgpu::MaskFormat format)
|
||||
+ : fPackedID(id), fFormat(format) {}
|
||||
+
|
||||
+ const SkPackedGlyphID fPackedID;
|
||||
+ skgpu::MaskFormat fFormat;
|
||||
+
|
||||
+ bool operator==(const GlyphEntryKey& that) const {
|
||||
+ return fPackedID == that.fPackedID && fFormat == that.fFormat;
|
||||
+ }
|
||||
+ bool operator!=(const GlyphEntryKey& that) const {
|
||||
+ return !(*this == that);
|
||||
+ }
|
||||
+
|
||||
+ uint32_t hash() const {
|
||||
+ return fPackedID.hash();
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
class Glyph {
|
||||
public:
|
||||
static skgpu::MaskFormat FormatFromSkGlyph(SkMask::Format format) {
|
||||
@@ -34,10 +53,11 @@ public:
|
||||
SkUNREACHABLE;
|
||||
}
|
||||
|
||||
- explicit Glyph(SkPackedGlyphID packedGlyphID) : fPackedID(packedGlyphID) {}
|
||||
+ explicit Glyph(SkPackedGlyphID packedGlyphID, skgpu::MaskFormat format)
|
||||
+ : fGlyphEntryKey(packedGlyphID, format) {}
|
||||
|
||||
- const SkPackedGlyphID fPackedID;
|
||||
- skgpu::AtlasLocator fAtlasLocator;
|
||||
+ const GlyphEntryKey fGlyphEntryKey;
|
||||
+ skgpu::AtlasLocator fAtlasLocator;
|
||||
};
|
||||
|
||||
} // namespace sktext::gpu
|
||||
diff --git a/src/text/gpu/GlyphVector.cpp b/src/text/gpu/GlyphVector.cpp
|
||||
index 2a8e85f926aa547169f4b85372e9d3fb99816956..7bec7a0b77d8560d5ef978281edd7df6c45cb56f 100644
|
||||
--- a/src/text/gpu/GlyphVector.cpp
|
||||
+++ b/src/text/gpu/GlyphVector.cpp
|
||||
@@ -99,14 +99,14 @@ SkSpan<const Glyph*> GlyphVector::glyphs() const {
|
||||
|
||||
// packedGlyphIDToGlyph must be run in single-threaded mode.
|
||||
// If fSkStrike is not sk_sp<SkStrike> then the conversion to Glyph* has not happened.
|
||||
-void GlyphVector::packedGlyphIDToGlyph(StrikeCache* cache) {
|
||||
+void GlyphVector::packedGlyphIDToGlyph(StrikeCache* cache, MaskFormat maskFormat) {
|
||||
if (fTextStrike == nullptr) {
|
||||
SkStrike* strike = fStrikePromise.strike();
|
||||
fTextStrike = cache->findOrCreateStrike(strike->strikeSpec());
|
||||
|
||||
// Get all the atlas locations for each glyph.
|
||||
for (Variant& variant : fGlyphs) {
|
||||
- variant.glyph = fTextStrike->getGlyph(variant.packedGlyphID);
|
||||
+ variant.glyph = fTextStrike->getGlyph(variant.packedGlyphID, maskFormat);
|
||||
}
|
||||
|
||||
// This must be pinned for the Atlas filling to work.
|
||||
diff --git a/src/text/gpu/GlyphVector.h b/src/text/gpu/GlyphVector.h
|
||||
index 42b92a93f70cc6d86d0a87dd07c2244e0da1281c..1eec6327d38fb4472b027faae68eecb9ad7509d7 100644
|
||||
--- a/src/text/gpu/GlyphVector.h
|
||||
+++ b/src/text/gpu/GlyphVector.h
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
// the sub runs.
|
||||
int unflattenSize() const { return GlyphVectorSize(fGlyphs.size()); }
|
||||
|
||||
- void packedGlyphIDToGlyph(StrikeCache* cache);
|
||||
+ void packedGlyphIDToGlyph(StrikeCache* cache, skgpu::MaskFormat);
|
||||
|
||||
static size_t GlyphVectorSize(size_t count) {
|
||||
return sizeof(Variant) * count;
|
||||
diff --git a/src/text/gpu/StrikeCache.cpp b/src/text/gpu/StrikeCache.cpp
|
||||
index add3127c92fdbfe56d6b56209a2235ce5a9f5acb..19df48329fd500f8682669ec96eb883b58243fdd 100644
|
||||
--- a/src/text/gpu/StrikeCache.cpp
|
||||
+++ b/src/text/gpu/StrikeCache.cpp
|
||||
@@ -207,10 +207,11 @@ TextStrike::TextStrike(StrikeCache* strikeCache, const SkStrikeSpec& strikeSpec)
|
||||
: fStrikeCache(strikeCache)
|
||||
, fStrikeSpec{strikeSpec} {}
|
||||
|
||||
-Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID) {
|
||||
- Glyph* glyph = fCache.findOrNull(packedGlyphID);
|
||||
+Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID, skgpu::MaskFormat format) {
|
||||
+ GlyphEntryKey localKey(packedGlyphID, format);
|
||||
+ Glyph* glyph = fCache.findOrNull(localKey);
|
||||
if (glyph == nullptr) {
|
||||
- glyph = fAlloc.make<Glyph>(packedGlyphID);
|
||||
+ glyph = fAlloc.make<Glyph>(packedGlyphID, format);
|
||||
fCache.set(glyph);
|
||||
fMemoryUsed += sizeof(Glyph);
|
||||
if (!fRemoved) {
|
||||
@@ -220,11 +221,11 @@ Glyph* TextStrike::getGlyph(SkPackedGlyphID packedGlyphID) {
|
||||
return glyph;
|
||||
}
|
||||
|
||||
-const SkPackedGlyphID& TextStrike::HashTraits::GetKey(const Glyph* glyph) {
|
||||
- return glyph->fPackedID;
|
||||
+const GlyphEntryKey& TextStrike::HashTraits::GetKey(const Glyph* glyph) {
|
||||
+ return glyph->fGlyphEntryKey;
|
||||
}
|
||||
|
||||
-uint32_t TextStrike::HashTraits::Hash(SkPackedGlyphID key) {
|
||||
+uint32_t TextStrike::HashTraits::Hash(GlyphEntryKey key) {
|
||||
return key.hash();
|
||||
}
|
||||
|
||||
diff --git a/src/text/gpu/StrikeCache.h b/src/text/gpu/StrikeCache.h
|
||||
index 007c45c6c6feecba3ff031ba3939ad2402e082b9..014afd5286602e3e049d8e48ae328273e599dc41 100644
|
||||
--- a/src/text/gpu/StrikeCache.h
|
||||
+++ b/src/text/gpu/StrikeCache.h
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "src/core/SkDescriptor.h"
|
||||
#include "src/core/SkStrikeSpec.h"
|
||||
#include "src/core/SkTHash.h"
|
||||
+#include "src/gpu/AtlasTypes.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -32,6 +33,7 @@ struct SkPackedGlyphID;
|
||||
namespace sktext::gpu {
|
||||
|
||||
class Glyph;
|
||||
+struct GlyphEntryKey;
|
||||
class StrikeCache;
|
||||
|
||||
// The TextStrike manages an SkArenaAlloc for Glyphs. The SkStrike is what actually creates
|
||||
@@ -43,7 +45,7 @@ public:
|
||||
TextStrike(StrikeCache* strikeCache,
|
||||
const SkStrikeSpec& strikeSpec);
|
||||
|
||||
- Glyph* getGlyph(SkPackedGlyphID);
|
||||
+ Glyph* getGlyph(SkPackedGlyphID, skgpu::MaskFormat format);
|
||||
const SkStrikeSpec& strikeSpec() const { return fStrikeSpec; }
|
||||
const SkDescriptor& getDescriptor() const { return fStrikeSpec.descriptor(); }
|
||||
|
||||
@@ -54,11 +56,11 @@ private:
|
||||
const SkStrikeSpec fStrikeSpec;
|
||||
|
||||
struct HashTraits {
|
||||
- static const SkPackedGlyphID& GetKey(const Glyph* glyph);
|
||||
- static uint32_t Hash(SkPackedGlyphID key);
|
||||
+ static const GlyphEntryKey& GetKey(const Glyph* glyph);
|
||||
+ static uint32_t Hash(GlyphEntryKey key);
|
||||
};
|
||||
// Map SkPackedGlyphID -> Glyph*.
|
||||
- skia_private::THashTable<Glyph*, SkPackedGlyphID, HashTraits> fCache;
|
||||
+ skia_private::THashTable<Glyph*, GlyphEntryKey, HashTraits> fCache;
|
||||
|
||||
// Store for the glyph information.
|
||||
SkArenaAlloc fAlloc{512};
|
||||
diff --git a/src/text/gpu/SubRunContainer.cpp b/src/text/gpu/SubRunContainer.cpp
|
||||
index 3a061a2012cd99de9ee4b3674f78ae99e0385d6c..a19460c82593c6713c047ab19e71caa27e375a6d 100644
|
||||
--- a/src/text/gpu/SubRunContainer.cpp
|
||||
+++ b/src/text/gpu/SubRunContainer.cpp
|
||||
@@ -651,8 +651,9 @@ public:
|
||||
|
||||
int glyphSrcPadding() const override { return 0; }
|
||||
|
||||
- void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const override {
|
||||
- fGlyphs.packedGlyphIDToGlyph(cache);
|
||||
+ void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache,
|
||||
+ skgpu::MaskFormat maskFormat) const override {
|
||||
+ fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
|
||||
}
|
||||
|
||||
std::tuple<bool, SkRect> deviceRectAndNeedsTransform(
|
||||
@@ -755,8 +756,9 @@ public:
|
||||
|
||||
const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
|
||||
|
||||
- void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
|
||||
- fGlyphs.packedGlyphIDToGlyph(cache);
|
||||
+ void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache,
|
||||
+ skgpu::MaskFormat maskFormat) const override {
|
||||
+ fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
|
||||
}
|
||||
|
||||
int glyphSrcPadding() const override { return 1; }
|
||||
@@ -884,8 +886,9 @@ public:
|
||||
|
||||
const AtlasSubRun* testingOnly_atlasSubRun() const override { return this; }
|
||||
|
||||
- void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache) const override {
|
||||
- fGlyphs.packedGlyphIDToGlyph(cache);
|
||||
+ void testingOnly_packedGlyphIDToGlyph(StrikeCache *cache,
|
||||
+ skgpu::MaskFormat maskFormat) const override {
|
||||
+ fGlyphs.packedGlyphIDToGlyph(cache, maskFormat);
|
||||
}
|
||||
|
||||
int glyphSrcPadding() const override { return SK_DistanceFieldInset; }
|
||||
diff --git a/src/text/gpu/SubRunContainer.h b/src/text/gpu/SubRunContainer.h
|
||||
index 2573dbb3964e9ab2cc0e276b60d4ab4f9804f0d9..4d1a3c8c2d55015d3d351d322ef039c45be2a398 100644
|
||||
--- a/src/text/gpu/SubRunContainer.h
|
||||
+++ b/src/text/gpu/SubRunContainer.h
|
||||
@@ -167,7 +167,7 @@ public:
|
||||
|
||||
const VertexFiller& vertexFiller() const { return fVertexFiller; }
|
||||
|
||||
- virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache) const = 0;
|
||||
+ virtual void testingOnly_packedGlyphIDToGlyph(StrikeCache* cache, skgpu::MaskFormat) const = 0;
|
||||
|
||||
protected:
|
||||
const VertexFiller fVertexFiller;
|
||||
diff --git a/tests/AtlasOobTest.cpp b/tests/AtlasOobTest.cpp
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..4e6fb02ee6af6543df285d8112f1a2ced5bd9ac9
|
||||
--- /dev/null
|
||||
+++ b/tests/AtlasOobTest.cpp
|
||||
@@ -0,0 +1,201 @@
|
||||
+/*
|
||||
+ * Copyright 2026 Google LLC
|
||||
+ *
|
||||
+ * Use of this source code is governed by a BSD-style license that can be
|
||||
+ * found in the LICENSE file.
|
||||
+ */
|
||||
+#include "include/core/SkCanvas.h"
|
||||
+#include "include/core/SkGraphics.h"
|
||||
+#include "include/core/SkSerialProcs.h"
|
||||
+#include "include/core/SkSurface.h"
|
||||
+#include "include/private/chromium/SkChromeRemoteGlyphCache.h"
|
||||
+#include "include/private/chromium/Slug.h"
|
||||
+#include "src/core/SkDescriptor.h"
|
||||
+#include "src/core/SkReadBuffer.h"
|
||||
+#include "src/core/SkTypeface_remote.h"
|
||||
+#include "src/core/SkWriteBuffer.h"
|
||||
+#include "src/gpu/AtlasTypes.h"
|
||||
+#include "tests/CtsEnforcement.h"
|
||||
+#include "tests/Test.h"
|
||||
+#include "tools/ToolUtils.h"
|
||||
+
|
||||
+#if defined(SK_GANESH)
|
||||
+#include "include/gpu/ganesh/GrDirectContext.h"
|
||||
+#include "include/gpu/ganesh/SkSurfaceGanesh.h"
|
||||
+#endif
|
||||
+
|
||||
+#if defined(SK_GRAPHITE)
|
||||
+#include "include/gpu/graphite/Context.h"
|
||||
+#include "include/gpu/graphite/Surface.h"
|
||||
+#include "tools/graphite/GraphiteTestContext.h"
|
||||
+#endif // defined(SK_GRAPHITE)
|
||||
+
|
||||
+#include <vector>
|
||||
+#include <cstring>
|
||||
+
|
||||
+namespace {
|
||||
+class FakeDiscardableManager : public SkStrikeClient::DiscardableHandleManager {
|
||||
+public:
|
||||
+ bool deleteHandle(SkDiscardableHandleId) override { return false; }
|
||||
+ void notifyCacheMiss(SkStrikeClient::CacheMissType, int) override {}
|
||||
+ void notifyReadFailure(const ReadFailureData&) override {}
|
||||
+ void assertHandleValid(SkDiscardableHandleId) override {}
|
||||
+};
|
||||
+
|
||||
+unsigned char kStrikeData[] = {
|
||||
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x07, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
|
||||
+ 0x00, 0x00, 0x00, 0x65, 0xd8, 0x50, 0xda, 0x99, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x63, 0x65, 0x72, 0x73, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x64, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x66, 0x86, 0x07, 0xc2, 0x42,
|
||||
+ 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x65, 0x72, 0x73, 0x38, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00
|
||||
+};
|
||||
+
|
||||
+unsigned char kDrawSlugOp[] = {
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x41,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
|
||||
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41,
|
||||
+ 0x00, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x86, 0x07, 0xc2, 0x42, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x65, 0x72, 0x73,
|
||||
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
+};
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
+// TODO: We expect this test to correctly hit an SkUnreachable and then crash. That does not work
|
||||
+// with our current testing framework because we have no to "expect" a crash. So for now we will
|
||||
+// land this test with only the valid loop enabled, but to test this is working locally, you should
|
||||
+// change the loop to have both iterations.
|
||||
+static void run_atlas_oob_test(skiatest::Reporter* reporter, SkCanvas* canvas) {
|
||||
+ auto discardableManager = sk_make_sp<FakeDiscardableManager>();
|
||||
+ SkStrikeClient client(discardableManager, false);
|
||||
+
|
||||
+ // 1. Prepare Strike Data
|
||||
+ if (!client.readStrikeData(kStrikeData, sizeof(kStrikeData))) {
|
||||
+ REPORTER_ASSERT(reporter, false, "Failed to read initial strike data");
|
||||
+ }
|
||||
+
|
||||
+ // 2. Prepare and Execute DrawSlug ops
|
||||
+ SkPaint paint;
|
||||
+ for (int idx = 0; idx < 1; ++idx) {
|
||||
+// for (int idx = 0; idx < 2; ++idx) {
|
||||
+ if (idx == 0) {
|
||||
+ kDrawSlugOp[0x48] = (unsigned char)skgpu::MaskFormat::kARGB;
|
||||
+ } else if (idx == 1) {
|
||||
+ kDrawSlugOp[0x48] = (unsigned char)skgpu::MaskFormat::kA8;
|
||||
+ }
|
||||
+ kDrawSlugOp[0xd8] = SkMask::kARGB32_Format;
|
||||
+ kDrawSlugOp[0xe0] = 0x99;
|
||||
+
|
||||
+ auto slug = client.deserializeSlugForTest(kDrawSlugOp, sizeof(kDrawSlugOp));
|
||||
+ if (slug) {
|
||||
+ slug->draw(canvas, paint);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
+
|
||||
+#if defined(SK_GANESH)
|
||||
+DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(Atlas_Oob_ganesh, reporter, ctxInfo, CtsEnforcement::kNextRelease) {
|
||||
+ auto dContext = ctxInfo.directContext();
|
||||
+ SkImageInfo info = SkImageInfo::MakeN32Premul(1024, 1024);
|
||||
+ auto surface = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, info);
|
||||
+ if (!surface) return;
|
||||
+ auto canvas = surface->getCanvas();
|
||||
+
|
||||
+ run_atlas_oob_test(reporter, canvas);
|
||||
+
|
||||
+ dContext->flushAndSubmit();
|
||||
+}
|
||||
+#endif // defined(SK_GANESH)
|
||||
+
|
||||
+#if defined(SK_GRAPHITE)
|
||||
+DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(Atlas_Oob_graphite, reporter, context, CtsEnforcement::kNextRelease) {
|
||||
+ using namespace skgpu::graphite;
|
||||
+ std::unique_ptr<Recorder> recorder = context->makeRecorder();
|
||||
+ SkImageInfo info = SkImageInfo::MakeN32Premul(1024, 1024);
|
||||
+ auto surface = SkSurfaces::RenderTarget(recorder.get(), info);
|
||||
+ if (!surface) return;
|
||||
+ auto canvas = surface->getCanvas();
|
||||
+
|
||||
+ run_atlas_oob_test(reporter, canvas);
|
||||
+
|
||||
+ std::unique_ptr<Recording> recording = recorder->snap();
|
||||
+ InsertRecordingInfo recordingInfo;
|
||||
+ recordingInfo.fRecording = recording.get();
|
||||
+ context->insertRecording(recordingInfo);
|
||||
+ context->submit();
|
||||
+}
|
||||
+#endif // defined(SK_GRAPHITE)
|
||||
@@ -9,4 +9,4 @@ refactor_use_non-deprecated_nskeyedarchiver_apis.patch
|
||||
chore_turn_off_launchapplicationaturl_deprecation_errors_in_squirrel.patch
|
||||
fix_crash_when_process_to_extract_zip_cannot_be_launched.patch
|
||||
use_uttype_class_instead_of_deprecated_uttypeconformsto.patch
|
||||
fix_clean_up_old_staged_updates_before_downloading_new_update.patch
|
||||
fix_clean_up_orphaned_staged_updates_before_downloading_new_update.patch
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Locascio <loc@anthropic.com>
|
||||
Date: Tue, 6 Jan 2026 08:23:03 -0800
|
||||
Subject: fix: clean up old staged updates before downloading new update
|
||||
|
||||
When checkForUpdates() is called while an update is already staged,
|
||||
Squirrel creates a new temporary directory for the download without
|
||||
cleaning up the old one. This can lead to significant disk usage if
|
||||
the app keeps checking for updates without restarting.
|
||||
|
||||
This change adds a force parameter to pruneUpdateDirectories that
|
||||
bypasses the AwaitingRelaunch state check. This is called before
|
||||
creating a new temp directory, ensuring old staged updates are
|
||||
cleaned up when a new download starts.
|
||||
|
||||
diff --git a/Squirrel/SQRLUpdater.m b/Squirrel/SQRLUpdater.m
|
||||
index d156616e81e6f25a3bded30e6216b8fc311f31bc..6cd4346bf43b191147aff819cb93387e71275a46 100644
|
||||
--- a/Squirrel/SQRLUpdater.m
|
||||
+++ b/Squirrel/SQRLUpdater.m
|
||||
@@ -543,11 +543,17 @@ - (RACSignal *)downloadBundleForUpdate:(SQRLUpdate *)update intoDirectory:(NSURL
|
||||
#pragma mark File Management
|
||||
|
||||
- (RACSignal *)uniqueTemporaryDirectoryForUpdate {
|
||||
- return [[[RACSignal
|
||||
+ // Clean up any old staged update directories before creating a new one.
|
||||
+ // This prevents disk usage from growing when checkForUpdates() is called
|
||||
+ // multiple times without the app restarting.
|
||||
+ return [[[[[self
|
||||
+ pruneUpdateDirectoriesWithForce:YES]
|
||||
+ ignoreValues]
|
||||
+ concat:[RACSignal
|
||||
defer:^{
|
||||
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
|
||||
return [directoryManager storageURL];
|
||||
- }]
|
||||
+ }]]
|
||||
flattenMap:^(NSURL *storageURL) {
|
||||
NSURL *updateDirectoryTemplate = [storageURL URLByAppendingPathComponent:[SQRLUpdaterUniqueTemporaryDirectoryPrefix stringByAppendingString:@"XXXXXXX"]];
|
||||
char *updateDirectoryCString = strdup(updateDirectoryTemplate.path.fileSystemRepresentation);
|
||||
@@ -643,7 +649,7 @@ - (BOOL)isRunningOnReadOnlyVolume {
|
||||
|
||||
- (RACSignal *)performHousekeeping {
|
||||
return [[RACSignal
|
||||
- merge:@[ [self pruneUpdateDirectories], [self truncateLogs] ]]
|
||||
+ merge:@[ [self pruneUpdateDirectoriesWithForce:NO], [self truncateLogs] ]]
|
||||
catch:^(NSError *error) {
|
||||
NSLog(@"Error doing housekeeping: %@", error);
|
||||
return [RACSignal empty];
|
||||
@@ -658,11 +664,12 @@ - (RACSignal *)performHousekeeping {
|
||||
///
|
||||
/// Sends each removed directory then completes, or errors, on an unspecified
|
||||
/// thread.
|
||||
-- (RACSignal *)pruneUpdateDirectories {
|
||||
+- (RACSignal *)pruneUpdateDirectoriesWithForce:(BOOL)force {
|
||||
return [[[RACSignal
|
||||
defer:^{
|
||||
- // If we already have updates downloaded we don't wanna prune them.
|
||||
- if (self.state == SQRLUpdaterStateAwaitingRelaunch) return [RACSignal empty];
|
||||
+ // If we already have updates downloaded we don't wanna prune them,
|
||||
+ // unless force is YES (used when starting a new download).
|
||||
+ if (!force && self.state == SQRLUpdaterStateAwaitingRelaunch) return [RACSignal empty];
|
||||
|
||||
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
|
||||
return [directoryManager storageURL];
|
||||
@@ -0,0 +1,130 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Locascio <loc@anthropic.com>
|
||||
Date: Tue, 6 Jan 2026 08:23:03 -0800
|
||||
Subject: fix: clean up orphaned staged updates before downloading new update
|
||||
|
||||
When checkForUpdates() is called while an update is already staged,
|
||||
Squirrel creates a new temporary directory for the download without
|
||||
cleaning up the old one. This can lead to significant disk usage if
|
||||
the app keeps checking for updates without restarting.
|
||||
|
||||
This change adds a pruneOrphanedUpdateDirectories step before creating
|
||||
a new temp directory. Unlike a blanket prune, this reads the current
|
||||
ShipItState.plist and preserves the directory it references, deleting
|
||||
only truly orphaned update directories. This keeps the on-disk
|
||||
footprint bounded (at most 2 dirs) while ensuring quitAndInstall
|
||||
remains safe to call even when a new check is in progress.
|
||||
|
||||
Refs https://github.com/electron/electron/issues/50200
|
||||
|
||||
diff --git a/Squirrel/SQRLUpdater.m b/Squirrel/SQRLUpdater.m
|
||||
index d156616e81e6f25a3bded30e6216b8fc311f31bc..41856e5754228d33982db72f97f2ff241615a357 100644
|
||||
--- a/Squirrel/SQRLUpdater.m
|
||||
+++ b/Squirrel/SQRLUpdater.m
|
||||
@@ -543,11 +543,19 @@ - (RACSignal *)downloadBundleForUpdate:(SQRLUpdate *)update intoDirectory:(NSURL
|
||||
#pragma mark File Management
|
||||
|
||||
- (RACSignal *)uniqueTemporaryDirectoryForUpdate {
|
||||
- return [[[RACSignal
|
||||
+ // Clean up any orphaned update directories before creating a new one.
|
||||
+ // This prevents disk usage from growing when checkForUpdates() is called
|
||||
+ // multiple times without the app restarting. The currently staged update
|
||||
+ // (referenced by ShipItState.plist) is always preserved so quitAndInstall
|
||||
+ // remains safe to call while a new check is in progress.
|
||||
+ return [[[[[self
|
||||
+ pruneOrphanedUpdateDirectories]
|
||||
+ ignoreValues]
|
||||
+ concat:[RACSignal
|
||||
defer:^{
|
||||
SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
|
||||
return [directoryManager storageURL];
|
||||
- }]
|
||||
+ }]]
|
||||
flattenMap:^(NSURL *storageURL) {
|
||||
NSURL *updateDirectoryTemplate = [storageURL URLByAppendingPathComponent:[SQRLUpdaterUniqueTemporaryDirectoryPrefix stringByAppendingString:@"XXXXXXX"]];
|
||||
char *updateDirectoryCString = strdup(updateDirectoryTemplate.path.fileSystemRepresentation);
|
||||
@@ -668,25 +676,68 @@ - (RACSignal *)pruneUpdateDirectories {
|
||||
return [directoryManager storageURL];
|
||||
}]
|
||||
flattenMap:^(NSURL *storageURL) {
|
||||
- NSFileManager *manager = [[NSFileManager alloc] init];
|
||||
- NSDirectoryEnumerator *enumerator = [manager enumeratorAtURL:storageURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsSubdirectoryDescendants errorHandler:^(NSURL *URL, NSError *error) {
|
||||
- NSLog(@"Error enumerating item %@ within directory %@: %@", URL, storageURL, error);
|
||||
- return YES;
|
||||
- }];
|
||||
+ return [self removeUpdateDirectoriesInStorageURL:storageURL excludingURL:nil];
|
||||
+ }]
|
||||
+ setNameWithFormat:@"%@ -prunedUpdateDirectories", self];
|
||||
+}
|
||||
|
||||
- return [[enumerator.rac_sequence.signal
|
||||
- filter:^(NSURL *enumeratedURL) {
|
||||
- NSString *name = enumeratedURL.lastPathComponent;
|
||||
- return [name hasPrefix:SQRLUpdaterUniqueTemporaryDirectoryPrefix];
|
||||
- }]
|
||||
- doNext:^(NSURL *directoryURL) {
|
||||
- NSError *error = nil;
|
||||
- if (![manager removeItemAtURL:directoryURL error:&error]) {
|
||||
- NSLog(@"Error removing old update directory at %@: %@", directoryURL, error.sqrl_verboseDescription);
|
||||
- }
|
||||
+/// Lazily removes orphaned temporary directories upon subscription, always
|
||||
+/// preserving the directory currently referenced by ShipItState.plist so that
|
||||
+/// quitAndInstall remains safe to call mid-check.
|
||||
+///
|
||||
+/// Safe to call in any state. Sends each removed directory then completes on
|
||||
+/// an unspecified thread. Errors reading the staged request are swallowed
|
||||
+/// (treated as "nothing staged").
|
||||
+- (RACSignal *)pruneOrphanedUpdateDirectories {
|
||||
+ return [[[[[SQRLShipItRequest
|
||||
+ readUsingURL:self.shipItStateURL]
|
||||
+ map:^(SQRLShipItRequest *request) {
|
||||
+ // The request holds the URL to the staged .app bundle; its parent
|
||||
+ // is the update.XXXXXXX directory we must preserve.
|
||||
+ return [request.updateBundleURL URLByDeletingLastPathComponent];
|
||||
+ }]
|
||||
+ catch:^(NSError *error) {
|
||||
+ // No staged request (or unreadable) — nothing to preserve.
|
||||
+ return [RACSignal return:nil];
|
||||
+ }]
|
||||
+ flattenMap:^(NSURL *stagedDirectoryURL) {
|
||||
+ SQRLDirectoryManager *directoryManager = [[SQRLDirectoryManager alloc] initWithApplicationIdentifier:SQRLShipItLauncher.shipItJobLabel];
|
||||
+ return [[directoryManager storageURL]
|
||||
+ flattenMap:^(NSURL *storageURL) {
|
||||
+ return [self removeUpdateDirectoriesInStorageURL:storageURL excludingURL:stagedDirectoryURL];
|
||||
}];
|
||||
}]
|
||||
- setNameWithFormat:@"%@ -prunedUpdateDirectories", self];
|
||||
+ setNameWithFormat:@"%@ -pruneOrphanedUpdateDirectories", self];
|
||||
+}
|
||||
+
|
||||
+/// Shared enumerate-and-delete logic for update temp directories.
|
||||
+///
|
||||
+/// storageURL - The Squirrel storage root to enumerate. Must not be nil.
|
||||
+/// excludedURL - Directory to skip (compared by standardized path). May be nil.
|
||||
+- (RACSignal *)removeUpdateDirectoriesInStorageURL:(NSURL *)storageURL excludingURL:(NSURL *)excludedURL {
|
||||
+ NSParameterAssert(storageURL != nil);
|
||||
+
|
||||
+ NSFileManager *manager = [[NSFileManager alloc] init];
|
||||
+ NSDirectoryEnumerator *enumerator = [manager enumeratorAtURL:storageURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsSubdirectoryDescendants errorHandler:^(NSURL *URL, NSError *error) {
|
||||
+ NSLog(@"Error enumerating item %@ within directory %@: %@", URL, storageURL, error);
|
||||
+ return YES;
|
||||
+ }];
|
||||
+
|
||||
+ NSString *excludedPath = excludedURL.URLByStandardizingPath.path;
|
||||
+
|
||||
+ return [[enumerator.rac_sequence.signal
|
||||
+ filter:^(NSURL *enumeratedURL) {
|
||||
+ NSString *name = enumeratedURL.lastPathComponent;
|
||||
+ if (![name hasPrefix:SQRLUpdaterUniqueTemporaryDirectoryPrefix]) return NO;
|
||||
+ if (excludedPath != nil && [enumeratedURL.URLByStandardizingPath.path isEqualToString:excludedPath]) return NO;
|
||||
+ return YES;
|
||||
+ }]
|
||||
+ doNext:^(NSURL *directoryURL) {
|
||||
+ NSError *error = nil;
|
||||
+ if (![manager removeItemAtURL:directoryURL error:&error]) {
|
||||
+ NSLog(@"Error removing old update directory at %@: %@", directoryURL, error.sqrl_verboseDescription);
|
||||
+ }
|
||||
+ }];
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
chore_allow_customizing_microtask_policy_per_context.patch
|
||||
cherry-pick-d5b0cb2acffe.patch
|
||||
|
||||
50
patches/v8/cherry-pick-d5b0cb2acffe.patch
Normal file
50
patches/v8/cherry-pick-d5b0cb2acffe.patch
Normal file
@@ -0,0 +1,50 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Darius Mercadier <dmercadier@chromium.org>
|
||||
Date: Wed, 25 Feb 2026 12:56:18 +0100
|
||||
Subject: [M144 Merge] [maglev] fix CanElideWriteBarrier Smi recording for phis
|
||||
|
||||
Recording a Tagged use is not enough for 2 reasons:
|
||||
|
||||
* Tagged uses are sometimes ignored, in particular for loop phis
|
||||
where we distinguish in-loop and out-of-loop uses.
|
||||
|
||||
* This Tagged use could only prevent untagging of this specific phi,
|
||||
but none of its inputs. So we could have a Smi phi as input to the
|
||||
current phi which gets untagged and retagged to a non-Smi, all
|
||||
while the current phi doesn't get untagged.
|
||||
|
||||
(cherry picked from commit a54bf5cd45e5b119e2afe6019428e81c3d626fb3)
|
||||
|
||||
Change-Id: I9b3a2ea339f2c9d81dbb74a44425ba55d8c73871
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7604255
|
||||
Auto-Submit: Darius Mercadier <dmercadier@chromium.org>
|
||||
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
|
||||
Commit-Queue: Darius Mercadier <dmercadier@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#105444}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7659106
|
||||
Auto-Submit: Srinivas Sista <srinivassista@chromium.org>
|
||||
Reviewed-by: Rezvan Mahdavi Hezaveh <rezvan@chromium.org>
|
||||
Commit-Queue: Srinivas Sista <srinivassista@chromium.org>
|
||||
Reviewed-by: Deepti Gandluri <gdeepti@chromium.org>
|
||||
Owners-Override: Srinivas Sista <srinivassista@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/14.4@{#64}
|
||||
Cr-Branched-From: 80acc26727d5a34e77dabeebe7c9213ec1bd4768-refs/heads/14.4.258@{#1}
|
||||
Cr-Branched-From: ce7e597e90f6df3fa4b6df224bc613b80c635450-refs/heads/main@{#104020}
|
||||
|
||||
diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
|
||||
index bf6a5ab1b41d684c37dd96f7720474c6bb71a4db..c21a41d7da3394bb8f35857e0dcf49a78b218d31 100644
|
||||
--- a/src/maglev/maglev-graph-builder.cc
|
||||
+++ b/src/maglev/maglev-graph-builder.cc
|
||||
@@ -4439,7 +4439,11 @@ bool MaglevGraphBuilder::CanElideWriteBarrier(ValueNode* object,
|
||||
ValueNode* value) {
|
||||
if (value->Is<RootConstant>() || value->Is<ConsStringMap>()) return true;
|
||||
if (!IsEmptyNodeType(GetType(value)) && CheckType(value, NodeType::kSmi)) {
|
||||
- value->MaybeRecordUseReprHint(UseRepresentation::kTagged);
|
||||
+ if constexpr (SmiValuesAre31Bits()) {
|
||||
+ if (Phi* value_as_phi = value->TryCast<Phi>()) {
|
||||
+ value_as_phi->SetUseRequires31BitValue();
|
||||
+ }
|
||||
+ }
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ async function main () {
|
||||
}));
|
||||
const hitRate = stats.CacheHit / (stats.Remote + stats.CacheHit + stats.LocalFallback);
|
||||
|
||||
console.log(`Effective cache hit rate: ${(hitRate * 100).toFixed(2)}%`);
|
||||
const messagePrefix = process.env.GITHUB_ACTIONS ? '::notice title=Build Stats::' : '';
|
||||
console.log(`${messagePrefix}Effective cache hit rate: ${(hitRate * 100).toFixed(2)}%`);
|
||||
|
||||
if (uploadStats) {
|
||||
if (!process.env.DD_API_KEY) {
|
||||
|
||||
21
script/codesign/cleanup-identity.sh
Executable file
21
script/codesign/cleanup-identity.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Removes the codesigning keychain created by generate-identity.sh.
|
||||
# Safe to run even if generate-identity.sh was never run (each step
|
||||
# is guarded).
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
KEYCHAIN="electron-codesign.keychain-db"
|
||||
|
||||
# delete-keychain also removes it from the search list
|
||||
if security list-keychains -d user | grep -q "$KEYCHAIN"; then
|
||||
security delete-keychain "$KEYCHAIN"
|
||||
echo "Deleted keychain: $KEYCHAIN"
|
||||
else
|
||||
echo "Keychain not found, nothing to delete"
|
||||
fi
|
||||
|
||||
# Clean up working directory
|
||||
rm -rf "$(dirname $0)"/.working
|
||||
echo "Cleanup complete"
|
||||
@@ -3,6 +3,8 @@
|
||||
set -eo pipefail
|
||||
|
||||
dir="$(dirname $0)"/.working
|
||||
KEYCHAIN="electron-codesign.keychain-db"
|
||||
KEYCHAIN_TEMP="$(openssl rand -hex 12)"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$dir"
|
||||
@@ -18,30 +20,16 @@ mkdir -p "$dir"
|
||||
|
||||
# Generate Certs
|
||||
openssl req -new -newkey rsa:2048 -x509 -days 7300 -nodes -config "$(dirname $0)"/codesign.cnf -extensions extended -batch -out "$dir"/certificate.cer -keyout "$dir"/certificate.key
|
||||
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "$dir"/certificate.cer
|
||||
sudo security import "$dir"/certificate.key -A -k /Library/Keychains/System.keychain
|
||||
|
||||
# restart(reload) taskgated daemon
|
||||
sudo pkill -f /usr/libexec/taskgated
|
||||
# macOS 15+ blocks modifications to the system keychain via SIP/TCC,
|
||||
# so we use a custom user-scoped keychain instead.
|
||||
# Refs https://github.com/electron/electron/issues/48182
|
||||
security create-keychain -p "$KEYCHAIN_TEMP" "$KEYCHAIN"
|
||||
security set-keychain-settings -t 3600 -u "$KEYCHAIN"
|
||||
security unlock-keychain -p "$KEYCHAIN_TEMP" "$KEYCHAIN"
|
||||
|
||||
# need once
|
||||
sudo security authorizationdb write system.privilege.taskport allow
|
||||
# need once
|
||||
DevToolsSecurity -enable
|
||||
security list-keychains -d user -s "$KEYCHAIN" $(security list-keychains -d user | tr -d '"')
|
||||
security import "$dir"/certificate.cer -k "$KEYCHAIN" -T /usr/bin/codesign
|
||||
security import "$dir"/certificate.key -k "$KEYCHAIN" -T /usr/bin/codesign -A
|
||||
|
||||
# openssl req -newkey rsa:2048 -nodes -keyout "$dir"/private.pem -x509 -days 1 -out "$dir"/certificate.pem -extensions extended -config "$(dirname $0)"/codesign.cnf
|
||||
# openssl x509 -inform PEM -in "$dir"/certificate.pem -outform DER -out "$dir"/certificate.cer
|
||||
# openssl x509 -pubkey -noout -in "$dir"/certificate.pem > "$dir"/public.key
|
||||
# rm -f "$dir"/certificate.pem
|
||||
|
||||
# Import Certs
|
||||
# security import "$dir"/certificate.cer -k $KEY_CHAIN
|
||||
# security import "$dir"/private.pem -k $KEY_CHAIN
|
||||
# security import "$dir"/public.key -k $KEY_CHAIN
|
||||
|
||||
# Generate Trust Settings
|
||||
# TODO: Remove NPX
|
||||
npm_config_yes=true npx ts-node "$(dirname $0)"/gen-trust.ts "$dir"/certificate.cer "$dir"/trust.xml
|
||||
|
||||
# Import Trust Settings
|
||||
sudo security trust-settings-import -d "$dir/trust.xml"
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_TEMP" "$KEYCHAIN"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
valid_certs=$(security find-identity -p codesigning -v)
|
||||
valid_certs=$(security find-identity -p codesigning)
|
||||
if [[ $valid_certs == *"1)"* ]]; then
|
||||
first_valid_cert=$(echo $valid_certs | sed 's/ \".*//' | sed 's/.* //')
|
||||
echo $first_valid_cert
|
||||
|
||||
@@ -128,6 +128,11 @@ def format_patch(repo, since):
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
'electron.gitattributes',
|
||||
),
|
||||
# Pin rename/copy detection to git's default so that patch output is
|
||||
# deterministic regardless of local or system-level diff.renames config
|
||||
# (e.g. 'copies', which would encode similar new files as copies).
|
||||
'-c',
|
||||
'diff.renames=true',
|
||||
# Ensure it is not possible to match anything
|
||||
# Disabled for now as we have consistent chunk headers
|
||||
# '-c',
|
||||
|
||||
@@ -14,6 +14,7 @@ const args = minimist(process.argv.slice(2), {
|
||||
|
||||
const BASE = path.resolve(__dirname, '../..');
|
||||
|
||||
const ROOT_PACKAGE_JSON = path.resolve(BASE, 'package.json');
|
||||
const NODE_DIR = path.resolve(BASE, 'third_party', 'electron_node');
|
||||
const JUNIT_DIR = args.jUnitDir ? path.resolve(args.jUnitDir) : null;
|
||||
const TAP_FILE_NAME = 'test.tap';
|
||||
@@ -38,6 +39,18 @@ const defaultOptions = [
|
||||
'-J'
|
||||
];
|
||||
|
||||
// The root package.json is ESM, which breaks the test runner.
|
||||
// Temporarily change it to CommonJS while running the tests, then
|
||||
// change it back when done.
|
||||
const resetPackageJson = ({ useESM }) => {
|
||||
// This won't always exist in CI.
|
||||
if (!fs.existsSync(ROOT_PACKAGE_JSON)) { return; }
|
||||
|
||||
const packageJson = JSON.parse(fs.readFileSync(ROOT_PACKAGE_JSON, 'utf-8'));
|
||||
packageJson.type = useESM ? 'module' : 'commonjs';
|
||||
fs.writeFileSync(ROOT_PACKAGE_JSON, JSON.stringify(packageJson, null, 2) + '\n');
|
||||
};
|
||||
|
||||
const getCustomOptions = () => {
|
||||
let customOptions = ['tools/test.py'];
|
||||
|
||||
@@ -79,6 +92,8 @@ async function main () {
|
||||
|
||||
const options = args.default ? defaultOptions : getCustomOptions();
|
||||
|
||||
resetPackageJson({ useESM: false });
|
||||
|
||||
const testChild = cp.spawn('python3', options, {
|
||||
env: {
|
||||
...process.env,
|
||||
@@ -88,7 +103,10 @@ async function main () {
|
||||
cwd: NODE_DIR,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
testChild.on('exit', (testCode) => {
|
||||
resetPackageJson({ useESM: true });
|
||||
|
||||
if (JUNIT_DIR) {
|
||||
fs.mkdirSync(JUNIT_DIR);
|
||||
const converterStream = require('tap-xunit')();
|
||||
|
||||
@@ -317,6 +317,12 @@ void BaseWindow::OnWindowSheetEnd() {
|
||||
Emit("sheet-end");
|
||||
}
|
||||
|
||||
void BaseWindow::OnWindowIsKeyChanged(bool is_key) {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
window()->SetActive(is_key);
|
||||
#endif
|
||||
}
|
||||
|
||||
void BaseWindow::OnWindowEnterHtmlFullScreen() {
|
||||
Emit("enter-html-full-screen");
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||
void OnWindowRotateGesture(float rotation) override;
|
||||
void OnWindowSheetBegin() override;
|
||||
void OnWindowSheetEnd() override;
|
||||
void OnWindowIsKeyChanged(bool is_key) override;
|
||||
void OnWindowEnterFullScreen() override;
|
||||
void OnWindowLeaveFullScreen() override;
|
||||
void OnWindowEnterHtmlFullScreen() override;
|
||||
|
||||
@@ -280,16 +280,22 @@ v8::Local<v8::Value> BrowserWindow::GetWebContents(v8::Isolate* isolate) {
|
||||
}
|
||||
|
||||
void BrowserWindow::OnWindowShow() {
|
||||
if (!web_contents_shown_) {
|
||||
web_contents()->WasShown();
|
||||
web_contents_shown_ = true;
|
||||
}
|
||||
BaseWindow::OnWindowShow();
|
||||
}
|
||||
|
||||
void BrowserWindow::OnWindowHide() {
|
||||
web_contents()->WasOccluded();
|
||||
web_contents_shown_ = false;
|
||||
BaseWindow::OnWindowHide();
|
||||
}
|
||||
|
||||
void BrowserWindow::Show() {
|
||||
web_contents()->WasShown();
|
||||
web_contents_shown_ = true;
|
||||
BaseWindow::Show();
|
||||
}
|
||||
|
||||
@@ -298,6 +304,7 @@ void BrowserWindow::ShowInactive() {
|
||||
if (IsModal())
|
||||
return;
|
||||
web_contents()->WasShown();
|
||||
web_contents_shown_ = true;
|
||||
BaseWindow::ShowInactive();
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ class BrowserWindow : public BaseWindow,
|
||||
// Helpers.
|
||||
|
||||
v8::Global<v8::Value> web_contents_;
|
||||
bool web_contents_shown_ = false;
|
||||
v8::Global<v8::Value> web_contents_view_;
|
||||
base::WeakPtr<api::WebContents> api_web_contents_;
|
||||
|
||||
|
||||
@@ -189,8 +189,23 @@ void Notification::NotificationFailed(const std::string& error) {
|
||||
|
||||
void Notification::NotificationDestroyed() {}
|
||||
|
||||
void Notification::NotificationClosed() {
|
||||
Emit("close");
|
||||
void Notification::NotificationClosed(const std::string& reason) {
|
||||
if (reason.empty()) {
|
||||
Emit("close");
|
||||
} else {
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
gin_helper::internal::Event* event =
|
||||
gin_helper::internal::Event::New(isolate);
|
||||
v8::Local<v8::Object> event_object =
|
||||
event->GetWrapper(isolate).ToLocalChecked();
|
||||
|
||||
gin_helper::Dictionary dict(isolate, event_object);
|
||||
dict.Set("reason", reason);
|
||||
|
||||
EmitWithoutEvent("close", event_object);
|
||||
}
|
||||
}
|
||||
|
||||
void Notification::Close() {
|
||||
|
||||
@@ -50,7 +50,7 @@ class Notification final : public gin_helper::DeprecatedWrappable<Notification>,
|
||||
void NotificationReplied(const std::string& reply) override;
|
||||
void NotificationDisplayed() override;
|
||||
void NotificationDestroyed() override;
|
||||
void NotificationClosed() override;
|
||||
void NotificationClosed(const std::string& reason) override;
|
||||
void NotificationFailed(const std::string& error) override;
|
||||
|
||||
// gin_helper::Wrappable
|
||||
|
||||
@@ -80,6 +80,14 @@ PowerMonitor::PowerMonitor(v8::Isolate* isolate) {
|
||||
}
|
||||
|
||||
PowerMonitor::~PowerMonitor() {
|
||||
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
|
||||
DestroyPlatformSpecificMonitors();
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
Browser::Get()->SetShutdownHandler(base::RepeatingCallback<bool()>());
|
||||
#endif
|
||||
|
||||
auto* power_monitor = base::PowerMonitor::GetInstance();
|
||||
power_monitor->RemovePowerStateObserver(this);
|
||||
power_monitor->RemovePowerSuspendObserver(this);
|
||||
|
||||
@@ -49,6 +49,7 @@ class PowerMonitor final : public gin_helper::DeprecatedWrappable<PowerMonitor>,
|
||||
|
||||
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
|
||||
void InitPlatformSpecificMonitors();
|
||||
void DestroyPlatformSpecificMonitors();
|
||||
#endif
|
||||
|
||||
// base::PowerStateObserver implementations:
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
}
|
||||
|
||||
- (void)addEmitter:(electron::api::PowerMonitor*)monitor_;
|
||||
- (void)removeEmitter:(electron::api::PowerMonitor*)monitor_;
|
||||
|
||||
@end
|
||||
|
||||
@@ -62,6 +63,10 @@
|
||||
self->emitters.push_back(monitor_);
|
||||
}
|
||||
|
||||
- (void)removeEmitter:(electron::api::PowerMonitor*)monitor_ {
|
||||
std::erase(self->emitters, monitor_);
|
||||
}
|
||||
|
||||
- (void)onScreenLocked:(NSNotification*)notification {
|
||||
for (auto* emitter : self->emitters) {
|
||||
emitter->Emit("lock-screen");
|
||||
@@ -98,4 +103,9 @@ void PowerMonitor::InitPlatformSpecificMonitors() {
|
||||
[g_lock_monitor addEmitter:this];
|
||||
}
|
||||
|
||||
void PowerMonitor::DestroyPlatformSpecificMonitors() {
|
||||
if (g_lock_monitor)
|
||||
[g_lock_monitor removeEmitter:this];
|
||||
}
|
||||
|
||||
} // namespace electron::api
|
||||
|
||||
@@ -49,6 +49,20 @@ void PowerMonitor::InitPlatformSpecificMonitors() {
|
||||
DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
}
|
||||
|
||||
void PowerMonitor::DestroyPlatformSpecificMonitors() {
|
||||
if (window_) {
|
||||
WTSUnRegisterSessionNotification(window_);
|
||||
UnregisterSuspendResumeNotification(static_cast<HANDLE>(window_));
|
||||
gfx::SetWindowUserData(window_, nullptr);
|
||||
DestroyWindow(window_);
|
||||
window_ = nullptr;
|
||||
}
|
||||
if (atom_) {
|
||||
UnregisterClass(MAKEINTATOM(atom_), instance_);
|
||||
atom_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK PowerMonitor::WndProcStatic(HWND hwnd,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
@@ -76,7 +90,7 @@ LRESULT CALLBACK PowerMonitor::WndProc(HWND hwnd,
|
||||
}
|
||||
if (should_treat_as_current_session) {
|
||||
if (wparam == WTS_SESSION_LOCK) {
|
||||
// Unretained is OK because this object is eternally pinned.
|
||||
// SelfKeepAlive prevents GC of this object, so Unretained is safe.
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce([](PowerMonitor* pm) { pm->Emit("lock-screen"); },
|
||||
|
||||
@@ -32,10 +32,6 @@
|
||||
#include "shell/browser/linux/x11_util.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_OZONE)
|
||||
#include "ui/ozone/public/ozone_platform.h"
|
||||
#endif
|
||||
|
||||
namespace electron::api {
|
||||
|
||||
gin::DeprecatedWrapperInfo Screen::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||
@@ -81,16 +77,9 @@ Screen::~Screen() {
|
||||
}
|
||||
|
||||
gfx::Point Screen::GetCursorScreenPoint(v8::Isolate* isolate) {
|
||||
#if defined(USE_OZONE)
|
||||
// Wayland will crash unless a window is created prior to calling
|
||||
// GetCursorScreenPoint.
|
||||
if (!ui::OzonePlatform::IsInitialized()) {
|
||||
gin_helper::ErrorThrower thrower(isolate);
|
||||
thrower.ThrowError(
|
||||
"screen.getCursorScreenPoint() cannot be called before a window has "
|
||||
"been created.");
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
if (x11_util::IsWayland())
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
return screen_->GetCursorScreenPoint();
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ void UtilityProcessWrapper::OnServiceProcessLaunch(
|
||||
EmitWithoutEvent("spawn");
|
||||
}
|
||||
|
||||
void UtilityProcessWrapper::HandleTermination(uint64_t exit_code) {
|
||||
void UtilityProcessWrapper::HandleTermination(uint32_t exit_code) {
|
||||
// HandleTermination is called from multiple callsites,
|
||||
// we need to ensure we only process it for the first callsite.
|
||||
if (terminated_)
|
||||
@@ -326,7 +326,7 @@ void UtilityProcessWrapper::CloseConnectorPort() {
|
||||
}
|
||||
}
|
||||
|
||||
void UtilityProcessWrapper::Shutdown(uint64_t exit_code) {
|
||||
void UtilityProcessWrapper::Shutdown(uint32_t exit_code) {
|
||||
node_service_remote_.reset();
|
||||
HandleTermination(exit_code);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ class UtilityProcessWrapper final
|
||||
static gin_helper::Handle<UtilityProcessWrapper> Create(gin::Arguments* args);
|
||||
static raw_ptr<UtilityProcessWrapper> FromProcessId(base::ProcessId pid);
|
||||
|
||||
void Shutdown(uint64_t exit_code);
|
||||
void Shutdown(uint32_t exit_code);
|
||||
|
||||
// gin_helper::Wrappable
|
||||
static gin::DeprecatedWrapperInfo kWrapperInfo;
|
||||
@@ -77,7 +77,7 @@ class UtilityProcessWrapper final
|
||||
void OnServiceProcessLaunch(const base::Process& process);
|
||||
void CloseConnectorPort();
|
||||
|
||||
void HandleTermination(uint64_t exit_code);
|
||||
void HandleTermination(uint32_t exit_code);
|
||||
|
||||
void PostMessage(gin::Arguments* args);
|
||||
bool Kill();
|
||||
|
||||
@@ -2201,6 +2201,11 @@ void WebContents::DidUpdateFaviconURL(
|
||||
iter->icon_url.is_valid())
|
||||
unique_urls.insert(iter->icon_url);
|
||||
}
|
||||
// Only emit if favicon URLs actually changed
|
||||
if (unique_urls == last_favicon_urls_)
|
||||
return;
|
||||
last_favicon_urls_ = unique_urls;
|
||||
|
||||
Emit("page-favicon-updated", unique_urls);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/flat_set.h"
|
||||
#include "base/functional/callback_forward.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/raw_ptr_exclusion.h"
|
||||
@@ -462,6 +463,9 @@ class WebContents final : public ExclusiveAccessContext,
|
||||
WebContents& operator=(const WebContents&) = delete;
|
||||
|
||||
private:
|
||||
// Store last emitted favicon URLs to avoid duplicate page-favicon-updated
|
||||
// events
|
||||
base::flat_set<GURL> last_favicon_urls_;
|
||||
// Does not manage lifetime of |web_contents|.
|
||||
WebContents(v8::Isolate* isolate, content::WebContents* web_contents);
|
||||
// Takes over ownership of |web_contents|.
|
||||
|
||||
@@ -83,13 +83,17 @@ void WebContentsView::ApplyBorderRadius() {
|
||||
|
||||
int WebContentsView::NonClientHitTest(const gfx::Point& point) {
|
||||
if (api_web_contents_) {
|
||||
auto* iwc = api_web_contents_->inspectable_web_contents();
|
||||
if (!iwc)
|
||||
return HTNOWHERE;
|
||||
// Convert the point to the contents view's coordinate space rather than
|
||||
// the InspectableWebContentsView's coordinate space, because the draggable
|
||||
// region is relative to the web content area. When DevTools is docked
|
||||
// (e.g. to the left), the contents view is offset within the parent,
|
||||
// so we need to account for that offset.
|
||||
auto* inspectable_view =
|
||||
api_web_contents_->inspectable_web_contents()->GetView();
|
||||
auto* inspectable_view = iwc->GetView();
|
||||
if (!inspectable_view)
|
||||
return HTNOWHERE;
|
||||
auto* contents_view = inspectable_view->GetContentsView();
|
||||
gfx::Point local_point(point);
|
||||
views::View::ConvertPointFromWidget(contents_view, &local_point);
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
@@ -71,6 +73,29 @@ Browser* Browser::Get() {
|
||||
return ElectronBrowserMainParts::Get()->browser();
|
||||
}
|
||||
|
||||
// static
|
||||
bool Browser::IsValidProtocolScheme(const std::string& scheme) {
|
||||
// RFC 3986 Section 3.1:
|
||||
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
||||
if (scheme.empty()) {
|
||||
LOG(ERROR) << "Protocol scheme must not be empty";
|
||||
return false;
|
||||
}
|
||||
if (!base::IsAsciiAlpha(scheme[0])) {
|
||||
LOG(ERROR) << "Protocol scheme must start with an ASCII letter";
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 1; i < scheme.size(); ++i) {
|
||||
const char c = scheme[i];
|
||||
if (!base::IsAsciiAlpha(c) && !base::IsAsciiDigit(c) && c != '+' &&
|
||||
c != '-' && c != '.') {
|
||||
LOG(ERROR) << "Protocol scheme contains invalid character: '" << c << "'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
|
||||
void Browser::Focus(gin::Arguments* args) {
|
||||
// Focus on the first visible window.
|
||||
|
||||
@@ -134,6 +134,10 @@ class Browser : private WindowListObserver {
|
||||
void SetAppUserModelID(const std::wstring& name);
|
||||
#endif
|
||||
|
||||
// Validate that a protocol scheme conforms to RFC 3986:
|
||||
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
||||
static bool IsValidProtocolScheme(const std::string& scheme);
|
||||
|
||||
// Remove the default protocol handler registry key
|
||||
bool RemoveAsDefaultProtocolClient(const std::string& protocol,
|
||||
gin::Arguments* args);
|
||||
|
||||
@@ -103,16 +103,19 @@ void Browser::ClearRecentDocuments() {}
|
||||
|
||||
bool Browser::SetAsDefaultProtocolClient(const std::string& protocol,
|
||||
gin::Arguments* args) {
|
||||
if (!IsValidProtocolScheme(protocol))
|
||||
return false;
|
||||
|
||||
return SetDefaultWebClient(protocol);
|
||||
}
|
||||
|
||||
bool Browser::IsDefaultProtocolClient(const std::string& protocol,
|
||||
gin::Arguments* args) {
|
||||
auto env = base::Environment::Create();
|
||||
|
||||
if (protocol.empty())
|
||||
if (!IsValidProtocolScheme(protocol))
|
||||
return false;
|
||||
|
||||
auto env = base::Environment::Create();
|
||||
|
||||
std::vector<std::string> argv = {kXdgSettings, "check",
|
||||
kXdgSettingsDefaultSchemeHandler, protocol};
|
||||
if (std::optional<std::string> desktop_name = env->GetVar("CHROME_DESKTOP")) {
|
||||
|
||||
@@ -233,7 +233,7 @@ bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol,
|
||||
|
||||
bool Browser::SetAsDefaultProtocolClient(const std::string& protocol,
|
||||
gin::Arguments* args) {
|
||||
if (protocol.empty())
|
||||
if (!IsValidProtocolScheme(protocol))
|
||||
return false;
|
||||
|
||||
NSString* identifier = [base::apple::MainBundle() bundleIdentifier];
|
||||
@@ -249,7 +249,7 @@ bool Browser::SetAsDefaultProtocolClient(const std::string& protocol,
|
||||
|
||||
bool Browser::IsDefaultProtocolClient(const std::string& protocol,
|
||||
gin::Arguments* args) {
|
||||
if (protocol.empty())
|
||||
if (!IsValidProtocolScheme(protocol))
|
||||
return false;
|
||||
|
||||
NSString* identifier = [base::apple::MainBundle() bundleIdentifier];
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "shell/browser/ui/win/jump_list.h"
|
||||
#include "shell/browser/window_list.h"
|
||||
#include "shell/common/application_info.h"
|
||||
#include "shell/common/command_line_util_win.h"
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "shell/common/gin_converters/image_converter.h"
|
||||
#include "shell/common/gin_converters/login_item_settings_converter.h"
|
||||
@@ -79,13 +80,22 @@ bool GetProtocolLaunchPath(gin::Arguments* args, std::wstring* exe) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Strip surrounding double quotes before re-quoting with AddQuoteForArg.
|
||||
if (exe->size() >= 2 && exe->front() == L'"' && exe->back() == L'"') {
|
||||
*exe = exe->substr(1, exe->size() - 2);
|
||||
}
|
||||
|
||||
// Read in optional args arg
|
||||
std::vector<std::wstring> launch_args;
|
||||
if (args->GetNext(&launch_args) && !launch_args.empty()) {
|
||||
std::wstring joined_args = base::JoinString(launch_args, L"\" \"");
|
||||
*exe = base::StrCat({L"\"", *exe, L"\" \"", joined_args, L"\" \"%1\""});
|
||||
std::wstring result = electron::AddQuoteForArg(*exe);
|
||||
for (const auto& arg : launch_args) {
|
||||
result += L' ';
|
||||
result += electron::AddQuoteForArg(arg);
|
||||
}
|
||||
*exe = base::StrCat({result, L" \"%1\""});
|
||||
} else {
|
||||
*exe = base::StrCat({L"\"", *exe, L"\" \"%1\""});
|
||||
*exe = base::StrCat({electron::AddQuoteForArg(*exe), L" \"%1\""});
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -153,9 +163,18 @@ bool FormatCommandLineString(std::wstring* exe,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Strip surrounding double quotes before re-quoting with AddQuoteForArg.
|
||||
if (exe->size() >= 2 && exe->front() == L'"' && exe->back() == L'"') {
|
||||
*exe = exe->substr(1, exe->size() - 2);
|
||||
}
|
||||
|
||||
*exe = electron::AddQuoteForArg(*exe);
|
||||
|
||||
if (!launch_args.empty()) {
|
||||
std::u16string joined_launch_args = base::JoinString(launch_args, u" ");
|
||||
*exe = base::StrCat({*exe, L" ", base::AsWStringView(joined_launch_args)});
|
||||
for (const auto& arg : launch_args) {
|
||||
*exe += L' ';
|
||||
*exe += electron::AddQuoteForArg(std::wstring(base::AsWStringView(arg)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -410,7 +429,7 @@ bool Browser::SetUserTasks(const std::vector<UserTask>& tasks) {
|
||||
|
||||
bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol,
|
||||
gin::Arguments* args) {
|
||||
if (protocol.empty())
|
||||
if (!IsValidProtocolScheme(protocol))
|
||||
return false;
|
||||
|
||||
// Main Registry Key
|
||||
@@ -489,7 +508,7 @@ bool Browser::SetAsDefaultProtocolClient(const std::string& protocol,
|
||||
// Software\Classes", which is inherited by "HKEY_CLASSES_ROOT"
|
||||
// anyway, and can be written by unprivileged users.
|
||||
|
||||
if (protocol.empty())
|
||||
if (!IsValidProtocolScheme(protocol))
|
||||
return false;
|
||||
|
||||
std::wstring exe;
|
||||
@@ -519,7 +538,7 @@ bool Browser::SetAsDefaultProtocolClient(const std::string& protocol,
|
||||
|
||||
bool Browser::IsDefaultProtocolClient(const std::string& protocol,
|
||||
gin::Arguments* args) {
|
||||
if (protocol.empty())
|
||||
if (!IsValidProtocolScheme(protocol))
|
||||
return false;
|
||||
|
||||
std::wstring exe;
|
||||
|
||||
@@ -88,13 +88,9 @@ HidChooserController::HidChooserController(
|
||||
exclusion_filters_(std::move(exclusion_filters)),
|
||||
callback_(std::move(callback)),
|
||||
initiator_document_(render_frame_host->GetWeakDocumentPtr()),
|
||||
origin_(content::WebContents::FromRenderFrameHost(render_frame_host)
|
||||
->GetPrimaryMainFrame()
|
||||
->GetLastCommittedOrigin()),
|
||||
origin_(render_frame_host->GetLastCommittedOrigin()),
|
||||
hid_delegate_(hid_delegate),
|
||||
render_frame_host_id_(render_frame_host->GetGlobalId()) {
|
||||
// The use above of GetMainFrame is safe as content::HidService instances are
|
||||
// not created for fenced frames.
|
||||
DCHECK(!render_frame_host->IsNestedWithinFencedFrame());
|
||||
|
||||
chooser_context_ = HidChooserContextFactory::GetForBrowserContext(
|
||||
|
||||
@@ -4,13 +4,27 @@
|
||||
|
||||
#include "shell/browser/linux/x11_util.h"
|
||||
|
||||
#include "build/build_config.h"
|
||||
#include "ui/ozone/platform_selection.h" // nogncheck
|
||||
|
||||
namespace x11_util {
|
||||
|
||||
bool IsX11() {
|
||||
static const bool is_x11 = ui::GetOzonePlatformId() == ui::kPlatformX11;
|
||||
return is_x11;
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
static const bool is = ui::GetOzonePlatformId() == ui::kPlatformX11;
|
||||
return is;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsWayland() {
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
static const bool is = ui::GetOzonePlatformId() == ui::kPlatformWayland;
|
||||
return is;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace x11_util
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
namespace x11_util {
|
||||
|
||||
bool IsX11();
|
||||
[[nodiscard]] bool IsX11();
|
||||
[[nodiscard]] bool IsWayland();
|
||||
|
||||
} // namespace x11_util
|
||||
|
||||
|
||||
@@ -170,6 +170,12 @@ class NativeWindowMac : public NativeWindow,
|
||||
void NotifyWindowDidFailToEnterFullScreen();
|
||||
void NotifyWindowWillLeaveFullScreen();
|
||||
|
||||
// Hide/show traffic light buttons around miniaturize/deminiaturize to
|
||||
// prevent them from flashing at the default position during the restore
|
||||
// animation when a custom trafficLightPosition is configured.
|
||||
void HideTrafficLights();
|
||||
void RestoreTrafficLights();
|
||||
|
||||
// Cleanup observers when window is getting closed. Note that the destructor
|
||||
// can be called much later after window gets closed, so we should not do
|
||||
// cleanup in destructor.
|
||||
|
||||
@@ -1498,6 +1498,18 @@ void NativeWindowMac::RedrawTrafficLights() {
|
||||
[buttons_proxy_ redraw];
|
||||
}
|
||||
|
||||
void NativeWindowMac::HideTrafficLights() {
|
||||
if (buttons_proxy_)
|
||||
[buttons_proxy_ setVisible:NO];
|
||||
}
|
||||
|
||||
void NativeWindowMac::RestoreTrafficLights() {
|
||||
if (buttons_proxy_ && window_button_visibility_.value_or(true)) {
|
||||
[buttons_proxy_ redraw];
|
||||
[buttons_proxy_ setVisible:YES];
|
||||
}
|
||||
}
|
||||
|
||||
// In simpleFullScreen mode, update the frame for new bounds.
|
||||
void NativeWindowMac::UpdateFrame() {
|
||||
NSWindow* window = GetNativeWindow().GetNativeNSWindow();
|
||||
|
||||
@@ -999,17 +999,13 @@ void NativeWindowViews::MoveTop() {
|
||||
|
||||
bool NativeWindowViews::CanResize() const {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
return resizable_ && thick_frame_;
|
||||
return has_frame() ? resizable_ && thick_frame_ : resizable_;
|
||||
#else
|
||||
return resizable_;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NativeWindowViews::IsResizable() const {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
if (has_frame())
|
||||
return ::GetWindowLong(GetAcceleratedWidget(), GWL_STYLE) & WS_THICKFRAME;
|
||||
#endif
|
||||
return CanResize();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "net/base/filename_util.h"
|
||||
#include "net/http/http_request_headers.h"
|
||||
#include "net/http/http_status_code.h"
|
||||
#include "net/http/http_util.h"
|
||||
#include "net/url_request/redirect_util.h"
|
||||
#include "services/network/public/cpp/resource_request.h"
|
||||
#include "services/network/public/cpp/shared_url_loader_factory.h"
|
||||
@@ -138,13 +139,17 @@ network::mojom::URLResponseHeadPtr ToResponseHead(
|
||||
base::Value::Dict headers;
|
||||
if (dict.Get("headers", &headers)) {
|
||||
for (const auto iter : headers) {
|
||||
if (!net::HttpUtil::IsValidHeaderName(iter.first))
|
||||
continue;
|
||||
if (iter.second.is_string()) {
|
||||
// key, value
|
||||
head->headers->AddHeader(iter.first, iter.second.GetString());
|
||||
if (net::HttpUtil::IsValidHeaderValue(iter.second.GetString()))
|
||||
head->headers->AddHeader(iter.first, iter.second.GetString());
|
||||
} else if (iter.second.is_list()) {
|
||||
// key: [values...]
|
||||
for (const auto& item : iter.second.GetList()) {
|
||||
if (item.is_string())
|
||||
if (item.is_string() &&
|
||||
net::HttpUtil::IsValidHeaderValue(item.GetString()))
|
||||
head->headers->AddHeader(iter.first, item.GetString());
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -46,9 +46,10 @@ void Notification::NotificationClicked() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void Notification::NotificationDismissed(bool should_destroy) {
|
||||
void Notification::NotificationDismissed(bool should_destroy,
|
||||
const std::string& close_reason) {
|
||||
if (delegate())
|
||||
delegate()->NotificationClosed();
|
||||
delegate()->NotificationClosed(close_reason);
|
||||
|
||||
set_is_dismissed(true);
|
||||
|
||||
|
||||
@@ -76,7 +76,8 @@ class Notification {
|
||||
|
||||
// Should be called by derived classes.
|
||||
void NotificationClicked();
|
||||
void NotificationDismissed(bool should_destroy = true);
|
||||
void NotificationDismissed(bool should_destroy = true,
|
||||
const std::string& close_reason = "");
|
||||
void NotificationFailed(const std::string& error = "");
|
||||
|
||||
// delete this.
|
||||
|
||||
@@ -24,7 +24,7 @@ class NotificationDelegate {
|
||||
virtual void NotificationAction(int action_index, int selection_index = -1) {}
|
||||
|
||||
virtual void NotificationClick() {}
|
||||
virtual void NotificationClosed() {}
|
||||
virtual void NotificationClosed(const std::string& reason = "") {}
|
||||
virtual void NotificationDisplayed() {}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -60,7 +60,7 @@ class NotificationDelegateImpl final : public electron::NotificationDelegate {
|
||||
->DispatchNonPersistentClickEvent(notification_id_, base::DoNothing());
|
||||
}
|
||||
|
||||
void NotificationClosed() override {
|
||||
void NotificationClosed(const std::string& reason) override {
|
||||
content::NotificationEventDispatcher::GetInstance()
|
||||
->DispatchNonPersistentCloseEvent(notification_id_, base::DoNothing());
|
||||
}
|
||||
|
||||
@@ -800,9 +800,24 @@ IFACEMETHODIMP ToastEventHandler::Invoke(
|
||||
IFACEMETHODIMP ToastEventHandler::Invoke(
|
||||
winui::Notifications::IToastNotification* sender,
|
||||
winui::Notifications::IToastDismissedEventArgs* e) {
|
||||
winui::Notifications::ToastDismissalReason reason;
|
||||
std::string reason_string;
|
||||
if (SUCCEEDED(e->get_Reason(&reason))) {
|
||||
switch (reason) {
|
||||
case winui::Notifications::ToastDismissalReason_UserCanceled:
|
||||
reason_string = "userCanceled";
|
||||
break;
|
||||
case winui::Notifications::ToastDismissalReason_ApplicationHidden:
|
||||
reason_string = "applicationHidden";
|
||||
break;
|
||||
case winui::Notifications::ToastDismissalReason_TimedOut:
|
||||
reason_string = "timedOut";
|
||||
break;
|
||||
}
|
||||
}
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE, base::BindOnce(&Notification::NotificationDismissed,
|
||||
notification_, false));
|
||||
notification_, false, reason_string));
|
||||
DebugLog("Notification dismissed");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "sandbox/win/src/nt_internals.h"
|
||||
#include "sandbox/win/src/win_utils.h"
|
||||
#include "shell/common/command_line_util_win.h"
|
||||
|
||||
namespace relauncher::internal {
|
||||
|
||||
@@ -50,49 +51,6 @@ HANDLE GetParentProcessHandle(base::ProcessHandle handle) {
|
||||
return ::OpenProcess(PROCESS_ALL_ACCESS, TRUE, ppid);
|
||||
}
|
||||
|
||||
StringType AddQuoteForArg(const StringType& arg) {
|
||||
// We follow the quoting rules of CommandLineToArgvW.
|
||||
// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
|
||||
std::wstring quotable_chars(L" \\\"");
|
||||
if (arg.find_first_of(quotable_chars) == std::wstring::npos) {
|
||||
// No quoting necessary.
|
||||
return arg;
|
||||
}
|
||||
|
||||
std::wstring out;
|
||||
out.push_back(L'"');
|
||||
for (size_t i = 0; i < arg.size(); ++i) {
|
||||
if (arg[i] == '\\') {
|
||||
// Find the extent of this run of backslashes.
|
||||
size_t start = i, end = start + 1;
|
||||
for (; end < arg.size() && arg[end] == '\\'; ++end) {
|
||||
}
|
||||
size_t backslash_count = end - start;
|
||||
|
||||
// Backslashes are escapes only if the run is followed by a double quote.
|
||||
// Since we also will end the string with a double quote, we escape for
|
||||
// either a double quote or the end of the string.
|
||||
if (end == arg.size() || arg[end] == '"') {
|
||||
// To quote, we need to output 2x as many backslashes.
|
||||
backslash_count *= 2;
|
||||
}
|
||||
for (size_t j = 0; j < backslash_count; ++j)
|
||||
out.push_back('\\');
|
||||
|
||||
// Advance i to one before the end to balance i++ in loop.
|
||||
i = end - 1;
|
||||
} else if (arg[i] == '"') {
|
||||
out.push_back('\\');
|
||||
out.push_back('"');
|
||||
} else {
|
||||
out.push_back(arg[i]);
|
||||
}
|
||||
}
|
||||
out.push_back('"');
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
StringType GetWaitEventName(base::ProcessId pid) {
|
||||
@@ -105,7 +63,7 @@ StringType ArgvToCommandLineString(const StringVector& argv) {
|
||||
for (const StringType& arg : argv) {
|
||||
if (!command_line.empty())
|
||||
command_line += L' ';
|
||||
command_line += AddQuoteForArg(arg);
|
||||
command_line += electron::AddQuoteForArg(arg);
|
||||
}
|
||||
return command_line;
|
||||
}
|
||||
|
||||
@@ -52,25 +52,21 @@ bool ElectronSerialDelegate::CanRequestPortPermission(
|
||||
auto* permission_helper =
|
||||
WebContentsPermissionHelper::FromWebContents(web_contents);
|
||||
return permission_helper->CheckSerialAccessPermission(
|
||||
web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin());
|
||||
frame->GetLastCommittedOrigin());
|
||||
}
|
||||
|
||||
bool ElectronSerialDelegate::HasPortPermission(
|
||||
content::RenderFrameHost* frame,
|
||||
const device::mojom::SerialPortInfo& port) {
|
||||
auto* web_contents = content::WebContents::FromRenderFrameHost(frame);
|
||||
return GetChooserContext(frame)->HasPortPermission(
|
||||
web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin(), port,
|
||||
frame);
|
||||
frame->GetLastCommittedOrigin(), port, frame);
|
||||
}
|
||||
|
||||
void ElectronSerialDelegate::RevokePortPermissionWebInitiated(
|
||||
content::RenderFrameHost* frame,
|
||||
const base::UnguessableToken& token) {
|
||||
auto* web_contents = content::WebContents::FromRenderFrameHost(frame);
|
||||
return GetChooserContext(frame)->RevokePortPermissionWebInitiated(
|
||||
web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin(), token,
|
||||
frame);
|
||||
frame->GetLastCommittedOrigin(), token, frame);
|
||||
}
|
||||
|
||||
const device::mojom::SerialPortInfo* ElectronSerialDelegate::GetPortInfo(
|
||||
|
||||
@@ -125,7 +125,7 @@ SerialChooserController::SerialChooserController(
|
||||
std::move(allowed_bluetooth_service_class_ids)),
|
||||
callback_(std::move(callback)),
|
||||
initiator_document_(render_frame_host->GetWeakDocumentPtr()) {
|
||||
origin_ = web_contents_->GetPrimaryMainFrame()->GetLastCommittedOrigin();
|
||||
origin_ = render_frame_host->GetLastCommittedOrigin();
|
||||
|
||||
chooser_context_ = SerialChooserContextFactory::GetForBrowserContext(
|
||||
web_contents_->GetBrowserContext())
|
||||
|
||||
@@ -270,34 +270,10 @@ void Relaunch(NSString* destinationPath) {
|
||||
}
|
||||
|
||||
bool Trash(NSString* path) {
|
||||
bool result = false;
|
||||
|
||||
if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_8) {
|
||||
result = [[NSFileManager defaultManager]
|
||||
trashItemAtURL:[NSURL fileURLWithPath:path]
|
||||
resultingItemURL:nil
|
||||
error:nil];
|
||||
}
|
||||
|
||||
// As a last resort try trashing with AppleScript.
|
||||
// This allows us to trash the app in macOS Sierra even when the app is
|
||||
// running inside an app translocation image.
|
||||
if (!result) {
|
||||
auto* code = R"str(
|
||||
set theFile to POSIX file "%@"
|
||||
tell application "Finder"
|
||||
move theFile to trash
|
||||
end tell
|
||||
)str";
|
||||
NSAppleScript* appleScript = [[NSAppleScript alloc]
|
||||
initWithSource:[NSString stringWithFormat:@(code), path]];
|
||||
NSDictionary* errorDict = nil;
|
||||
NSAppleEventDescriptor* scriptResult =
|
||||
[appleScript executeAndReturnError:&errorDict];
|
||||
result = (scriptResult != nil);
|
||||
}
|
||||
|
||||
return result;
|
||||
return [[NSFileManager defaultManager]
|
||||
trashItemAtURL:[NSURL fileURLWithPath:path]
|
||||
resultingItemURL:nil
|
||||
error:nil];
|
||||
}
|
||||
|
||||
bool DeleteOrTrash(NSString* path) {
|
||||
|
||||
@@ -87,8 +87,8 @@ MouseDownImpl g_nsnextstepframe_mousedown;
|
||||
(electron::NativeWindowMac*)[(id)self.window shell];
|
||||
if (shell && !shell->has_frame())
|
||||
[self cr_mouseDownOnFrameView:event];
|
||||
g_nsthemeframe_mousedown(self, @selector(mouseDown:), event);
|
||||
}
|
||||
g_nsthemeframe_mousedown(self, @selector(mouseDown:), event);
|
||||
}
|
||||
|
||||
- (void)swiz_nsnextstepframe_mouseDown:(NSEvent*)event {
|
||||
@@ -98,8 +98,8 @@ MouseDownImpl g_nsnextstepframe_mousedown;
|
||||
if (shell && !shell->has_frame()) {
|
||||
[self cr_mouseDownOnFrameView:event];
|
||||
}
|
||||
g_nsnextstepframe_mousedown(self, @selector(mouseDown:), event);
|
||||
}
|
||||
g_nsnextstepframe_mousedown(self, @selector(mouseDown:), event);
|
||||
}
|
||||
|
||||
- (void)swiz_nsview_swipeWithEvent:(NSEvent*)event {
|
||||
|
||||
@@ -255,6 +255,10 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
|
||||
shell_->SetWindowLevel(NSNormalWindowLevel);
|
||||
shell_->UpdateWindowOriginalFrame();
|
||||
shell_->DetachChildren();
|
||||
// Hide the traffic light buttons container before miniaturize so that
|
||||
// when the window is restored, macOS does not render the buttons at
|
||||
// their default position during the deminiaturize animation.
|
||||
shell_->HideTrafficLights();
|
||||
}
|
||||
|
||||
- (void)windowDidMiniaturize:(NSNotification*)notification {
|
||||
@@ -272,6 +276,10 @@ using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
|
||||
shell_->set_wants_to_be_visible(true);
|
||||
shell_->AttachChildren();
|
||||
shell_->SetWindowLevel(level_);
|
||||
// Reposition traffic light buttons and make them visible again.
|
||||
// They were hidden in windowWillMiniaturize to prevent a flash at
|
||||
// the default (0,0) position during the restore animation.
|
||||
shell_->RestoreTrafficLights();
|
||||
shell_->NotifyWindowRestore();
|
||||
}
|
||||
|
||||
|
||||
@@ -343,6 +343,10 @@ InspectableWebContents::InspectableWebContents(
|
||||
}
|
||||
|
||||
InspectableWebContents::~InspectableWebContents() {
|
||||
// Explicitly destroy the view first to ensure that any callbacks triggered
|
||||
// during view teardown (like NonClientHitTest) does not access a
|
||||
// partially-destroyed view.
|
||||
view_.reset();
|
||||
// Unsubscribe from devtools and Clean up resources.
|
||||
if (GetDevToolsWebContents())
|
||||
WebContentsDestroyed();
|
||||
|
||||
@@ -158,7 +158,7 @@ DialogResult ShowTaskDialogWstr(gfx::AcceleratedWidget parent,
|
||||
config.hInstance = GetModuleHandle(nullptr);
|
||||
config.dwFlags = flags;
|
||||
|
||||
if (parent) {
|
||||
if (parent && ::IsWindowEnabled(parent)) {
|
||||
config.hwndParent = parent;
|
||||
config.dwFlags |= TDF_POSITION_RELATIVE_TO_WINDOW;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "components/input/native_web_keyboard_event.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
#include "shell/browser/ui/views/menu_bar.h"
|
||||
#include "ui/events/keycodes/dom/keycode_converter.h"
|
||||
#include "ui/views/layout/box_layout.h"
|
||||
|
||||
namespace electron {
|
||||
@@ -22,21 +21,9 @@ bool IsAltKey(const input::NativeWebKeyboardEvent& event) {
|
||||
|
||||
bool IsAltModifier(const input::NativeWebKeyboardEvent& event) {
|
||||
using Mods = input::NativeWebKeyboardEvent::Modifiers;
|
||||
|
||||
// AltGraph (AltGr) should not be treated as a single Alt keypress for
|
||||
// menu-bar toggling.
|
||||
if (event.windows_key_code == ui::VKEY_ALTGR ||
|
||||
ui::KeycodeConverter::DomKeyToKeyString(event.dom_key) == "AltGraph") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (event.GetModifiers() & Mods::kKeyModifiers) == Mods::kAltKey;
|
||||
}
|
||||
|
||||
bool IsSingleAltKey(const input::NativeWebKeyboardEvent& event) {
|
||||
return IsAltKey(event) && IsAltModifier(event);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RootView::RootView(NativeWindow* window)
|
||||
@@ -111,7 +98,7 @@ void RootView::HandleKeyEvent(const input::NativeWebKeyboardEvent& event) {
|
||||
return;
|
||||
|
||||
// Show accelerator when "Alt" is pressed.
|
||||
if (menu_bar_visible_ && IsSingleAltKey(event))
|
||||
if (menu_bar_visible_ && IsAltKey(event))
|
||||
menu_bar_->SetAcceleratorVisibility(
|
||||
event.GetType() == blink::WebInputEvent::Type::kRawKeyDown);
|
||||
|
||||
@@ -134,11 +121,11 @@ void RootView::HandleKeyEvent(const input::NativeWebKeyboardEvent& event) {
|
||||
|
||||
// Toggle the menu bar only when a single Alt is released.
|
||||
if (event.GetType() == blink::WebInputEvent::Type::kRawKeyDown &&
|
||||
IsSingleAltKey(event)) {
|
||||
IsAltKey(event)) {
|
||||
// When a single Alt is pressed:
|
||||
menu_bar_alt_pressed_ = true;
|
||||
} else if (event.GetType() == blink::WebInputEvent::Type::kKeyUp &&
|
||||
IsSingleAltKey(event) && menu_bar_alt_pressed_) {
|
||||
IsAltKey(event) && menu_bar_alt_pressed_) {
|
||||
// When a single Alt is released right after a Alt is pressed:
|
||||
menu_bar_alt_pressed_ = false;
|
||||
if (menu_bar_autohide_)
|
||||
|
||||
@@ -43,7 +43,7 @@ UsbChooserController::UsbChooserController(
|
||||
: WebContentsObserver(web_contents),
|
||||
options_(std::move(options)),
|
||||
callback_(std::move(callback)),
|
||||
origin_(render_frame_host->GetMainFrame()->GetLastCommittedOrigin()),
|
||||
origin_(render_frame_host->GetLastCommittedOrigin()),
|
||||
usb_delegate_(usb_delegate),
|
||||
render_frame_host_id_(render_frame_host->GetGlobalId()) {
|
||||
chooser_context_ = UsbChooserContextFactory::GetForBrowserContext(
|
||||
|
||||
@@ -219,7 +219,7 @@ void WebContentsPermissionHelper::RequestPermission(
|
||||
base::Value::Dict details) {
|
||||
auto* permission_manager = static_cast<ElectronPermissionManager*>(
|
||||
web_contents_->GetBrowserContext()->GetPermissionControllerDelegate());
|
||||
auto origin = web_contents_->GetLastCommittedURL();
|
||||
auto origin = requesting_frame->GetLastCommittedOrigin().GetURL();
|
||||
permission_manager->RequestPermissionWithDetails(
|
||||
content::PermissionDescriptorUtil::
|
||||
CreatePermissionDescriptorForPermissionType(permission),
|
||||
|
||||
@@ -343,9 +343,6 @@ void WebContentsPreferences::AppendCommandLineSwitches(
|
||||
command_line->AppendSwitchASCII(::switches::kDisableBlinkFeatures,
|
||||
*disable_blink_features_);
|
||||
|
||||
if (node_integration_in_worker_)
|
||||
command_line->AppendSwitch(switches::kNodeIntegrationInWorker);
|
||||
|
||||
// We are appending args to a webContents so let's save the current state
|
||||
// of our preferences object so that during the lifetime of the WebContents
|
||||
// we can fetch the options used to initially configure the WebContents
|
||||
|
||||
54
shell/common/command_line_util_win.cc
Normal file
54
shell/common/command_line_util_win.cc
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2026 Microsoft GmbH.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/common/command_line_util_win.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace electron {
|
||||
|
||||
std::wstring AddQuoteForArg(const std::wstring& arg) {
|
||||
// We follow the quoting rules of CommandLineToArgvW.
|
||||
// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
|
||||
constexpr wchar_t kQuotableChars[] = L" \\\"";
|
||||
if (arg.find_first_of(kQuotableChars) == std::wstring::npos) {
|
||||
// No quoting necessary.
|
||||
return arg;
|
||||
}
|
||||
|
||||
std::wstring out;
|
||||
out.push_back(L'"');
|
||||
for (size_t i = 0; i < arg.size(); ++i) {
|
||||
if (arg[i] == '\\') {
|
||||
// Find the extent of this run of backslashes.
|
||||
size_t start = i, end = start + 1;
|
||||
for (; end < arg.size() && arg[end] == '\\'; ++end) {
|
||||
}
|
||||
size_t backslash_count = end - start;
|
||||
|
||||
// Backslashes are escapes only if the run is followed by a double quote.
|
||||
// Since we also will end the string with a double quote, we escape for
|
||||
// either a double quote or the end of the string.
|
||||
if (end == arg.size() || arg[end] == '"') {
|
||||
// To quote, we need to output 2x as many backslashes.
|
||||
backslash_count *= 2;
|
||||
}
|
||||
for (size_t j = 0; j < backslash_count; ++j)
|
||||
out.push_back('\\');
|
||||
|
||||
// Advance i to one before the end to balance i++ in loop.
|
||||
i = end - 1;
|
||||
} else if (arg[i] == '"') {
|
||||
out.push_back('\\');
|
||||
out.push_back('"');
|
||||
} else {
|
||||
out.push_back(arg[i]);
|
||||
}
|
||||
}
|
||||
out.push_back('"');
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
20
shell/common/command_line_util_win.h
Normal file
20
shell/common/command_line_util_win.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2026 Microsoft GmbH.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_COMMON_COMMAND_LINE_UTIL_WIN_H_
|
||||
#define ELECTRON_SHELL_COMMON_COMMAND_LINE_UTIL_WIN_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace electron {
|
||||
|
||||
// Quotes |arg| using CommandLineToArgvW-compatible quoting rules so that
|
||||
// the argument round-trips correctly through CreateProcess →
|
||||
// CommandLineToArgvW. If no quoting is necessary the string is returned
|
||||
// unchanged. See http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
|
||||
std::wstring AddQuoteForArg(const std::wstring& arg);
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_COMMON_COMMAND_LINE_UTIL_WIN_H_
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "net/cert/x509_certificate.h"
|
||||
#include "net/cert/x509_util.h"
|
||||
#include "net/http/http_response_headers.h"
|
||||
#include "net/http/http_util.h"
|
||||
#include "net/http/http_version.h"
|
||||
#include "net/url_request/redirect_info.h"
|
||||
#include "services/network/public/cpp/data_element.h"
|
||||
@@ -197,6 +198,10 @@ bool Converter<net::HttpResponseHeaders*>::FromV8(
|
||||
}
|
||||
std::string value;
|
||||
gin::ConvertFromV8(isolate, localStrVal, &value);
|
||||
if (!net::HttpUtil::IsValidHeaderName(key) ||
|
||||
!net::HttpUtil::IsValidHeaderValue(value)) {
|
||||
return false;
|
||||
}
|
||||
out->AddHeader(key, value);
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -279,10 +279,6 @@ inline constexpr base::cstring_view kAppPath = "app-path";
|
||||
// The command line switch versions of the options.
|
||||
inline constexpr base::cstring_view kScrollBounce = "scroll-bounce";
|
||||
|
||||
// Command switch passed to renderer process to control nodeIntegration.
|
||||
inline constexpr base::cstring_view kNodeIntegrationInWorker =
|
||||
"node-integration-in-worker";
|
||||
|
||||
// Widevine options
|
||||
// Path to Widevine CDM binaries.
|
||||
inline constexpr base::cstring_view kWidevineCdmPath = "widevine-cdm-path";
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "shell/renderer/api/electron_api_context_bridge.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -827,13 +828,18 @@ void ExposeAPIInWorld(v8::Isolate* isolate,
|
||||
ExposeAPI(isolate, source_context, target_isolate, target_context, key, api);
|
||||
}
|
||||
|
||||
gin_helper::Dictionary TraceKeyPath(const gin_helper::Dictionary& start,
|
||||
const std::vector<std::string>& key_path) {
|
||||
std::optional<gin_helper::Dictionary> TraceKeyPath(
|
||||
const gin_helper::Dictionary& start,
|
||||
const std::vector<std::string>& key_path,
|
||||
bool allow_silent_failure) {
|
||||
gin_helper::Dictionary current = start;
|
||||
for (size_t i = 0; i < key_path.size() - 1; i++) {
|
||||
CHECK(current.Get(key_path[i], ¤t))
|
||||
<< "Failed to get property '" << key_path[i] << "' at index " << i
|
||||
<< " in key path";
|
||||
if (!current.Get(key_path[i], ¤t)) {
|
||||
if (allow_silent_failure)
|
||||
return std::nullopt;
|
||||
CHECK(false) << "Failed to get property '" << key_path[i] << "' at index "
|
||||
<< i << " in key path";
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
@@ -842,7 +848,8 @@ void OverrideGlobalValueFromIsolatedWorld(
|
||||
v8::Isolate* isolate,
|
||||
const std::vector<std::string>& key_path,
|
||||
v8::Local<v8::Object> value,
|
||||
bool support_dynamic_properties) {
|
||||
bool support_dynamic_properties,
|
||||
bool allow_silent_failure) {
|
||||
if (key_path.empty())
|
||||
return;
|
||||
|
||||
@@ -854,7 +861,11 @@ void OverrideGlobalValueFromIsolatedWorld(
|
||||
gin_helper::Dictionary global(isolate, main_context->Global());
|
||||
|
||||
const std::string final_key = key_path[key_path.size() - 1];
|
||||
gin_helper::Dictionary target_object = TraceKeyPath(global, key_path);
|
||||
auto maybe_target_object =
|
||||
TraceKeyPath(global, key_path, allow_silent_failure);
|
||||
if (!maybe_target_object.has_value())
|
||||
return;
|
||||
gin_helper::Dictionary target_object = maybe_target_object.value();
|
||||
|
||||
{
|
||||
v8::Context::Scope main_context_scope(main_context);
|
||||
@@ -887,8 +898,8 @@ bool OverrideGlobalPropertyFromIsolatedWorld(
|
||||
gin_helper::Dictionary global(isolate, main_context->Global());
|
||||
|
||||
const std::string final_key = key_path[key_path.size() - 1];
|
||||
v8::Local<v8::Object> target_object =
|
||||
TraceKeyPath(global, key_path).GetHandle();
|
||||
auto target_dict = TraceKeyPath(global, key_path, false);
|
||||
v8::Local<v8::Object> target_object = target_dict.value().GetHandle();
|
||||
|
||||
{
|
||||
v8::Context::Scope main_context_scope(main_context);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "shell/common/node_bindings.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/node_util.h"
|
||||
#include "shell/common/options_switches.h"
|
||||
#include "shell/common/v8_util.h"
|
||||
#include "shell/renderer/electron_render_frame_observer.h"
|
||||
#include "shell/renderer/web_worker_observer.h"
|
||||
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "third_party/blink/public/web/web_local_frame.h"
|
||||
#include "third_party/blink/renderer/core/execution_context/execution_context.h" // nogncheck
|
||||
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" // nogncheck
|
||||
#include "third_party/blink/renderer/core/workers/worker_global_scope.h" // nogncheck
|
||||
#include "third_party/blink/renderer/core/workers/worker_settings.h" // nogncheck
|
||||
|
||||
#if BUILDFLAG(IS_LINUX) && (defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64))
|
||||
#define ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX
|
||||
@@ -207,44 +209,54 @@ void ElectronRendererClient::WillReleaseScriptContext(
|
||||
electron_bindings_->EnvironmentDestroyed(env);
|
||||
}
|
||||
|
||||
void ElectronRendererClient::WorkerScriptReadyForEvaluationOnWorkerThread(
|
||||
v8::Local<v8::Context> context) {
|
||||
namespace {
|
||||
|
||||
bool WorkerHasNodeIntegration(blink::ExecutionContext* ec) {
|
||||
// We do not create a Node.js environment in service or shared workers
|
||||
// owing to an inability to customize sandbox policies in these workers
|
||||
// given that they're run out-of-process.
|
||||
// Also avoid creating a Node.js environment for worklet global scope
|
||||
// created on the main thread.
|
||||
auto* ec = blink::ExecutionContext::From(context);
|
||||
if (ec->IsServiceWorkerGlobalScope() || ec->IsSharedWorkerGlobalScope() ||
|
||||
ec->IsMainThreadWorkletGlobalScope())
|
||||
return false;
|
||||
|
||||
auto* wgs = blink::DynamicTo<blink::WorkerGlobalScope>(ec);
|
||||
if (!wgs)
|
||||
return false;
|
||||
|
||||
// Read the nodeIntegrationInWorker preference from the worker's settings,
|
||||
// which were copied from the initiating frame's WebPreferences at worker
|
||||
// creation time. This ensures that in-process child windows with different
|
||||
// webPreferences get the correct per-frame value rather than a process-wide
|
||||
// value.
|
||||
auto* worker_settings = wgs->GetWorkerSettings();
|
||||
return worker_settings && worker_settings->NodeIntegrationInWorker();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ElectronRendererClient::WorkerScriptReadyForEvaluationOnWorkerThread(
|
||||
v8::Local<v8::Context> context) {
|
||||
auto* ec = blink::ExecutionContext::From(context);
|
||||
if (!WorkerHasNodeIntegration(ec))
|
||||
return;
|
||||
|
||||
// This won't be correct for in-process child windows with webPreferences
|
||||
// that have a different value for nodeIntegrationInWorker
|
||||
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kNodeIntegrationInWorker)) {
|
||||
auto* current = WebWorkerObserver::GetCurrent();
|
||||
if (current)
|
||||
return;
|
||||
WebWorkerObserver::Create()->WorkerScriptReadyForEvaluation(context);
|
||||
}
|
||||
auto* current = WebWorkerObserver::GetCurrent();
|
||||
if (current)
|
||||
return;
|
||||
WebWorkerObserver::Create()->WorkerScriptReadyForEvaluation(context);
|
||||
}
|
||||
|
||||
void ElectronRendererClient::WillDestroyWorkerContextOnWorkerThread(
|
||||
v8::Local<v8::Context> context) {
|
||||
auto* ec = blink::ExecutionContext::From(context);
|
||||
if (ec->IsServiceWorkerGlobalScope() || ec->IsSharedWorkerGlobalScope() ||
|
||||
ec->IsMainThreadWorkletGlobalScope())
|
||||
if (!WorkerHasNodeIntegration(ec))
|
||||
return;
|
||||
|
||||
// TODO(loc): Note that this will not be correct for in-process child windows
|
||||
// with webPreferences that have a different value for nodeIntegrationInWorker
|
||||
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kNodeIntegrationInWorker)) {
|
||||
auto* current = WebWorkerObserver::GetCurrent();
|
||||
if (current)
|
||||
current->ContextWillDestroy(context);
|
||||
}
|
||||
auto* current = WebWorkerObserver::GetCurrent();
|
||||
if (current)
|
||||
current->ContextWillDestroy(context);
|
||||
}
|
||||
|
||||
void ElectronRendererClient::SetUpWebAssemblyTrapHandler() {
|
||||
|
||||
@@ -1478,6 +1478,29 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('protocol scheme validation', () => {
|
||||
it('rejects empty protocol names', () => {
|
||||
expect(app.setAsDefaultProtocolClient('')).to.equal(false);
|
||||
expect(app.isDefaultProtocolClient('')).to.equal(false);
|
||||
expect(app.removeAsDefaultProtocolClient('')).to.equal(false);
|
||||
});
|
||||
|
||||
it('rejects non-conformant protocol names ', () => {
|
||||
// Starting with a digit.
|
||||
expect(app.setAsDefaultProtocolClient('0badscheme')).to.equal(false);
|
||||
// Starting with a hyphen.
|
||||
expect(app.setAsDefaultProtocolClient('-badscheme')).to.equal(false);
|
||||
// Containing backslashes.
|
||||
expect(app.setAsDefaultProtocolClient('http\\shell\\open\\command')).to.equal(false);
|
||||
// Containing forward slashes.
|
||||
expect(app.setAsDefaultProtocolClient('bad/protocol')).to.equal(false);
|
||||
// Containing spaces.
|
||||
expect(app.setAsDefaultProtocolClient('bad protocol')).to.equal(false);
|
||||
// Containing colons.
|
||||
expect(app.setAsDefaultProtocolClient('bad:protocol')).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'win32')('app launch through uri', () => {
|
||||
it('does not launch for argument following a URL', async () => {
|
||||
const appPath = path.join(fixturesPath, 'api', 'quit-app');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user