mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
116 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dce1b44b61 | ||
|
|
8b7a1ca78f | ||
|
|
00120e6337 | ||
|
|
3e524ffe31 | ||
|
|
57f29743b4 | ||
|
|
75b03a5aae | ||
|
|
6efe392926 | ||
|
|
16a5482dde | ||
|
|
047a48ebfd | ||
|
|
f78d95d3ad | ||
|
|
3314f0cbf5 | ||
|
|
05bf827fc0 | ||
|
|
03d98f04b6 | ||
|
|
b494afe684 | ||
|
|
f54c9df0cd | ||
|
|
dee6e01e9e | ||
|
|
483e39cc74 | ||
|
|
cd7490d233 | ||
|
|
b990bd6c97 | ||
|
|
56a0b45ef2 | ||
|
|
5871f81bb9 | ||
|
|
511f27506f | ||
|
|
8189ee64b9 | ||
|
|
cc52f07023 | ||
|
|
890adefb95 | ||
|
|
348e76b1d6 | ||
|
|
cfee3286b2 | ||
|
|
d7acbeb8ee | ||
|
|
b35c90777a | ||
|
|
b5bc30da45 | ||
|
|
3ca4944ad8 | ||
|
|
40fcfaa5e9 | ||
|
|
da6e16157f | ||
|
|
358d6f83cf | ||
|
|
c5918acbf1 | ||
|
|
dba8e7fe42 | ||
|
|
2fc331859f | ||
|
|
c3a3274826 | ||
|
|
675737397d | ||
|
|
5059502456 | ||
|
|
b1093f454c | ||
|
|
51146df170 | ||
|
|
1c59c8eb61 | ||
|
|
09bb0c14a7 | ||
|
|
fcef0963d2 | ||
|
|
b608874076 | ||
|
|
f06842e6f8 | ||
|
|
4119881a36 | ||
|
|
ecb9afd7d6 | ||
|
|
a70431f8db | ||
|
|
b21dae6bee | ||
|
|
cb3fef890f | ||
|
|
b61a6f8021 | ||
|
|
b390336a5c | ||
|
|
7a9747021d | ||
|
|
18fd51af5f | ||
|
|
9e51cf4a65 | ||
|
|
76a07c227d | ||
|
|
9308ac6e67 | ||
|
|
3823245a46 | ||
|
|
f49f74831b | ||
|
|
0b26e76739 | ||
|
|
079010f01a | ||
|
|
8f673fe81d | ||
|
|
1012fd687a | ||
|
|
871be236f8 | ||
|
|
cee4a64493 | ||
|
|
d71c08a404 | ||
|
|
5a0f75f42c | ||
|
|
f5e26258da | ||
|
|
6165f6afc9 | ||
|
|
35f871c702 | ||
|
|
8c3c382673 | ||
|
|
16ae9e16cf | ||
|
|
1d14d15d8c | ||
|
|
32df7f58bb | ||
|
|
66c3319458 | ||
|
|
49955512a8 | ||
|
|
977dc2527e | ||
|
|
4f70332460 | ||
|
|
d8e1f4f73b | ||
|
|
ec40581e83 | ||
|
|
22cdcf400c | ||
|
|
1b36d1d175 | ||
|
|
4578597dea | ||
|
|
20deb65058 | ||
|
|
7b7d22fa7c | ||
|
|
851350b9f3 | ||
|
|
7162f641b5 | ||
|
|
88e3f4a1e6 | ||
|
|
0d1900048d | ||
|
|
af65324717 | ||
|
|
6d1cc18322 | ||
|
|
4d73469ed5 | ||
|
|
ca17f574de | ||
|
|
a5fdd272b1 | ||
|
|
15f3c45fe9 | ||
|
|
3f01fb3110 | ||
|
|
96789ee78b | ||
|
|
4b8885ffc8 | ||
|
|
c47b196d02 | ||
|
|
d67c319642 | ||
|
|
6839dd92b8 | ||
|
|
d468a73edb | ||
|
|
e4dbd1407e | ||
|
|
37a422d05b | ||
|
|
26e7f27110 | ||
|
|
707e8d5b7b | ||
|
|
6fda94858d | ||
|
|
1eece7ada0 | ||
|
|
c33bfffeec | ||
|
|
02bf71fb43 | ||
|
|
74b85afca4 | ||
|
|
fbc398f9bc | ||
|
|
4c32d141d8 | ||
|
|
88bedfcf25 |
@@ -53,14 +53,8 @@ executors:
|
||||
description: "macOS executor size"
|
||||
type: enum
|
||||
enum: ["macos.x86.medium.gen2", "large"]
|
||||
xcode:
|
||||
description: "xcode version"
|
||||
default: 13.2.1
|
||||
type: enum
|
||||
enum: ["12.4.0", "13.2.1"]
|
||||
|
||||
macos:
|
||||
xcode: << parameters.xcode >>
|
||||
xcode: 13.3.0
|
||||
resource_class: << parameters.size >>
|
||||
|
||||
# Electron Runners
|
||||
@@ -131,6 +125,9 @@ env-apple-silicon: &env-apple-silicon
|
||||
USE_PREBUILT_V8_CONTEXT_SNAPSHOT: 1
|
||||
npm_config_arch: arm64
|
||||
|
||||
env-runner: &env-runner
|
||||
IS_ELECTRON_RUNNER: 1
|
||||
|
||||
env-arm64: &env-arm64
|
||||
GN_EXTRA_ARGS: 'target_cpu = "arm64" fatal_linker_warnings = false enable_linux_installer = false'
|
||||
MKSNAPSHOT_TOOLCHAIN: //build/toolchain/linux:clang_arm64
|
||||
@@ -346,7 +343,7 @@ step-wait-for-goma: &step-wait-for-goma
|
||||
sleep 5
|
||||
done
|
||||
echo "Goma ready"
|
||||
no_output_timeout: 2m
|
||||
no_output_timeout: 5m
|
||||
|
||||
step-restore-brew-cache: &step-restore-brew-cache
|
||||
restore_cache:
|
||||
@@ -498,6 +495,7 @@ step-install-signing-cert-on-mac: &step-install-signing-cert-on-mac
|
||||
name: Import and trust self-signed codesigning cert on MacOS
|
||||
command: |
|
||||
if [ "$TARGET_ARCH" != "arm64" ] && [ "`uname`" == "Darwin" ]; then
|
||||
sudo security authorizationdb write com.apple.trust-settings.admin allow
|
||||
cd src/electron
|
||||
./script/codesign/generate-identity.sh
|
||||
fi
|
||||
@@ -628,7 +626,7 @@ step-electron-publish: &step-electron-publish
|
||||
echo 'Uploading Electron release distribution to Azure'
|
||||
script/release/uploaders/upload.py --verbose --UPLOAD_TO_STORAGE
|
||||
else
|
||||
echo 'Uploading Electron release distribution to Github releases'
|
||||
echo 'Uploading Electron release distribution to GitHub releases'
|
||||
script/release/uploaders/upload.py --verbose
|
||||
fi
|
||||
|
||||
@@ -997,6 +995,7 @@ steps-electron-gn-check: &steps-electron-gn-check
|
||||
- *step-checkout-electron
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- install-python2-mac
|
||||
- *step-setup-env-for-build
|
||||
- *step-setup-goma-for-build
|
||||
- *step-generate-deps-hash
|
||||
@@ -1101,6 +1100,7 @@ steps-tests: &steps-tests
|
||||
- *step-setup-linux-for-headless-testing
|
||||
- *step-restore-brew-cache
|
||||
- *step-fix-known-hosts-linux
|
||||
- install-python2-mac
|
||||
- *step-install-signing-cert-on-mac
|
||||
|
||||
- run:
|
||||
@@ -1190,6 +1190,31 @@ steps-test-node: &steps-test-node
|
||||
|
||||
# Command Aliases
|
||||
commands:
|
||||
install-python2-mac:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v2.7.18-python-cache-{{ arch }}
|
||||
name: Restore python cache
|
||||
- run:
|
||||
name: Install python2 on macos
|
||||
command: |
|
||||
if [ "`uname`" == "Darwin" ] && [ "$IS_ELECTRON_RUNNER" != "1" ]; then
|
||||
if [ ! -f "python-downloads/python-2.7.18-macosx10.9.pkg" ]; then
|
||||
mkdir python-downloads
|
||||
echo 'Downloading Python 2.7.18'
|
||||
curl -O https://dev-cdn.electronjs.org/python/python-2.7.18-macosx10.9.pkg
|
||||
mv python-2.7.18-macosx10.9.pkg python-downloads
|
||||
else
|
||||
echo 'Using Python install from cache'
|
||||
fi
|
||||
sudo installer -pkg python-downloads/python-2.7.18-macosx10.9.pkg -target /
|
||||
fi
|
||||
- save_cache:
|
||||
paths:
|
||||
- python-downloads
|
||||
key: v2.7.18-python-cache-{{ arch }}
|
||||
name: Persisting python cache
|
||||
maybe-restore-portaled-src-cache:
|
||||
parameters:
|
||||
halt-if-successful:
|
||||
@@ -1357,6 +1382,7 @@ commands:
|
||||
- run: rm -rf src/electron
|
||||
- *step-restore-brew-cache
|
||||
- *step-install-gnutar-on-mac
|
||||
- install-python2-mac
|
||||
- *step-save-brew-cache
|
||||
- when:
|
||||
condition: << parameters.build >>
|
||||
@@ -1548,6 +1574,7 @@ commands:
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-restore-brew-cache
|
||||
- install-python2-mac
|
||||
- *step-get-more-space-on-mac
|
||||
- when:
|
||||
condition: << parameters.checkout >>
|
||||
@@ -1607,7 +1634,7 @@ jobs:
|
||||
name: linux-docker
|
||||
size: medium
|
||||
environment:
|
||||
<<: *env-linux-medium
|
||||
<<: *env-linux-2xlarge
|
||||
<<: *env-testing-build
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
|
||||
<<: *steps-electron-ts-compile-for-doc-change
|
||||
@@ -2180,7 +2207,6 @@ jobs:
|
||||
osx-testing-x64-tests:
|
||||
executor:
|
||||
name: macos
|
||||
xcode: 12.4.0
|
||||
size: macos.x86.medium.gen2
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
@@ -2194,12 +2220,12 @@ jobs:
|
||||
<<: *env-mac-large
|
||||
<<: *env-stack-dumping
|
||||
<<: *env-apple-silicon
|
||||
<<: *env-runner
|
||||
<<: *steps-tests
|
||||
|
||||
mas-testing-x64-tests:
|
||||
executor:
|
||||
name: macos
|
||||
xcode: 12.4.0
|
||||
size: macos.x86.medium.gen2
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
@@ -2213,6 +2239,7 @@ jobs:
|
||||
<<: *env-mac-large
|
||||
<<: *env-stack-dumping
|
||||
<<: *env-apple-silicon
|
||||
<<: *env-runner
|
||||
<<: *steps-tests
|
||||
|
||||
# Layer 4: Summary.
|
||||
|
||||
178
.github/workflows/electron_woa_testing.yml
vendored
Normal file
178
.github/workflows/electron_woa_testing.yml
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
name: Electron WOA Testing
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: '**'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
appveyor_job_id:
|
||||
description: 'Job Id of Appveyor WOA job to test'
|
||||
type: text
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
electron-woa-init:
|
||||
if: ${{ github.event_name == 'push' && github.repository == 'electron/electron' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Dummy step for push event
|
||||
run: |
|
||||
echo "This job is a needed initialization step for Electron WOA testing. Another test result will appear once the electron-woa-testing build is done."
|
||||
|
||||
electron-woa-testing:
|
||||
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'electron/electron' }}
|
||||
runs-on: [self-hosted, woa]
|
||||
permissions:
|
||||
checks: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: LouisBrunner/checks-action@v1.1.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: electron-woa-testing
|
||||
status: in_progress
|
||||
details_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
output: |
|
||||
{"summary":"Test In Progress","text_description":"See job details here: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}
|
||||
- name: Clean Workspace
|
||||
run: |
|
||||
Remove-Item * -Recurse -Force
|
||||
shell: powershell
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: src\electron
|
||||
fetch-depth: 0
|
||||
- name: Yarn install
|
||||
run: |
|
||||
cd src\electron
|
||||
node script/yarn.js install --frozen-lockfile
|
||||
- name: Download and extract dist.zip for test
|
||||
run: |
|
||||
$localArtifactPath = "$pwd\dist.zip"
|
||||
$serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/dist.zip"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
|
||||
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -osrc\out\Default -y $localArtifactPath
|
||||
shell: powershell
|
||||
- name: Download and extract native test executables for test
|
||||
run: |
|
||||
$localArtifactPath = "src\out\Default\shell_browser_ui_unittests.exe"
|
||||
$serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/shell_browser_ui_unittests.exe"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
|
||||
shell: powershell
|
||||
- name: Download and extract ffmpeg.zip for test
|
||||
run: |
|
||||
$localArtifactPath = "$pwd\ffmpeg.zip"
|
||||
$serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/ffmpeg.zip"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
|
||||
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -osrc\out\ffmpeg $localArtifactPath
|
||||
shell: powershell
|
||||
- name: Download node headers for test
|
||||
run: |
|
||||
$localArtifactPath = "src\node_headers.zip"
|
||||
$serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/node_headers.zip"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
|
||||
cd src
|
||||
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -y node_headers.zip
|
||||
shell: powershell
|
||||
- name: Download electron.lib for test
|
||||
run: |
|
||||
$localArtifactPath = "src\out\Default\electron.lib"
|
||||
$serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/electron.lib"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
|
||||
shell: powershell
|
||||
# Uncomment the following block if pdb files are needed to debug issues
|
||||
# - name: Download pdb files for detailed stacktraces
|
||||
# if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
# run: |
|
||||
# try {
|
||||
# $localArtifactPath = "src\pdb.zip"
|
||||
# $serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/pdb.zip"
|
||||
# Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
|
||||
# cd src
|
||||
# & "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -y pdb.zip
|
||||
# } catch {
|
||||
# Write-Host "There was an exception encountered while downloading pdb files:" $_.Exception.Message
|
||||
# } finally {
|
||||
# $global:LASTEXITCODE = 0
|
||||
# }
|
||||
# shell: powershell
|
||||
- name: Setup node headers
|
||||
run: |
|
||||
New-Item src\out\Default\gen\node_headers\Release -Type directory
|
||||
Copy-Item -path src\out\Default\electron.lib -destination src\out\Default\gen\node_headers\Release\node.lib
|
||||
shell: powershell
|
||||
- name: Run Electron Main process tests
|
||||
run: |
|
||||
cd src
|
||||
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
|
||||
set npm_config_arch=arm64
|
||||
cd electron
|
||||
node script/yarn test --runners=main --enable-logging --disable-features=CalculateNativeWinOcclusion
|
||||
env:
|
||||
ELECTRON_ENABLE_STACK_DUMPING: true
|
||||
ELECTRON_OUT_DIR: Default
|
||||
IGNORE_YARN_INSTALL_ERROR: 1
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
ELECTRON_SKIP_NATIVE_MODULE_TESTS: true
|
||||
- name: Run Electron Remote based tests
|
||||
if: ${{ success() || failure() }}
|
||||
run: |
|
||||
cd src
|
||||
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
|
||||
set npm_config_arch=arm64
|
||||
cd electron
|
||||
node script/yarn test --runners=remote --enable-logging --disable-features=CalculateNativeWinOcclusion
|
||||
env:
|
||||
ELECTRON_OUT_DIR: Default
|
||||
IGNORE_YARN_INSTALL_ERROR: 1
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
ELECTRON_SKIP_NATIVE_MODULE_TESTS: true
|
||||
- name: Verify ffmpeg
|
||||
run: |
|
||||
cd src
|
||||
echo "Verifying non proprietary ffmpeg"
|
||||
python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
|
||||
shell: cmd
|
||||
- name: Kill processes left running from last test run
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
Get-Process | Where Name -Like "electron*" | Stop-Process
|
||||
Get-Process | Where Name -Like "msedge*" | Stop-Process
|
||||
shell: powershell
|
||||
- name: Delete user app data directories
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
Remove-Item -path $env:APPDATA/Electron* -Recurse -Force -ErrorAction Ignore
|
||||
shell: powershell
|
||||
- uses: LouisBrunner/checks-action@v1.1.1
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: electron-woa-testing
|
||||
conclusion: "${{ job.status }}"
|
||||
details_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
output: |
|
||||
{"summary":"${{ job.status }}","text_description":"See job details here: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}
|
||||
- uses: LouisBrunner/checks-action@v1.1.1
|
||||
if: ${{ success() }}
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: electron-woa-testing
|
||||
conclusion: "${{ job.status }}"
|
||||
details_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
output: |
|
||||
{"summary":"Job Succeeded","text_description":"See job details here: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}
|
||||
- uses: LouisBrunner/checks-action@v1.1.1
|
||||
if: ${{ ! success() }}
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: electron-woa-testing
|
||||
conclusion: "${{ job.status }}"
|
||||
details_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
output: |
|
||||
{"summary":"Job Failed","text_description":"See job details here: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}
|
||||
@@ -23,7 +23,5 @@
|
||||
"br_spaces": 0
|
||||
},
|
||||
"single-h1": false,
|
||||
"no-inline-html": {
|
||||
"allowed_elements": ["br"]
|
||||
}
|
||||
"no-inline-html": false
|
||||
}
|
||||
|
||||
6
BUILD.gn
6
BUILD.gn
@@ -80,7 +80,10 @@ if (is_linux) {
|
||||
}
|
||||
|
||||
generate_stubs("electron_gtk_stubs") {
|
||||
sigs = [ "shell/browser/ui/electron_gtk.sigs" ]
|
||||
sigs = [
|
||||
"shell/browser/ui/electron_gdk_pixbuf.sigs",
|
||||
"shell/browser/ui/electron_gtk.sigs",
|
||||
]
|
||||
extra_header = "shell/browser/ui/electron_gtk.fragment"
|
||||
output_name = "electron_gtk_stubs"
|
||||
public_deps = [ "//ui/gtk:gtk_config" ]
|
||||
@@ -363,6 +366,7 @@ source_set("electron_lib") {
|
||||
"//chrome/app/resources:platform_locale_settings",
|
||||
"//components/autofill/core/common:features",
|
||||
"//components/certificate_transparency",
|
||||
"//components/embedder_support:browser_util",
|
||||
"//components/language/core/browser",
|
||||
"//components/net_log",
|
||||
"//components/network_hints/browser",
|
||||
|
||||
@@ -1 +1 @@
|
||||
18.2.4
|
||||
18.3.9
|
||||
12
appveyor.yml
12
appveyor.yml
@@ -38,16 +38,6 @@ environment:
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
MOCHA_MULTI_REPORTERS: mocha-appveyor-reporter, tap
|
||||
GOMA_FALLBACK_ON_AUTH_FAILURE: true
|
||||
notifications:
|
||||
- provider: Webhook
|
||||
url: https://electron-mission-control.herokuapp.com/rest/appveyor-hook
|
||||
method: POST
|
||||
headers:
|
||||
x-mission-control-secret:
|
||||
secure: 90BLVPcqhJPG7d24v0q/RRray6W3wDQ8uVQlQjOHaBWkw1i8FoA1lsjr2C/v1dVok+tS2Pi6KxDctPUkwIb4T27u4RhvmcPzQhVpfwVJAG9oNtq+yKN7vzHfg7k/pojEzVdJpQLzeJGcSrZu7VY39Q==
|
||||
on_build_success: false
|
||||
on_build_failure: true
|
||||
on_build_status_changed: false
|
||||
build_script:
|
||||
- ps: >-
|
||||
if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
|
||||
@@ -243,7 +233,7 @@ deploy_script:
|
||||
& python script\release\uploaders\upload.py --verbose
|
||||
}
|
||||
} elseif (Test-Path Env:\TEST_WOA) {
|
||||
node script/release/ci-release-build.js --job=electron-woa-testing --ci=VSTS --armTest --appveyorJobId=$env:APPVEYOR_JOB_ID $env:APPVEYOR_REPO_BRANCH
|
||||
node script/release/ci-release-build.js --job=electron-woa-testing --ci=GHA --appveyorJobId=$env:APPVEYOR_JOB_ID $env:APPVEYOR_REPO_BRANCH
|
||||
}
|
||||
on_finish:
|
||||
- if exist src\electron\electron.log ( appveyor-retry appveyor PushArtifact src\electron\electron.log )
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
steps:
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Copy Files to: src/electron'
|
||||
inputs:
|
||||
TargetFolder: src/electron
|
||||
|
||||
- bash: |
|
||||
cd src/electron
|
||||
node script/yarn.js install --frozen-lockfile
|
||||
displayName: 'Yarn install'
|
||||
|
||||
- bash: |
|
||||
export ZIP_DEST=$PWD/src/out/Default
|
||||
echo "##vso[task.setvariable variable=ZIP_DEST]$ZIP_DEST"
|
||||
mkdir -p $ZIP_DEST
|
||||
cd src/electron
|
||||
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=dist.zip --dest=$ZIP_DEST
|
||||
cd $ZIP_DEST
|
||||
unzip -o dist.zip
|
||||
xattr -cr Electron.app
|
||||
displayName: 'Download and unzip dist files for test'
|
||||
env:
|
||||
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
|
||||
|
||||
- bash: |
|
||||
export FFMPEG_ZIP_DEST=$PWD/src/out/ffmpeg
|
||||
mkdir -p $FFMPEG_ZIP_DEST
|
||||
cd src/electron
|
||||
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=ffmpeg.zip --dest=$FFMPEG_ZIP_DEST
|
||||
cd $FFMPEG_ZIP_DEST
|
||||
unzip -o ffmpeg.zip
|
||||
displayName: 'Download and unzip ffmpeg for test'
|
||||
env:
|
||||
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
|
||||
|
||||
- bash: |
|
||||
export NODE_HEADERS_DEST=$PWD/src/out/Default/gen
|
||||
mkdir -p $NODE_HEADERS_DEST
|
||||
cd src/electron
|
||||
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=node_headers.tar.gz --dest=$NODE_HEADERS_DEST
|
||||
cd $NODE_HEADERS_DEST
|
||||
tar xzf node_headers.tar.gz
|
||||
displayName: 'Download and untar node header files for test'
|
||||
env:
|
||||
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
|
||||
|
||||
- bash: |
|
||||
export CROSS_ARCH_SNAPSHOTS=$PWD/src/out/Default/cross-arch-snapshots
|
||||
mkdir -p $CROSS_ARCH_SNAPSHOTS
|
||||
cd src/electron
|
||||
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=cross-arch-snapshots/snapshot_blob.bin --dest=$CROSS_ARCH_SNAPSHOTS
|
||||
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=cross-arch-snapshots/v8_context_snapshot.arm64.bin --dest=$CROSS_ARCH_SNAPSHOTS
|
||||
displayName: 'Download cross arch snapshot files'
|
||||
env:
|
||||
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
|
||||
|
||||
- bash: |
|
||||
cd src
|
||||
export ELECTRON_OUT_DIR=Default
|
||||
export npm_config_arch=arm64
|
||||
(cd electron && node script/yarn test --enable-logging --runners main)
|
||||
displayName: 'Run Electron main tests'
|
||||
timeoutInMinutes: 20
|
||||
env:
|
||||
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
|
||||
IGNORE_YARN_INSTALL_ERROR: 1
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
|
||||
- bash: |
|
||||
cd src
|
||||
export ELECTRON_OUT_DIR=Default
|
||||
export npm_config_arch=arm64
|
||||
(cd electron && node script/yarn test --enable-logging --runners remote)
|
||||
displayName: 'Run Electron remote tests'
|
||||
timeoutInMinutes: 20
|
||||
condition: succeededOrFailed()
|
||||
env:
|
||||
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
|
||||
IGNORE_YARN_INSTALL_ERROR: 1
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
|
||||
- bash: |
|
||||
cd src
|
||||
python electron/script/verify-ffmpeg.py --source-root "$PWD" --build-dir out/Default --ffmpeg-path out/ffmpeg
|
||||
displayName: Verify non proprietary ffmpeg
|
||||
timeoutInMinutes: 5
|
||||
condition: succeededOrFailed()
|
||||
env:
|
||||
TARGET_ARCH: arm64
|
||||
|
||||
- bash: |
|
||||
cd src
|
||||
echo Verify cross arch snapshot
|
||||
python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default --snapshot-files-dir $PWD/out/Default/cross-arch-snapshots
|
||||
displayName: Verify cross arch snapshot
|
||||
timeoutInMinutes: 5
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Publish Test Results'
|
||||
inputs:
|
||||
testResultsFiles: '*.xml'
|
||||
|
||||
searchFolder: '$(System.DefaultWorkingDirectory)/src/junit/'
|
||||
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- bash: killall Electron || echo "No Electron processes left running"
|
||||
displayName: 'Kill processes left running from last test run'
|
||||
condition: always()
|
||||
|
||||
- bash: |
|
||||
rm -rf ~/Library/Application\ Support/Electron*
|
||||
rm -rf ~/Library/Application\ Support/electron*
|
||||
displayName: 'Delete user app data directories'
|
||||
condition: always()
|
||||
|
||||
- task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3
|
||||
displayName: 'Clean Agent Directories'
|
||||
|
||||
condition: always()
|
||||
@@ -1,130 +0,0 @@
|
||||
workspace:
|
||||
clean: all
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
path: src\electron
|
||||
|
||||
- script: |
|
||||
node script/yarn.js install --frozen-lockfile
|
||||
displayName: 'Yarn install'
|
||||
|
||||
- powershell: |
|
||||
$localArtifactPath = "$pwd\dist.zip"
|
||||
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/dist.zip"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
|
||||
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -o$(Pipeline.Workspace)\src\out\Default -y $localArtifactPath
|
||||
displayName: 'Download and extract dist.zip for test'
|
||||
env:
|
||||
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
- powershell: |
|
||||
$localArtifactPath = "$(Pipeline.Workspace)\src\out\Default\shell_browser_ui_unittests.exe"
|
||||
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/shell_browser_ui_unittests.exe"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
|
||||
displayName: 'Download and extract native test executables for test'
|
||||
env:
|
||||
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
- powershell: |
|
||||
$localArtifactPath = "$pwd\ffmpeg.zip"
|
||||
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/ffmpeg.zip"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
|
||||
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -o$(Pipeline.Workspace)\src\out\ffmpeg $localArtifactPath
|
||||
displayName: 'Download and extract ffmpeg.zip for test'
|
||||
env:
|
||||
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
- powershell: |
|
||||
$localArtifactPath = "$(Pipeline.Workspace)\src\node_headers.zip"
|
||||
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/node_headers.zip"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
|
||||
cd $(Pipeline.Workspace)\src
|
||||
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -y node_headers.zip
|
||||
displayName: 'Download node headers for test'
|
||||
env:
|
||||
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
- powershell: |
|
||||
$localArtifactPath = "$(Pipeline.Workspace)\src\out\Default\electron.lib"
|
||||
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/electron.lib"
|
||||
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
|
||||
displayName: 'Download electron.lib for test'
|
||||
env:
|
||||
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
# Uncomment the following block if pdb files are needed to debug issues
|
||||
# - powershell: |
|
||||
# try {
|
||||
# $localArtifactPath = "$(Pipeline.Workspace)\src\pdb.zip"
|
||||
# $serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/pdb.zip"
|
||||
# Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
|
||||
# cd $(Pipeline.Workspace)\src
|
||||
# & "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -y pdb.zip
|
||||
# } catch {
|
||||
# Write-Host "There was an exception encountered while downloading pdb files:" $_.Exception.Message
|
||||
# } finally {
|
||||
# $global:LASTEXITCODE = 0
|
||||
# }
|
||||
# displayName: 'Download pdb files for detailed stacktraces'
|
||||
# env:
|
||||
# APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
|
||||
|
||||
- powershell: |
|
||||
New-Item $(Pipeline.Workspace)\src\out\Default\gen\node_headers\Release -Type directory
|
||||
Copy-Item -path $(Pipeline.Workspace)\src\out\Default\electron.lib -destination $(Pipeline.Workspace)\src\out\Default\gen\node_headers\Release\node.lib
|
||||
displayName: 'Setup node headers'
|
||||
|
||||
- script: |
|
||||
cd $(Pipeline.Workspace)\src
|
||||
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
|
||||
set npm_config_arch=arm64
|
||||
cd electron
|
||||
node script/yarn test --runners=main --enable-logging --disable-features=CalculateNativeWinOcclusion
|
||||
displayName: 'Run Electron Main process tests'
|
||||
env:
|
||||
ELECTRON_ENABLE_STACK_DUMPING: true
|
||||
ELECTRON_OUT_DIR: Default
|
||||
IGNORE_YARN_INSTALL_ERROR: 1
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
|
||||
- script: |
|
||||
cd $(Pipeline.Workspace)\src
|
||||
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
|
||||
set npm_config_arch=arm64
|
||||
cd electron
|
||||
node script/yarn test --runners=remote --enable-logging --disable-features=CalculateNativeWinOcclusion
|
||||
displayName: 'Run Electron Remote based tests'
|
||||
env:
|
||||
ELECTRON_OUT_DIR: Default
|
||||
IGNORE_YARN_INSTALL_ERROR: 1
|
||||
ELECTRON_TEST_RESULTS_DIR: junit
|
||||
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
condition: succeededOrFailed()
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: 'Publish Test Results'
|
||||
inputs:
|
||||
testResultsFiles: '*.xml'
|
||||
searchFolder: '$(Pipeline.Workspace)/src/junit/'
|
||||
condition: always()
|
||||
|
||||
- script: |
|
||||
cd $(Pipeline.Workspace)\src
|
||||
echo "Verifying non proprietary ffmpeg"
|
||||
python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
|
||||
displayName: 'Verify ffmpeg'
|
||||
|
||||
- powershell: |
|
||||
Get-Process | Where Name –Like "electron*" | Stop-Process
|
||||
Get-Process | Where Name –Like "msedge*" | Stop-Process
|
||||
displayName: 'Kill processes left running from last test run'
|
||||
condition: always()
|
||||
|
||||
- powershell: |
|
||||
Remove-Item -path $env:APPDATA/Electron* -Recurse -Force -ErrorAction Ignore
|
||||
displayName: 'Delete user app data directories'
|
||||
condition: always()
|
||||
@@ -24,7 +24,11 @@ template("extract_symbols") {
|
||||
assert(defined(invoker.binary), "Need binary to dump")
|
||||
assert(defined(invoker.symbol_dir), "Need directory for symbol output")
|
||||
|
||||
dump_syms_label = "//third_party/breakpad:dump_syms($host_toolchain)"
|
||||
if (host_os == "win" && target_cpu == "x86") {
|
||||
dump_syms_label = "//third_party/breakpad:dump_syms(//build/toolchain/win:win_clang_x64)"
|
||||
} else {
|
||||
dump_syms_label = "//third_party/breakpad:dump_syms($host_toolchain)"
|
||||
}
|
||||
dump_syms_binary = get_label_info(dump_syms_label, "root_out_dir") +
|
||||
"/dump_syms$_host_executable_suffix"
|
||||
|
||||
|
||||
@@ -70,9 +70,6 @@ an issue:
|
||||
* [Windows Store](tutorial/windows-store-guide.md)
|
||||
* [Snapcraft](tutorial/snapcraft.md)
|
||||
* [Updates](tutorial/updates.md)
|
||||
* [Deploying an Update Server](tutorial/updates.md#deploying-an-update-server)
|
||||
* [Implementing Updates in Your App](tutorial/updates.md#implementing-updates-in-your-app)
|
||||
* [Applying Updates](tutorial/updates.md#applying-updates)
|
||||
* [Getting Support](tutorial/support.md)
|
||||
|
||||
## Detailed Tutorials
|
||||
|
||||
@@ -484,7 +484,6 @@ Returns:
|
||||
* `argv` string[] - An array of the second instance's command line arguments
|
||||
* `workingDirectory` string - The second instance's working directory
|
||||
* `additionalData` unknown - A JSON object of additional data passed from the second instance
|
||||
* `ackCallback` unknown - A function that can be used to send data back to the second instance
|
||||
|
||||
This event will be emitted inside the primary instance of your application
|
||||
when a second instance has been executed and calls `app.requestSingleInstanceLock()`.
|
||||
@@ -496,35 +495,12 @@ non-minimized.
|
||||
|
||||
**Note:** If the second instance is started by a different user than the first, the `argv` array will not include the arguments.
|
||||
|
||||
**Note:** `ackCallback` allows the user to send data back to the
|
||||
second instance during the `app.requestSingleInstanceLock()` flow.
|
||||
This callback can be used for cases where the second instance
|
||||
needs to obtain additional information from the first instance
|
||||
before quitting.
|
||||
|
||||
Currently, the limit on the message size is kMaxMessageLength,
|
||||
or around 32kB. To be safe, keep the amount of data passed to 31kB at most.
|
||||
|
||||
In order to call the callback, `event.preventDefault()` must be called, first.
|
||||
If the callback is not called in either case, `null` will be sent back.
|
||||
If `event.preventDefault()` is not called, but `ackCallback` is called
|
||||
by the user in the event, then the behaviour is undefined.
|
||||
|
||||
This event is guaranteed to be emitted after the `ready` event of `app`
|
||||
gets emitted.
|
||||
|
||||
**Note:** Extra command line arguments might be added by Chromium,
|
||||
such as `--original-process-start-time`.
|
||||
|
||||
### Event: 'first-instance-ack'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `additionalData` unknown - A JSON object of additional data passed from the first instance, in response to the first instance's `second-instance` event.
|
||||
|
||||
This event will be emitted within the second instance during the call to `app.requestSingleInstanceLock()`, when the first instance calls the `ackCallback` provided by the `second-instance` event handler.
|
||||
|
||||
## Methods
|
||||
|
||||
The `app` object has the following methods:
|
||||
@@ -985,33 +961,21 @@ starts:
|
||||
const { app } = require('electron')
|
||||
let myWindow = null
|
||||
|
||||
app.on('first-instance-ack', (event, additionalData) => {
|
||||
// Print out the ack received from the first instance.
|
||||
// Note this event handler must come before the requestSingleInstanceLock call.
|
||||
// Expected output: '{"myAckKey":"myAckValue"}'
|
||||
console.log(JSON.stringify(additionalData))
|
||||
})
|
||||
|
||||
const additionalData = { myKey: 'myValue' }
|
||||
const gotTheLock = app.requestSingleInstanceLock(additionalData)
|
||||
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory, additionalData, ackCallback) => {
|
||||
// We must call preventDefault if we're sending back data.
|
||||
event.preventDefault()
|
||||
app.on('second-instance', (event, commandLine, workingDirectory, additionalData) => {
|
||||
// Print out data received from the second instance.
|
||||
// Expected output: '{"myKey":"myValue"}'
|
||||
console.log(JSON.stringify(additionalData))
|
||||
console.log(additionalData)
|
||||
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (myWindow) {
|
||||
if (myWindow.isMinimized()) myWindow.restore()
|
||||
myWindow.focus()
|
||||
}
|
||||
const ackData = { myAckKey: 'myAckValue' }
|
||||
ackCallback(ackData)
|
||||
})
|
||||
|
||||
// Create myWindow, load the rest of the app, etc...
|
||||
|
||||
@@ -1638,6 +1638,8 @@ Opens the devtools.
|
||||
When `contents` is a `<webview>` tag, the `mode` would be `detach` by default,
|
||||
explicitly passing an empty `mode` can force using last used dock state.
|
||||
|
||||
On Windows, if Windows Control Overlay is enabled, Devtools will be opened with `mode: 'detach'`.
|
||||
|
||||
#### `contents.closeDevTools()`
|
||||
|
||||
Closes the devtools.
|
||||
|
||||
@@ -16,7 +16,7 @@ win.loadURL('https://twitter.com')
|
||||
|
||||
win.webContents.on(
|
||||
'did-frame-navigate',
|
||||
(event, url, isMainFrame, frameProcessId, frameRoutingId) => {
|
||||
(event, url, httpResponseCode, httpStatusText, isMainFrame, frameProcessId, frameRoutingId) => {
|
||||
const frame = webFrameMain.fromId(frameProcessId, frameRoutingId)
|
||||
if (frame) {
|
||||
const code = 'document.body.innerHTML = document.body.innerHTML.replaceAll("heck", "h*ck")'
|
||||
|
||||
21
docs/fiddles/tutorial-first-app/index.html
Normal file
21
docs/fiddles/tutorial-first-app/index.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'"
|
||||
/>
|
||||
<meta
|
||||
http-equiv="X-Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'"
|
||||
/>
|
||||
<title>Hello from Electron renderer!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello from Electron renderer!</h1>
|
||||
<p>👋</p>
|
||||
<p id="info"></p>
|
||||
</body>
|
||||
<script src="./renderer.js"></script>
|
||||
</html>
|
||||
26
docs/fiddles/tutorial-first-app/main.js
Normal file
26
docs/fiddles/tutorial-first-app/main.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
});
|
||||
|
||||
win.loadFile('index.html');
|
||||
};
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow();
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
21
docs/fiddles/tutorial-preload/index.html
Normal file
21
docs/fiddles/tutorial-preload/index.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'"
|
||||
/>
|
||||
<meta
|
||||
http-equiv="X-Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'"
|
||||
/>
|
||||
<title>Hello from Electron renderer!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello from Electron renderer!</h1>
|
||||
<p>👋</p>
|
||||
<p id="info"></p>
|
||||
</body>
|
||||
<script src="./renderer.js"></script>
|
||||
</html>
|
||||
30
docs/fiddles/tutorial-preload/main.js
Normal file
30
docs/fiddles/tutorial-preload/main.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
const path = require('path');
|
||||
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
},
|
||||
});
|
||||
|
||||
win.loadFile('index.html');
|
||||
};
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow();
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
7
docs/fiddles/tutorial-preload/preload.js
Normal file
7
docs/fiddles/tutorial-preload/preload.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const { contextBridge } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('versions', {
|
||||
node: () => process.versions.node,
|
||||
chrome: () => process.versions.chrome,
|
||||
electron: () => process.versions.electron,
|
||||
});
|
||||
2
docs/fiddles/tutorial-preload/renderer.js
Normal file
2
docs/fiddles/tutorial-preload/renderer.js
Normal file
@@ -0,0 +1,2 @@
|
||||
const information = document.getElementById('info');
|
||||
information.innerText = `This app is using Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`;
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 59 KiB |
BIN
docs/images/preload-example.png
Normal file
BIN
docs/images/preload-example.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
BIN
docs/images/windows-taskbar-icon-overlay.png
Normal file
BIN
docs/images/windows-taskbar-icon-overlay.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/images/windows-taskbar-jumplist.png
Normal file
BIN
docs/images/windows-taskbar-jumplist.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 173 KiB |
BIN
docs/images/windows-taskbar-thumbnail-toolbar.png
Normal file
BIN
docs/images/windows-taskbar-thumbnail-toolbar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
@@ -1,26 +1,26 @@
|
||||
# Application Distribution
|
||||
---
|
||||
title: 'Application Packaging'
|
||||
description: 'To distribute your app with Electron, you need to package and rebrand it. To do this, you can either use specialized tooling or manual approaches.'
|
||||
slug: application-distribution
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
To distribute your app with Electron, you need to package and rebrand it.
|
||||
To do this, you can either use specialized tooling or manual approaches.
|
||||
To distribute your app with Electron, you need to package and rebrand it. To do this, you
|
||||
can either use specialized tooling or manual approaches.
|
||||
|
||||
## With tooling
|
||||
|
||||
You can use the following tools to distribute your application:
|
||||
There are a couple tools out there that exist to package and distribute your Electron app.
|
||||
We recommend using [Electron Forge](https://www.electronforge.io). You can check out
|
||||
its documentation directly, or refer to the [Packaging and Distribution](./tutorial-5-packaging.md)
|
||||
part of the Electron tutorial.
|
||||
|
||||
* [electron-forge](https://github.com/electron-userland/electron-forge)
|
||||
* [electron-builder](https://github.com/electron-userland/electron-builder)
|
||||
* [electron-packager](https://github.com/electron/electron-packager)
|
||||
## Manual packaging
|
||||
|
||||
These tools will take care of all the steps you need to take to end up with a
|
||||
distributable Electron application, such as bundling your application,
|
||||
rebranding the executable, and setting the right icons.
|
||||
If you prefer the manual approach, there are 2 ways to distribute your application:
|
||||
|
||||
You can check the example of how to package your app with `electron-forge` in
|
||||
the [Quick Start guide](quick-start.md#package-and-distribute-your-application).
|
||||
|
||||
## Manual distribution
|
||||
- With prebuilt binaries
|
||||
- With an app source code archive
|
||||
|
||||
### With prebuilt binaries
|
||||
|
||||
@@ -29,21 +29,19 @@ binaries](https://github.com/electron/electron/releases). Next, the folder
|
||||
containing your app should be named `app` and placed in Electron's resources
|
||||
directory as shown in the following examples.
|
||||
|
||||
> *NOTE:* the location of Electron's prebuilt binaries is indicated
|
||||
:::note
|
||||
The location of Electron's prebuilt binaries is indicated
|
||||
with `electron/` in the examples below.
|
||||
:::
|
||||
|
||||
*On macOS:*
|
||||
|
||||
```plaintext
|
||||
```plain title='macOS'
|
||||
electron/Electron.app/Contents/Resources/app/
|
||||
├── package.json
|
||||
├── main.js
|
||||
└── index.html
|
||||
```
|
||||
|
||||
*On Windows and Linux:*
|
||||
|
||||
```plaintext
|
||||
```plain title='Windows and Linux'
|
||||
electron/resources/app
|
||||
├── package.json
|
||||
├── main.js
|
||||
@@ -54,7 +52,7 @@ Then execute `Electron.app` on macOS, `electron` on Linux, or `electron.exe`
|
||||
on Windows, and Electron will start as your app. The `electron` directory
|
||||
will then be your distribution to deliver to users.
|
||||
|
||||
### With an app source code archive
|
||||
### With an app source code archive (asar)
|
||||
|
||||
Instead of shipping your app by copying all of its source files, you can
|
||||
package your app into an [asar] archive to improve the performance of reading
|
||||
@@ -65,16 +63,12 @@ To use an `asar` archive to replace the `app` folder, you need to rename the
|
||||
archive to `app.asar`, and put it under Electron's resources directory like
|
||||
below, and Electron will then try to read the archive and start from it.
|
||||
|
||||
*On macOS:*
|
||||
|
||||
```plaintext
|
||||
```plain title='macOS'
|
||||
electron/Electron.app/Contents/Resources/
|
||||
└── app.asar
|
||||
```
|
||||
|
||||
*On Windows and Linux:*
|
||||
|
||||
```plaintext
|
||||
```plain title='Windows'
|
||||
electron/resources/
|
||||
└── app.asar
|
||||
```
|
||||
@@ -87,47 +81,44 @@ You can find more details on how to use `asar` in the
|
||||
After bundling your app into Electron, you will want to rebrand Electron
|
||||
before distributing it to users.
|
||||
|
||||
#### macOS
|
||||
- **Windows:** You can rename `electron.exe` to any name you like, and edit
|
||||
its icon and other information with tools like [rcedit](https://github.com/electron/rcedit).
|
||||
- **Linux:** You can rename the `electron` executable to any name you like.
|
||||
- **macOS:** You can rename `Electron.app` to any name you want, and you also have to rename
|
||||
the `CFBundleDisplayName`, `CFBundleIdentifier` and `CFBundleName` fields in the
|
||||
following files:
|
||||
|
||||
You can rename `Electron.app` to any name you want, and you also have to rename
|
||||
the `CFBundleDisplayName`, `CFBundleIdentifier` and `CFBundleName` fields in the
|
||||
following files:
|
||||
- `Electron.app/Contents/Info.plist`
|
||||
- `Electron.app/Contents/Frameworks/Electron Helper.app/Contents/Info.plist`
|
||||
|
||||
* `Electron.app/Contents/Info.plist`
|
||||
* `Electron.app/Contents/Frameworks/Electron Helper.app/Contents/Info.plist`
|
||||
You can also rename the helper app to avoid showing `Electron Helper` in the
|
||||
Activity Monitor, but make sure you have renamed the helper app's executable
|
||||
file's name.
|
||||
|
||||
You can also rename the helper app to avoid showing `Electron Helper` in the
|
||||
Activity Monitor, but make sure you have renamed the helper app's executable
|
||||
file's name.
|
||||
The structure of a renamed app would be like:
|
||||
|
||||
The structure of a renamed app would be like:
|
||||
|
||||
```plaintext
|
||||
```plain
|
||||
MyApp.app/Contents
|
||||
├── Info.plist
|
||||
├── MacOS/
|
||||
│ └── MyApp
|
||||
│ └── MyApp
|
||||
└── Frameworks/
|
||||
└── MyApp Helper.app
|
||||
├── Info.plist
|
||||
└── MacOS/
|
||||
└── MyApp Helper
|
||||
└── MyApp Helper
|
||||
```
|
||||
|
||||
#### Windows
|
||||
:::note
|
||||
|
||||
You can rename `electron.exe` to any name you like, and edit its icon and other
|
||||
information with tools like [rcedit](https://github.com/electron/rcedit).
|
||||
|
||||
#### Linux
|
||||
|
||||
You can rename the `electron` executable to any name you like.
|
||||
|
||||
### Rebranding by rebuilding Electron from source
|
||||
|
||||
It is also possible to rebrand Electron by changing the product name and
|
||||
it is also possible to rebrand Electron by changing the product name and
|
||||
building it from source. To do this you need to set the build argument
|
||||
corresponding to the product name (`electron_product_name = "YourProductName"`)
|
||||
in the `args.gn` file and rebuild.
|
||||
|
||||
Keep in mind this is not recommended as setting up the environment to compile
|
||||
from source is not trivial and takes significant time.
|
||||
|
||||
:::
|
||||
|
||||
[asar]: https://github.com/electron/asar
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
# Code Signing
|
||||
---
|
||||
title: 'Code Signing'
|
||||
description: 'Code signing is a security technology that you use to certify that an app was created by you.'
|
||||
slug: code-signing
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
Code signing is a security technology that you use to certify that an app was
|
||||
created by you.
|
||||
created by you. You should sign your application so it does not trigger any
|
||||
operating system security checks.
|
||||
|
||||
On macOS the system can detect any change to the app, whether the change is
|
||||
On macOS, the system can detect any change to the app, whether the change is
|
||||
introduced accidentally or by malicious code.
|
||||
|
||||
On Windows, the system assigns a trust level to your code signing certificate
|
||||
which if you don't have, or if your trust level is low, will cause security
|
||||
dialogs to appear when users start using your application. Trust level builds
|
||||
dialogs to appear when users start using your application. Trust level builds
|
||||
over time so it's better to start code signing as early as possible.
|
||||
|
||||
While it is possible to distribute unsigned apps, it is not recommended. Both
|
||||
@@ -16,20 +22,19 @@ Windows and macOS will, by default, prevent either the download or the execution
|
||||
of unsigned applications. Starting with macOS Catalina (version 10.15), users
|
||||
have to go through multiple manual steps to open unsigned applications.
|
||||
|
||||

|
||||

|
||||
|
||||
As you can see, users get two options: Move the app straight to the trash or
|
||||
cancel running it. You don't want your users to see that dialog.
|
||||
|
||||
If you are building an Electron app that you intend to package and distribute,
|
||||
it should be code-signed.
|
||||
it should be code signed.
|
||||
|
||||
# Signing & notarizing macOS builds
|
||||
## Signing & notarizing macOS builds
|
||||
|
||||
Properly preparing macOS applications for release requires two steps: First, the
|
||||
app needs to be code-signed. Then, the app needs to be uploaded to Apple for a
|
||||
process called "notarization", where automated systems will further verify that
|
||||
Properly preparing macOS applications for release requires two steps. First, the
|
||||
app needs to be code signed. Then, the app needs to be uploaded to Apple for a
|
||||
process called **notarization**, where automated systems will further verify that
|
||||
your app isn't doing anything to endanger its users.
|
||||
|
||||
To start the process, ensure that you fulfill the requirements for signing and
|
||||
@@ -42,18 +47,18 @@ notarizing your app:
|
||||
Electron's ecosystem favors configuration and freedom, so there are multiple
|
||||
ways to get your application signed and notarized.
|
||||
|
||||
## `electron-forge`
|
||||
### Using Electron Forge
|
||||
|
||||
If you're using Electron's favorite build tool, getting your application signed
|
||||
and notarized requires a few additions to your configuration. [Forge](https://electronforge.io) is a
|
||||
collection of the official Electron tools, using [`electron-packager`],
|
||||
[`electron-osx-sign`], and [`electron-notarize`] under the hood.
|
||||
|
||||
Let's take a look at an example configuration with all required fields. Not all
|
||||
of them are required: the tools will be clever enough to automatically find a
|
||||
suitable `identity`, for instance, but we recommend that you are explicit.
|
||||
Let's take a look at an example `package.json` configuration with all required fields. Not all of them are
|
||||
required: the tools will be clever enough to automatically find a suitable `identity`, for instance,
|
||||
but we recommend that you are explicit.
|
||||
|
||||
```json
|
||||
```json title="package.json" {7}
|
||||
{
|
||||
"name": "my-app",
|
||||
"version": "0.0.1",
|
||||
@@ -69,7 +74,7 @@ suitable `identity`, for instance, but we recommend that you are explicit.
|
||||
},
|
||||
"osxNotarize": {
|
||||
"appleId": "felix@felix.fun",
|
||||
"appleIdPassword": "my-apple-id-password",
|
||||
"appleIdPassword": "my-apple-id-password"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,11 +82,11 @@ suitable `identity`, for instance, but we recommend that you are explicit.
|
||||
}
|
||||
```
|
||||
|
||||
The `plist` file referenced here needs the following macOS-specific entitlements
|
||||
The `entitlements.plist` file referenced here needs the following macOS-specific entitlements
|
||||
to assure the Apple security mechanisms that your app is doing these things
|
||||
without meaning any harm:
|
||||
|
||||
```xml
|
||||
```xml title="entitlements.plist"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
@@ -104,7 +109,7 @@ file](https://github.com/electron/fiddle/blob/master/forge.config.js).
|
||||
If you plan to access the microphone or camera within your app using Electron's APIs, you'll also
|
||||
need to add the following entitlements:
|
||||
|
||||
```xml
|
||||
```xml title="entitlements.plist"
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
@@ -113,28 +118,26 @@ need to add the following entitlements:
|
||||
|
||||
If these are not present in your app's entitlements when you invoke, for example:
|
||||
|
||||
```js
|
||||
```js title="main.js"
|
||||
const { systemPreferences } = require('electron')
|
||||
|
||||
const microphone = systemPreferences.askForMediaAccess('microphone')
|
||||
```
|
||||
|
||||
Your app may crash. See the Resource Access section in [Hardened Runtime](https://developer.apple.com/documentation/security/hardened_runtime) for more information and entitlements you may need.
|
||||
|
||||
## `electron-builder`
|
||||
### Using Electron Builder
|
||||
|
||||
Electron Builder comes with a custom solution for signing your application. You
|
||||
can find [its documentation here](https://www.electron.build/code-signing).
|
||||
|
||||
## `electron-packager`
|
||||
### Using Electron Packager
|
||||
|
||||
If you're not using an integrated build pipeline like Forge or Builder, you
|
||||
are likely using [`electron-packager`], which includes [`electron-osx-sign`] and
|
||||
[`electron-notarize`].
|
||||
|
||||
If you're using Packager's API, you can pass [in configuration that both signs
|
||||
and notarizes your
|
||||
application](https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html).
|
||||
and notarizes your application](https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html).
|
||||
|
||||
```js
|
||||
const packager = require('electron-packager')
|
||||
@@ -155,11 +158,11 @@ packager({
|
||||
})
|
||||
```
|
||||
|
||||
The `plist` file referenced here needs the following macOS-specific entitlements
|
||||
The `entitlements.plist` file referenced here needs the following macOS-specific entitlements
|
||||
to assure the Apple security mechanisms that your app is doing these things
|
||||
without meaning any harm:
|
||||
|
||||
```xml
|
||||
```xml title="entitlements.plist"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
@@ -175,11 +178,11 @@ without meaning any harm:
|
||||
Up until Electron 12, the `com.apple.security.cs.allow-unsigned-executable-memory` entitlement was required
|
||||
as well. However, it should not be used anymore if it can be avoided.
|
||||
|
||||
## Mac App Store
|
||||
### Signing Mac App Store applications
|
||||
|
||||
See the [Mac App Store Guide].
|
||||
|
||||
# Signing Windows builds
|
||||
## Signing Windows builds
|
||||
|
||||
Before signing Windows builds, you must do the following:
|
||||
|
||||
@@ -190,31 +193,140 @@ Before signing Windows builds, you must do the following:
|
||||
You can get a code signing certificate from a lot of resellers. Prices vary, so
|
||||
it may be worth your time to shop around. Popular resellers include:
|
||||
|
||||
* [digicert](https://www.digicert.com/code-signing/microsoft-authenticode.htm)
|
||||
* [Sectigo](https://sectigo.com/ssl-certificates-tls/code-signing)
|
||||
* Amongst others, please shop around to find one that suits your needs, Google
|
||||
is your friend 😄
|
||||
- [digicert](https://www.digicert.com/code-signing/microsoft-authenticode.htm)
|
||||
- [Sectigo](https://sectigo.com/ssl-certificates-tls/code-signing)
|
||||
- Amongst others, please shop around to find one that suits your needs! 😄
|
||||
|
||||
There are a number of tools for signing your packaged app:
|
||||
:::caution Keep your certificate password private
|
||||
Your certificate password should be a **secret**. Do not share it publicly or
|
||||
commit it to your source code.
|
||||
:::
|
||||
|
||||
* [`electron-winstaller`] will generate an installer for windows and sign it for
|
||||
you
|
||||
* [`electron-forge`] can sign installers it generates through the
|
||||
Squirrel.Windows or MSI targets.
|
||||
* [`electron-builder`] can sign some of its windows targets
|
||||
### Using Electron Forge
|
||||
|
||||
## Windows Store
|
||||
Once you have a code signing certificate file (`.pfx`), you can sign
|
||||
[Squirrel.Windows][maker-squirrel] and [MSI][maker-msi] installers in Electron Forge
|
||||
with the `certificateFile` and `certificatePassword` fields in their respective
|
||||
configuration objects.
|
||||
|
||||
For example, if you keep your Forge config in your `package.json` file and are
|
||||
creating a Squirrel.Windows installer:
|
||||
|
||||
```json {9-15} title='package.json'
|
||||
{
|
||||
"name": "my-app",
|
||||
"version": "0.0.1",
|
||||
//...
|
||||
"config": {
|
||||
"forge": {
|
||||
"packagerConfig": {},
|
||||
"makers": [
|
||||
{
|
||||
"name": "@electron-forge/maker-squirrel",
|
||||
"config": {
|
||||
"certificateFile": "./cert.pfx",
|
||||
"certificatePassword": "this-is-a-secret"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
### Using electron-winstaller (Squirrel.Windows)
|
||||
|
||||
[`electron-winstaller`] is a package that can generate Squirrel.Windows installers for your
|
||||
Electron app. This is the tool used under the hood by Electron Forge's
|
||||
[Squirrel.Windows Maker][maker-squirrel]. If you're not using Electron Forge and want to use
|
||||
`electron-winstaller` directly, use the `certificateFile` and `certificatePassword` configuration
|
||||
options when creating your installer.
|
||||
|
||||
```js {10-11}
|
||||
const electronInstaller = require('electron-winstaller')
|
||||
// NB: Use this syntax within an async function, Node does not have support for
|
||||
// top-level await as of Node 12.
|
||||
try {
|
||||
await electronInstaller.createWindowsInstaller({
|
||||
appDirectory: '/tmp/build/my-app-64',
|
||||
outputDirectory: '/tmp/build/installer64',
|
||||
authors: 'My App Inc.',
|
||||
exe: 'myapp.exe',
|
||||
certificateFile: './cert.pfx',
|
||||
certificatePassword: 'this-is-a-secret',
|
||||
})
|
||||
console.log('It worked!')
|
||||
} catch (e) {
|
||||
console.log(`No dice: ${e.message}`)
|
||||
}
|
||||
```
|
||||
|
||||
For full configuration options, check out the [`electron-winstaller`] repository!
|
||||
|
||||
### Using electron-wix-msi (WiX MSI)
|
||||
|
||||
[`electron-wix-msi`] is a package that can generate MSI installers for your
|
||||
Electron app. This is the tool used under the hood by Electron Forge's [MSI Maker][maker-msi].
|
||||
|
||||
If you're not using Electron Forge and want to use `electron-wix-msi` directly, use the
|
||||
`certificateFile` and `certificatePassword` configuration options
|
||||
or pass in parameters directly to [SignTool.exe] with the `signWithParams` option.
|
||||
|
||||
```js {12-13}
|
||||
import { MSICreator } from 'electron-wix-msi'
|
||||
|
||||
// Step 1: Instantiate the MSICreator
|
||||
const msiCreator = new MSICreator({
|
||||
appDirectory: '/path/to/built/app',
|
||||
description: 'My amazing Kitten simulator',
|
||||
exe: 'kittens',
|
||||
name: 'Kittens',
|
||||
manufacturer: 'Kitten Technologies',
|
||||
version: '1.1.2',
|
||||
outputDirectory: '/path/to/output/folder',
|
||||
certificateFile: './cert.pfx',
|
||||
certificatePassword: 'this-is-a-secret',
|
||||
})
|
||||
|
||||
// Step 2: Create a .wxs template file
|
||||
const supportBinaries = await msiCreator.create()
|
||||
|
||||
// 🆕 Step 2a: optionally sign support binaries if you
|
||||
// sign you binaries as part of of your packaging script
|
||||
supportBinaries.forEach(async (binary) => {
|
||||
// Binaries are the new stub executable and optionally
|
||||
// the Squirrel auto updater.
|
||||
await signFile(binary)
|
||||
})
|
||||
|
||||
// Step 3: Compile the template to a .msi file
|
||||
await msiCreator.compile()
|
||||
```
|
||||
|
||||
For full configuration options, check out the [`electron-wix-msi`] repository!
|
||||
|
||||
### Using Electron Builder
|
||||
|
||||
Electron Builder comes with a custom solution for signing your application. You
|
||||
can find [its documentation here](https://www.electron.build/code-signing).
|
||||
|
||||
### Signing Windows Store applications
|
||||
|
||||
See the [Windows Store Guide].
|
||||
|
||||
[Apple Developer Program]: https://developer.apple.com/programs/
|
||||
[apple developer program]: https://developer.apple.com/programs/
|
||||
[`electron-builder`]: https://github.com/electron-userland/electron-builder
|
||||
[`electron-forge`]: https://github.com/electron-userland/electron-forge
|
||||
[`electron-osx-sign`]: https://github.com/electron-userland/electron-osx-sign
|
||||
[`electron-packager`]: https://github.com/electron/electron-packager
|
||||
[`electron-notarize`]: https://github.com/electron/electron-notarize
|
||||
[`electron-winstaller`]: https://github.com/electron/windows-installer
|
||||
[Xcode]: https://developer.apple.com/xcode
|
||||
[`electron-wix-msi`]: https://github.com/felixrieseberg/electron-wix-msi
|
||||
[xcode]: https://developer.apple.com/xcode
|
||||
[signing certificates]: https://github.com/electron/electron-osx-sign/wiki/1.-Getting-Started#certificates
|
||||
[Mac App Store Guide]: mac-app-store-submission-guide.md
|
||||
[Windows Store Guide]: windows-store-guide.md
|
||||
[mac app store guide]: ./mac-app-store-submission-guide.md
|
||||
[windows store guide]: ./windows-store-guide.md
|
||||
[maker-squirrel]: https://www.electronforge.io/config/makers/squirrel.windows
|
||||
[maker-msi]: https://www.electronforge.io/config/makers/wix-msi
|
||||
[signtool.exe]: https://docs.microsoft.com/en-us/dotnet/framework/tools/signtool-exe
|
||||
|
||||
54
docs/tutorial/distribution-overview.md
Normal file
54
docs/tutorial/distribution-overview.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: 'Distribution Overview'
|
||||
description: 'To distribute your app with Electron, you need to package and rebrand it. To do this, you can either use specialized tooling or manual approaches.'
|
||||
slug: distribution-overview
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
Once your app is ready for production, there are a couple steps you need to take before
|
||||
you can deliver it to your users.
|
||||
|
||||
## Packaging
|
||||
|
||||
To distribute your app with Electron, you need to package all your resources and assets
|
||||
into an executable and rebrand it. To do this, you can either use specialized tooling
|
||||
or do it manually. See the [Application Packaging][application-packaging] tutorial
|
||||
for more information.
|
||||
|
||||
## Code signing
|
||||
|
||||
Code signing is a security technology that you use to certify that an app was
|
||||
created by you. You should sign your application so it does not trigger the
|
||||
security checks of your user's operating system.
|
||||
|
||||
To get started with each operating system's code signing process, please read the
|
||||
[Code Signing][code-signing] docs.
|
||||
|
||||
## Publishing
|
||||
|
||||
Once your app is packaged and signed, you can freely distribute your app directly
|
||||
to users by uploading your installers online.
|
||||
|
||||
To reach more users, you can also choose to upload your app to each operating system's
|
||||
digital distribution platform (i.e. app store). These require another build step aside
|
||||
from your direct download app. For more information, check out each individual app store guide:
|
||||
|
||||
- [Mac App Store][mac-app]
|
||||
- [Windows Store][windows-store]
|
||||
- [Snapcraft (Linux)][snapcraft]
|
||||
|
||||
## Updating
|
||||
|
||||
Electron's auto-updater allows you to deliver application updates to users
|
||||
without forcing them to manually download new versions of your application.
|
||||
Check out the [Updating Applications][updates] guide for details on implementing automatic updates
|
||||
with Electron.
|
||||
|
||||
<!-- Link labels -->
|
||||
|
||||
[application-packaging]: ./application-distribution.md
|
||||
[code-signing]: ./code-signing.md
|
||||
[mac-app]: ./mac-app-store-submission-guide.md
|
||||
[windows-store]: ./windows-store-guide.md
|
||||
[snapcraft]: ./snapcraft.md
|
||||
[updates]: ./updates.md
|
||||
56
docs/tutorial/examples.md
Normal file
56
docs/tutorial/examples.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: 'Examples Overview'
|
||||
description: 'A set of examples for common Electron features'
|
||||
slug: examples
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
# Examples Overview
|
||||
|
||||
In this section, we have collected a set of guides for common features
|
||||
that you may want to implement in your Electron application. Each guide
|
||||
contains a practical example in a minimal, self-contained example app.
|
||||
The easiest way to run these examples is by downloading [Electron Fiddle][fiddle].
|
||||
|
||||
Once Fiddle is installed, you can press on the "Open in Fiddle" button that you
|
||||
will find below code samples like the following one:
|
||||
|
||||
```fiddle docs/fiddles/quick-start
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const replaceText = (selector, text) => {
|
||||
const element = document.getElementById(selector)
|
||||
if (element) element.innerText = text
|
||||
}
|
||||
|
||||
for (const type of ['chrome', 'node', 'electron']) {
|
||||
replaceText(`${type}-version`, process.versions[type])
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
If there is still something that you do not know how to do, please take a look at the [API][app]
|
||||
as there is a chance it might be documented just there (and also open an issue requesting the
|
||||
guide!).
|
||||
|
||||
<!-- guide-table-start -->
|
||||
|
||||
| Guide | Description |
|
||||
| :-------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Message ports] | This guide provides some examples of how you might use MessagePorts in your app to communicate different processes. |
|
||||
| [Device access] | Learn how to access the device hardware (Bluetooth, USB, Serial). |
|
||||
| [Keyboard shortcuts] | Configure local and global keyboard shortcuts for your Electron application. |
|
||||
| [Multithreading] | With Web Workers, it is possible to run JavaScript in OS-level threads |
|
||||
| [Offscreen rendering] | Offscreen rendering lets you obtain the content of a BrowserWindow in a bitmap, so it can be rendered anywhere. |
|
||||
| [Spellchecker] | Learn how to use the built-in spellchecker, set languages, etc. |
|
||||
| [Web embeds] | Discover the different ways to embed third-party web content in your application. |
|
||||
|
||||
<!-- guide-table-end -->
|
||||
|
||||
## How to...?
|
||||
|
||||
You can find the full list of "How to?" in the sidebar. If there is
|
||||
something that you would like to do that is not documented, please join
|
||||
our [Discord server][] and let us know!
|
||||
|
||||
[discord server]: https://discord.com/invite/electron
|
||||
[fiddle]: https://www.electronjs.org/fiddle
|
||||
@@ -1,10 +1,11 @@
|
||||
# Introduction
|
||||
---
|
||||
title: 'Introduction'
|
||||
description: 'Welcome to the Electron documentation! If this is your first time developing an Electron app, read through this Getting Started section to get familiar with the basics. Otherwise, feel free to explore our guides and API documentation!'
|
||||
slug: /latest/
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
Welcome to the Electron documentation! If this is your first time developing
|
||||
an Electron app, read through this Getting Started section to get familiar with the
|
||||
basics. Otherwise, feel free to explore our guides and API documentation!
|
||||
|
||||
## What is Electron?
|
||||
# What is Electron?
|
||||
|
||||
Electron is a framework for building desktop applications using JavaScript,
|
||||
HTML, and CSS. By embedding [Chromium][chromium] and [Node.js][node] into its
|
||||
@@ -12,20 +13,12 @@ binary, Electron allows you to maintain one JavaScript codebase and create
|
||||
cross-platform apps that work on Windows, macOS, and Linux — no native development
|
||||
experience required.
|
||||
|
||||
## Prerequisites
|
||||
## Getting started
|
||||
|
||||
These docs operate under the assumption that the reader is familiar with both
|
||||
Node.js and general web development. If you need to get more comfortable with
|
||||
either of these areas, we recommend the following resources:
|
||||
|
||||
* [Getting started with the Web (MDN)][mdn-guide]
|
||||
* [Introduction to Node.js][node-guide]
|
||||
|
||||
Moreover, you'll have a better time understanding how Electron works if you get
|
||||
acquainted with Chromium's process model. You can get a brief overview of
|
||||
Chrome architecture with the [Chrome comic][comic], which was released alongside
|
||||
Chrome's launch back in 2008. Although it's been over a decade since then, the
|
||||
core principles introduced in the comic remain helpful to understand Electron.
|
||||
We recommend you to start with the [tutorial], which guides you through the
|
||||
process of developing an Electron app and distributing it to users.
|
||||
The [examples] and [API documentation] are also good places to browse around
|
||||
and discover new things.
|
||||
|
||||
## Running examples with Electron Fiddle
|
||||
|
||||
@@ -39,21 +32,44 @@ a code block. If you have Fiddle installed, this button will open a
|
||||
`fiddle.electronjs.org` link that will automatically load the example into Fiddle,
|
||||
no copy-pasting required.
|
||||
|
||||
```fiddle docs/fiddles/quick-start
|
||||
```
|
||||
|
||||
## What is in the docs?
|
||||
|
||||
All the official documentation is available from the sidebar. These
|
||||
are the different categories and what you can expect on each one:
|
||||
|
||||
- **Tutorial**: An end-to-end guide on how to create and publish your first Electron
|
||||
application.
|
||||
- **Processes in Electron**: In-depth reference on Electron processes and how to work with them.
|
||||
- **Best Practices**: Important checklists to keep in mind when developing an Electron app.
|
||||
- **How-To Examples**: Quick references to add features to your Electron app.
|
||||
- **Development**: Miscellaneous development guides.
|
||||
- **Distribution**: Learn how to distribute your app to end users.
|
||||
- **Testing and debugging**: How to debug JavaScript, write tests, and other tools used
|
||||
to create quality Electron applications.
|
||||
- **Resources**: Useful links to better understand how the Electron project works
|
||||
and is organized.
|
||||
- **Contributing to Electron**: Compiling Electron and making contributions can be daunting.
|
||||
We try to make it easier in this section.
|
||||
|
||||
## Getting help
|
||||
|
||||
Are you getting stuck anywhere? Here are a few links to places to look:
|
||||
|
||||
* If you need help with developing your app, our [community Discord server][discord]
|
||||
is a great place to get advice from other Electron app developers.
|
||||
* If you suspect you're running into a bug with the `electron` package, please check
|
||||
the [GitHub issue tracker][issue-tracker] to see if any existing issues match your
|
||||
problem. If not, feel free to fill out our bug report template and submit a new issue.
|
||||
- If you need help with developing your app, our [community Discord server][discord]
|
||||
is a great place to get advice from other Electron app developers.
|
||||
- If you suspect you're running into a bug with the `electron` package, please check
|
||||
the [GitHub issue tracker][issue-tracker] to see if any existing issues match your
|
||||
problem. If not, feel free to fill out our bug report template and submit a new issue.
|
||||
|
||||
<!-- Links -->
|
||||
|
||||
[api documentation]: ../api/app.md
|
||||
[chromium]: https://www.chromium.org/
|
||||
[node]: https://nodejs.org/
|
||||
[mdn-guide]: https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web
|
||||
[node-guide]: https://nodejs.dev/learn
|
||||
[comic]: https://www.google.com/googlebooks/chrome/
|
||||
[discord]: https://discord.com/invite/APGC3k5yaH
|
||||
[examples]: examples.md
|
||||
[fiddle]: https://electronjs.org/fiddle
|
||||
[issue-tracker]: https://github.com/electron/electron/issues
|
||||
[discord]: https://discord.gg/electronjs
|
||||
[node]: https://nodejs.org/
|
||||
|
||||
@@ -8,8 +8,7 @@ your app.
|
||||
|
||||
Here is a very brief example of what a MessagePort is and how it works:
|
||||
|
||||
```js
|
||||
// renderer.js ///////////////////////////////////////////////////////////////
|
||||
```js title='renderer.js (Renderer Process)'
|
||||
// MessagePorts are created in pairs. A connected pair of message ports is
|
||||
// called a channel.
|
||||
const channel = new MessageChannel()
|
||||
@@ -28,8 +27,7 @@ port2.postMessage({ answer: 42 })
|
||||
ipcRenderer.postMessage('port', null, [port1])
|
||||
```
|
||||
|
||||
```js
|
||||
// main.js ///////////////////////////////////////////////////////////////////
|
||||
```js title='main.js (Main Process)'
|
||||
// In the main process, we receive the port.
|
||||
ipcMain.on('port', (event) => {
|
||||
// When we receive a MessagePort in the main process, it becomes a
|
||||
@@ -84,14 +82,84 @@ process, you can listen for the `close` event by calling `port.on('close',
|
||||
|
||||
## Example use cases
|
||||
|
||||
### Setting up a MessageChannel between two renderers
|
||||
|
||||
In this example, the main process sets up a MessageChannel, then sends each port
|
||||
to a different renderer. This allows renderers to send messages to each other
|
||||
without needing to use the main process as an in-between.
|
||||
|
||||
```js title='main.js (Main Process)'
|
||||
const { BrowserWindow, app, MessageChannelMain } = require('electron')
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
// create the windows.
|
||||
const mainWindow = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
contextIsolation: false,
|
||||
preload: 'preloadMain.js'
|
||||
}
|
||||
})
|
||||
|
||||
const secondaryWindow = BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
contextIsolation: false,
|
||||
preload: 'preloadSecondary.js'
|
||||
}
|
||||
})
|
||||
|
||||
// set up the channel.
|
||||
const { port1, port2 } = new MessageChannelMain()
|
||||
|
||||
// once the webContents are ready, send a port to each webContents with postMessage.
|
||||
mainWindow.once('ready-to-show', () => {
|
||||
mainWindow.webContents.postMessage('port', null, [port1])
|
||||
})
|
||||
|
||||
secondaryWindow.once('ready-to-show', () => {
|
||||
secondaryWindow.webContents.postMessage('port', null, [port2])
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
Then, in your preload scripts you receive the port through IPC and set up the
|
||||
listeners.
|
||||
|
||||
```js title='preloadMain.js and preloadSecondary.js (Preload scripts)'
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
ipcRenderer.on('port', e => {
|
||||
// port received, make it globally available.
|
||||
window.electronMessagePort = e.ports[0]
|
||||
|
||||
window.electronMessagePort.onmessage = messageEvent => {
|
||||
// handle message
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
In this example messagePort is bound to the `window` object directly. It is better
|
||||
to use `contextIsolation` and set up specific contextBridge calls for each of your
|
||||
expected messages, but for the simplicity of this example we don't. You can find an
|
||||
example of context isolation further down this page at [Communicating directly between the main process and the main world of a context-isolated page](#communicating-directly-between-the-main-process-and-the-main-world-of-a-context-isolated-page)
|
||||
|
||||
That means window.messagePort is globally available and you can call
|
||||
`postMessage` on it from anywhere in your app to send a message to the other
|
||||
renderer.
|
||||
|
||||
```js title='renderer.js (Renderer Process)'
|
||||
// elsewhere in your code to send a message to the other renderers message handler
|
||||
window.electronMessagePort.postmessage('ping')
|
||||
```
|
||||
|
||||
### Worker process
|
||||
|
||||
In this example, your app has a worker process implemented as a hidden window.
|
||||
You want the app page to be able to communicate directly with the worker
|
||||
process, without the performance overhead of relaying via the main process.
|
||||
|
||||
```js
|
||||
// main.js ///////////////////////////////////////////////////////////////////
|
||||
```js title='main.js (Main Process)'
|
||||
const { BrowserWindow, app, ipcMain, MessageChannelMain } = require('electron')
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
@@ -129,8 +197,7 @@ app.whenReady().then(async () => {
|
||||
})
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- worker.html ------------------------------------------------------------>
|
||||
```html title='worker.html'
|
||||
<script>
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
@@ -153,8 +220,7 @@ ipcRenderer.on('new-client', (event) => {
|
||||
</script>
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- app.html --------------------------------------------------------------->
|
||||
```html title='app.html'
|
||||
<script>
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
@@ -182,9 +248,7 @@ Electron's built-in IPC methods only support two modes: fire-and-forget
|
||||
can implement a "response stream", where a single request responds with a
|
||||
stream of data.
|
||||
|
||||
```js
|
||||
// renderer.js ///////////////////////////////////////////////////////////////
|
||||
|
||||
```js title='renderer.js (Renderer Process)'
|
||||
const makeStreamingRequest = (element, callback) => {
|
||||
// MessageChannels are lightweight--it's cheap to create a new one for each
|
||||
// request.
|
||||
@@ -213,9 +277,7 @@ makeStreamingRequest(42, (data) => {
|
||||
// We will see "got response data: 42" 10 times.
|
||||
```
|
||||
|
||||
```js
|
||||
// main.js ///////////////////////////////////////////////////////////////////
|
||||
|
||||
```js title='main.js (Main Process)'
|
||||
ipcMain.on('give-me-a-stream', (event, msg) => {
|
||||
// The renderer has sent us a MessagePort that it wants us to send our
|
||||
// response over.
|
||||
@@ -242,8 +304,7 @@ the renderer are delivered to the isolated world, rather than to the main
|
||||
world. Sometimes you want to deliver messages to the main world directly,
|
||||
without having to step through the isolated world.
|
||||
|
||||
```js
|
||||
// main.js ///////////////////////////////////////////////////////////////////
|
||||
```js title='main.js (Main Process)'
|
||||
const { BrowserWindow, app, MessageChannelMain } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
@@ -278,8 +339,7 @@ app.whenReady().then(async () => {
|
||||
})
|
||||
```
|
||||
|
||||
```js
|
||||
// preload.js ////////////////////////////////////////////////////////////////
|
||||
```js title='preload.js (Preload Script)'
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
// We need to wait until the main world is ready to receive the message before
|
||||
@@ -297,8 +357,7 @@ ipcRenderer.on('main-world-port', async (event) => {
|
||||
})
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- index.html ------------------------------------------------------------->
|
||||
```html title='index.html'
|
||||
<script>
|
||||
window.onmessage = (event) => {
|
||||
// event.source === window means the message is coming from the preload
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
---
|
||||
title: 'Process Model'
|
||||
description: 'Electron inherits its multi-process architecture from Chromium, which makes the framework architecturally very similar to a modern web browser. This guide will expand on the concepts applied in the tutorial.'
|
||||
slug: process-model
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
# Process Model
|
||||
|
||||
Electron inherits its multi-process architecture from Chromium, which makes the framework
|
||||
architecturally very similar to a modern web browser. In this guide, we'll expound on
|
||||
the conceptual knowledge of Electron that we applied in the minimal [quick start app][].
|
||||
architecturally very similar to a modern web browser. This guide will expand on the
|
||||
concepts applied in the [Tutorial][tutorial].
|
||||
|
||||
[quick start app]: ./quick-start.md
|
||||
[tutorial]: ./tutorial-1-prerequisites.md
|
||||
|
||||
## Why not a single process?
|
||||
|
||||
@@ -27,10 +34,10 @@ visualizes this model:
|
||||

|
||||
|
||||
Electron applications are structured very similarly. As an app developer, you control
|
||||
two types of processes: main and renderer. These are analogous to Chrome's own browser
|
||||
and renderer processes outlined above.
|
||||
two types of processes: [main](#the-main-process) and [renderer](#the-renderer-process).
|
||||
These are analogous to Chrome's own browser and renderer processes outlined above.
|
||||
|
||||
[Chrome Comic]: https://www.google.com/googlebooks/chrome/
|
||||
[chrome comic]: https://www.google.com/googlebooks/chrome/
|
||||
|
||||
## The main process
|
||||
|
||||
@@ -68,7 +75,7 @@ When a `BrowserWindow` instance is destroyed, its corresponding renderer process
|
||||
terminated as well.
|
||||
|
||||
[browser-window]: ../api/browser-window.md
|
||||
[web-embed]: ./web-embeds.md
|
||||
[web-embed]: ../tutorial/web-embeds.md
|
||||
[web-contents]: ../api/web-contents.md
|
||||
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
|
||||
|
||||
@@ -90,7 +97,7 @@ app.on('window-all-closed', () => {
|
||||
```
|
||||
|
||||
[app]: ../api/app.md
|
||||
[quick-start-lifecycle]: ./quick-start.md#manage-your-windows-lifecycle
|
||||
[quick-start-lifecycle]: ../tutorial/quick-start.md#manage-your-windows-lifecycle
|
||||
|
||||
### Native APIs
|
||||
|
||||
@@ -105,7 +112,7 @@ For a full list of Electron's main process modules, check out our API documentat
|
||||
|
||||
Each Electron app spawns a separate renderer process for each open `BrowserWindow`
|
||||
(and each web embed). As its name implies, a renderer is responsible for
|
||||
*rendering* web content. For all intents and purposes, code ran in renderer processes
|
||||
_rendering_ web content. For all intents and purposes, code ran in renderer processes
|
||||
should behave according to web standards (insofar as Chromium does, at least).
|
||||
|
||||
Therefore, all user interfaces and app functionality within a single browser
|
||||
@@ -115,18 +122,22 @@ web.
|
||||
Although explaining every web spec is out of scope for this guide, the bare minimum
|
||||
to understand is:
|
||||
|
||||
* An HTML file is your entry point for the renderer process.
|
||||
* UI styling is added through Cascading Style Sheets (CSS).
|
||||
* Executable JavaScript code can be added through `<script>` elements.
|
||||
- An HTML file is your entry point for the renderer process.
|
||||
- UI styling is added through Cascading Style Sheets (CSS).
|
||||
- Executable JavaScript code can be added through `<script>` elements.
|
||||
|
||||
Moreover, this also means that the renderer has no direct access to `require`
|
||||
or other Node.js APIs. In order to directly include NPM modules in the renderer,
|
||||
you must use the same bundler toolchains (for example, `webpack` or `parcel`) that you
|
||||
use on the web.
|
||||
|
||||
> Note: Renderer processes can be spawned with a full Node.js environment for ease of
|
||||
> development. Historically, this used to be the default, but this feature was disabled
|
||||
> for security reasons.
|
||||
:::warning
|
||||
|
||||
Renderer processes can be spawned with a full Node.js environment for ease of
|
||||
development. Historically, this used to be the default, but this feature was disabled
|
||||
for security reasons.
|
||||
|
||||
:::
|
||||
|
||||
At this point, you might be wondering how your renderer process user interfaces
|
||||
can interact with Node.js and Electron's native desktop functionality if these
|
||||
@@ -135,8 +146,9 @@ way to import Electron's content scripts.
|
||||
|
||||
## Preload scripts
|
||||
|
||||
<!-- Note: This guide doesn't take sandboxing into account, which might fundamentally
|
||||
<!-- Note: This guide doesn't take sandboxing into account, which might fundamentally
|
||||
change the statements here. -->
|
||||
|
||||
Preload scripts contain code that executes in a renderer process before its web content
|
||||
begins loading. These scripts run within the renderer context, but are granted more
|
||||
privileges by having access to Node.js APIs.
|
||||
@@ -149,8 +161,8 @@ const { BrowserWindow } = require('electron')
|
||||
//...
|
||||
const win = new BrowserWindow({
|
||||
webPreferences: {
|
||||
preload: 'path/to/preload.js'
|
||||
}
|
||||
preload: 'path/to/preload.js',
|
||||
},
|
||||
})
|
||||
//...
|
||||
```
|
||||
@@ -165,7 +177,7 @@ the [`contextIsolation`][context-isolation] default.
|
||||
|
||||
```js title='preload.js'
|
||||
window.myAPI = {
|
||||
desktop: true
|
||||
desktop: true,
|
||||
}
|
||||
```
|
||||
|
||||
@@ -184,7 +196,7 @@ securely:
|
||||
const { contextBridge } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('myAPI', {
|
||||
desktop: true
|
||||
desktop: true,
|
||||
})
|
||||
```
|
||||
|
||||
@@ -195,14 +207,15 @@ console.log(window.myAPI)
|
||||
|
||||
This feature is incredibly useful for two main purposes:
|
||||
|
||||
* By exposing [`ipcRenderer`][ipcRenderer] helpers to the renderer, you can use
|
||||
- By exposing [`ipcRenderer`][ipcrenderer] helpers to the renderer, you can use
|
||||
inter-process communication (IPC) to trigger main process tasks from the
|
||||
renderer (and vice-versa).
|
||||
* If you're developing an Electron wrapper for an existing web app hosted on a remote
|
||||
- If you're developing an Electron wrapper for an existing web app hosted on a remote
|
||||
URL, you can add custom properties onto the renderer's `window` global that can
|
||||
be used for desktop-only logic on the web client's side.
|
||||
|
||||
[window-mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window
|
||||
[context-isolation]: ./context-isolation.md
|
||||
[context-bridge]: ../api/context-bridge.md
|
||||
[ipcRenderer]: ../api/ipc-renderer.md
|
||||
[ipcrenderer]: ../api/ipc-renderer.md
|
||||
[tutorial]: ./tutorial-1-prerequisites.md
|
||||
|
||||
@@ -131,7 +131,6 @@ folder of your project:
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
@@ -427,7 +426,6 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
143
docs/tutorial/tutorial-1-prerequisites.md
Normal file
143
docs/tutorial/tutorial-1-prerequisites.md
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
title: 'Prerequisites'
|
||||
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.'
|
||||
slug: tutorial-prerequisites
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
:::info Follow along the tutorial
|
||||
|
||||
This is **part 1** of the Electron tutorial.
|
||||
|
||||
1. **[Prerequisites][prerequisites]**
|
||||
1. [Building your First App][building your first app]
|
||||
1. [Using Preload Scripts][preload]
|
||||
1. [Adding Features][features]
|
||||
1. [Packaging Your Application][packaging]
|
||||
1. [Publishing and Updating][updates]
|
||||
|
||||
:::
|
||||
|
||||
Electron is a framework for building desktop applications using JavaScript,
|
||||
HTML, and CSS. By embedding [Chromium][chromium] and [Node.js][node] into a
|
||||
single binary file, Electron allows you to create cross-platform apps that
|
||||
work on Windows, macOS, and Linux with a single JavaScript codebase.
|
||||
|
||||
This tutorial will guide you through the process of developing a desktop
|
||||
application with Electron and distributing it to end users.
|
||||
|
||||
## Assumptions
|
||||
|
||||
Electron is a native wrapper layer for web apps and is run in a Node.js environment.
|
||||
Therefore, this tutorial assumes you are generally familiar with Node and
|
||||
front-end web development basics. If you need to do some background reading before
|
||||
continuing, we recommend the following resources:
|
||||
|
||||
- [Getting started with the Web (MDN Web Docs)][mdn-guide]
|
||||
- [Introduction to Node.js][node-guide]
|
||||
|
||||
## Required tools
|
||||
|
||||
### Code editor
|
||||
|
||||
You will need a text editor to write your code. We recommend using [Visual Studio Code],
|
||||
although you can choose whichever one you prefer.
|
||||
|
||||
### Command line
|
||||
|
||||
Throughout the tutorial, we will ask you to use various command-line interfaces (CLIs). You can
|
||||
type these commands into your system's default terminal:
|
||||
|
||||
- Windows: Command Prompt or PowerShell
|
||||
- macOS: Terminal
|
||||
- Linux: varies depending on distribution (e.g. GNOME Terminal, Konsole)
|
||||
|
||||
Most code editors also come with an integrated terminal, which you can also use.
|
||||
|
||||
### Git and GitHub
|
||||
|
||||
Git is a commonly-used version control system for source code, and GitHub is a collaborative
|
||||
development platform built on top of it. Although neither is strictly necessary to building
|
||||
an Electron application, we will use GitHub releases to set up automatic updates later
|
||||
on in the tutorial. Therefore, we'll require you to:
|
||||
|
||||
- [Create a GitHub account](https://github.com/join)
|
||||
- [Install Git](https://github.com/git-guides/install-git)
|
||||
|
||||
If you're unfamiliar with how Git works, we recommend reading GitHub's [Git guides]. You can also
|
||||
use the [GitHub Desktop] app if you prefer using a visual interface over the command line.
|
||||
|
||||
We recommend that you create a local Git repository and publish it to GitHub before starting
|
||||
the tutorial, and commit your code after every step.
|
||||
|
||||
:::info Installing Git via GitHub Desktop
|
||||
|
||||
GitHub Desktop will install the latest version of Git on your system if you don't already have
|
||||
it installed.
|
||||
|
||||
:::
|
||||
|
||||
### Node.js and npm
|
||||
|
||||
To begin developing an Electron app, you need to install the [Node.js][node-download]
|
||||
runtime and its bundled npm package manager onto your system. We recommend that you
|
||||
use the latest long-term support (LTS) version.
|
||||
|
||||
:::tip
|
||||
|
||||
Please install Node.js using pre-built installers for your platform.
|
||||
You may encounter incompatibility issues with different development tools otherwise.
|
||||
If you are using macOS, we recommend using a package manager like [Homebrew] or
|
||||
[nvm] to avoid any directory permission issues.
|
||||
|
||||
:::
|
||||
|
||||
To check that Node.js was installed correctly, you can use the `-v` flag when
|
||||
running the `node` and `npm` commands. These should print out the installed
|
||||
versions.
|
||||
|
||||
```sh
|
||||
$ node -v
|
||||
v16.14.2
|
||||
$ npm -v
|
||||
8.7.0
|
||||
```
|
||||
|
||||
:::caution
|
||||
|
||||
Although you need Node.js installed locally to scaffold an Electron project,
|
||||
Electron **does not use your system's Node.js installation to run its code**. Instead, it
|
||||
comes bundled with its own Node.js runtime. This means that your end users do not
|
||||
need to install Node.js themselves as a prerequisite to running your app.
|
||||
|
||||
To check which version of Node.js is running in your app, you can access the global
|
||||
[`process.versions`] variable in the main process or preload script. You can also reference
|
||||
the list of versions in the [electron/releases] repository.
|
||||
|
||||
:::
|
||||
|
||||
<!-- Links -->
|
||||
|
||||
[chromium]: https://www.chromium.org/
|
||||
[electron/releases]: https://github.com/electron/releases/blob/master/readme.md#releases
|
||||
[homebrew]: https://brew.sh/
|
||||
[mdn-guide]: https://developer.mozilla.org/en-US/docs/Learn/
|
||||
[node]: https://nodejs.org/
|
||||
[node-guide]: https://nodejs.dev/learn
|
||||
[node-download]: https://nodejs.org/en/download/
|
||||
[nvm]: https://github.com/nvm-sh/nvm
|
||||
[process-model]: ./process-model.md
|
||||
[`process.versions`]: https://nodejs.org/api/process.html#processversions
|
||||
[github]: https://github.com/
|
||||
[git guides]: https://github.com/git-guides/
|
||||
[github desktop]: https://desktop.github.com/
|
||||
[visual studio code]: https://code.visualstudio.com/
|
||||
|
||||
<!-- Tutorial links -->
|
||||
|
||||
[prerequisites]: tutorial-1-prerequisites.md
|
||||
[building your first app]: tutorial-2-first-app.md
|
||||
[preload]: tutorial-3-preload.md
|
||||
[features]: tutorial-4-adding-features.md
|
||||
[packaging]: tutorial-5-packaging.md
|
||||
[updates]: tutorial-6-publishing-updating.md
|
||||
480
docs/tutorial/tutorial-2-first-app.md
Normal file
480
docs/tutorial/tutorial-2-first-app.md
Normal file
@@ -0,0 +1,480 @@
|
||||
---
|
||||
title: 'Building your First App'
|
||||
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.'
|
||||
slug: tutorial-first-app
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
:::info Follow along the tutorial
|
||||
|
||||
This is **part 2** of the Electron tutorial.
|
||||
|
||||
1. [Prerequisites][prerequisites]
|
||||
1. **[Building your First App][building your first app]**
|
||||
1. [Using Preload Scripts][preload]
|
||||
1. [Adding Features][features]
|
||||
1. [Packaging Your Application][packaging]
|
||||
1. [Publishing and Updating][updates]
|
||||
|
||||
:::
|
||||
|
||||
## Learning goals
|
||||
|
||||
In this part of the tutorial, you will learn how to set up your Electron project
|
||||
and write a minimal starter application. By the end of this section,
|
||||
you should be able to run a working Electron app in development mode from
|
||||
your terminal.
|
||||
|
||||
## Setting up your project
|
||||
|
||||
:::caution Avoid WSL
|
||||
|
||||
If you are on a Windows machine, please do not use [Windows Subsystem for Linux][wsl] (WSL)
|
||||
when following this tutorial as you will run into issues when trying to execute the
|
||||
application.
|
||||
|
||||
<!--https://www.electronforge.io/guides/developing-with-wsl-->
|
||||
|
||||
:::
|
||||
|
||||
### Initializing your npm project
|
||||
|
||||
Electron apps are scaffolded using npm, with the package.json file
|
||||
as an entry point. Start by creating a folder and initializing an npm package
|
||||
within it with `npm init`.
|
||||
|
||||
```sh npm2yarn
|
||||
mkdir my-electron-app && cd my-electron-app
|
||||
npm init
|
||||
```
|
||||
|
||||
This command will prompt you to configure some fields in your package.json.
|
||||
There are a few rules to follow for the purposes of this tutorial:
|
||||
|
||||
- _entry point_ should be `main.js` (you will be creating that file soon).
|
||||
- _author_, _license_, and _description_ can be any value, but will be necessary for
|
||||
[packaging][packaging] later on.
|
||||
|
||||
Then, install Electron into your app's **devDependencies**, which is the list of external
|
||||
development-only package dependencies not required in production.
|
||||
|
||||
:::info Why is Electron a devDependency?
|
||||
|
||||
This may seem counter-intuitive since your production code is running Electron APIs.
|
||||
However, packaged apps will come bundled with the Electron binary, eliminating the need to specify
|
||||
it as a production dependency.
|
||||
|
||||
:::
|
||||
|
||||
```sh npm2yarn
|
||||
npm install electron --save-dev
|
||||
```
|
||||
|
||||
Your package.json file should look something like this after initializing your package
|
||||
and installing Electron. You should also now have a `node_modules` folder containing
|
||||
the Electron executable, as well as a `package-lock.json` lockfile that specifies
|
||||
the exact dependency versions to install.
|
||||
|
||||
```json title='package.json'
|
||||
{
|
||||
"name": "my-electron-app",
|
||||
"version": "1.0.0",
|
||||
"description": "Hello World!",
|
||||
"main": "main.js",
|
||||
"author": "Jane Doe",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"electron": "19.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::info Advanced Electron installation steps
|
||||
|
||||
If installing Electron directly fails, please refer to our [Advanced Installation][installation]
|
||||
documentation for instructions on download mirrors, proxies, and troubleshooting steps.
|
||||
|
||||
:::
|
||||
|
||||
### Adding a .gitignore
|
||||
|
||||
The [`.gitignore`][gitignore] file specifies which files and directories to avoid tracking
|
||||
with Git. You should place a copy of [GitHub's Node.js gitignore template][gitignore-template]
|
||||
into your project's root folder to avoid committing your project's `node_modules` folder.
|
||||
|
||||
## Running an Electron app
|
||||
|
||||
:::tip Further reading
|
||||
|
||||
Read [Electron's process model][process-model] documentation to better
|
||||
understand how Electron's multiple processes work together.
|
||||
|
||||
:::
|
||||
|
||||
The [`main`][package-json-main] script you defined in package.json is the entry point of any
|
||||
Electron application. This script controls the **main process**, which runs in a Node.js
|
||||
environment and is responsible for controlling your app's lifecycle, displaying native
|
||||
interfaces, performing privileged operations, and managing renderer processes
|
||||
(more on that later).
|
||||
|
||||
Before creating your first Electron app, you will first use a trivial script to ensure your
|
||||
main process entry point is configured correctly. Create a `main.js` file in the root folder
|
||||
of your project with a single line of code:
|
||||
|
||||
```js title='main.js'
|
||||
console.log(`Hello from Electron 👋`)
|
||||
```
|
||||
|
||||
Because Electron's main process is a Node.js runtime, you can execute arbitrary Node.js code
|
||||
with the `electron` command (you can even use it as a [REPL]). To execute this script,
|
||||
add `electron .` to the `start` command in the [`scripts`][package-scripts]
|
||||
field of your package.json. This command will tell the Electron executable to look for the main
|
||||
script in the current directory and run it in dev mode.
|
||||
|
||||
```json {8-10} title='package.json'
|
||||
{
|
||||
"name": "my-electron-app",
|
||||
"version": "1.0.0",
|
||||
"description": "Hello World!",
|
||||
"main": "main.js",
|
||||
"author": "Jane Doe",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "electron ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^19.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```sh npm2yarn
|
||||
npm run start
|
||||
```
|
||||
|
||||
Your terminal should print out `Hello from Electron 👋`. Congratulations,
|
||||
you have executed your first line of code in Electron! Next, you will learn
|
||||
how to create user interfaces with HTML and load that into a native window.
|
||||
|
||||
## Loading a web page into a BrowserWindow
|
||||
|
||||
In Electron, each window displays a web page that can be loaded either from a local HTML
|
||||
file or a remote web address. For this example, you will be loading in a local file. Start
|
||||
by creating a barebones web page in an `index.html` file in the root folder of your project:
|
||||
|
||||
```html title='index.html'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'"
|
||||
/>
|
||||
<meta
|
||||
http-equiv="X-Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'"
|
||||
/>
|
||||
<title>Hello from Electron renderer!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello from Electron renderer!</h1>
|
||||
<p>👋</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Now that you have a web page, you can load it into an Electron [BrowserWindow][browser-window].
|
||||
Replace the contents your `main.js` file with the following code. We will explain each
|
||||
highlighted block separately.
|
||||
|
||||
```js {1,3-10,12-14} title='main.js' showLineNumbers
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
```
|
||||
|
||||
### Importing modules
|
||||
|
||||
```js title='main.js (Line 1)'
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
```
|
||||
|
||||
In the first line, we are importing two Electron modules
|
||||
with CommonJS module syntax:
|
||||
|
||||
- [app][app], which controls your application's event lifecycle.
|
||||
- [BrowserWindow][browser-window], which creates and manages app windows.
|
||||
|
||||
:::info Capitalization conventions
|
||||
|
||||
You might have noticed the capitalization difference between the **a**pp
|
||||
and **B**rowser**W**indow modules. Electron follows typical JavaScript conventions here,
|
||||
where PascalCase modules are instantiable class constructors (e.g. BrowserWindow, Tray,
|
||||
Notification) whereas camelCase modules are not instantiable (e.g. app, ipcRenderer, webContents).
|
||||
|
||||
:::
|
||||
|
||||
:::warning ES Modules in Electron
|
||||
|
||||
[ECMAScript modules](https://nodejs.org/api/esm.html) (i.e. using `import` to load a module)
|
||||
are currently not directly supported in Electron. You can find more information about the
|
||||
state of ESM in Electron in [electron/electron#21457](https://github.com/electron/electron/issues/21457).
|
||||
|
||||
:::
|
||||
|
||||
### Writing a reusable function to instantiate windows
|
||||
|
||||
The `createWindow()` function loads your web page into a new BrowserWindow instance:
|
||||
|
||||
```js title='main.js (Lines 3-10)'
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
}
|
||||
```
|
||||
|
||||
### Calling your function when the app is ready
|
||||
|
||||
```js title='main.js (Lines 12-14)'
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
```
|
||||
|
||||
Many of Electron's core modules are Node.js [event emitters] that adhere to Node's asynchronous
|
||||
event-driven architecture. The app module is one of these emitters.
|
||||
|
||||
In Electron, BrowserWindows can only be created after the app module's [`ready`][app-ready] event
|
||||
is fired. You can wait for this event by using the [`app.whenReady()`][app-when-ready] API and
|
||||
calling `createWindow()` once its promise is fulfilled.
|
||||
|
||||
:::info
|
||||
|
||||
You typically listen to Node.js events by using an emitter's `.on` function.
|
||||
|
||||
```diff
|
||||
+ app.on('ready').then(() => {
|
||||
- app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
```
|
||||
|
||||
However, Electron exposes `app.whenReady()` as a helper specifically for the `ready` event to
|
||||
avoid subtle pitfalls with directly listening to that event in particular.
|
||||
See [electron/electron#21972](https://github.com/electron/electron/pull/21972) for details.
|
||||
|
||||
:::
|
||||
|
||||
At this point, running your Electron application's `start` command should successfully
|
||||
open a window that displays your web page!
|
||||
|
||||
Each web page your app displays in a window will run in a separate process called a
|
||||
**renderer** process (or simply _renderer_ for short). Renderer processes have access
|
||||
to the same JavaScript APIs and tooling you use for typical front-end web
|
||||
development, such as using [webpack] to bundle and minify your code or [React][react]
|
||||
to build your user interfaces.
|
||||
|
||||
## Managing your app's window lifecycle
|
||||
|
||||
Application windows behave differently on each operating system. Rather than
|
||||
enforce these conventions by default, Electron gives you the choice to implement
|
||||
them in your app code if you wish to follow them. You can implement basic window
|
||||
conventions by listening for events emitted by the app and BrowserWindow modules.
|
||||
|
||||
:::tip Process-specific control flow
|
||||
|
||||
Checking against Node's [`process.platform`][node-platform] variable can help you
|
||||
to run code conditionally on certain platforms. Note that there are only three
|
||||
possible platforms that Electron can run in: `win32` (Windows), `linux` (Linux),
|
||||
and `darwin` (macOS).
|
||||
|
||||
:::
|
||||
|
||||
### Quit the app when all windows are closed (Windows & Linux)
|
||||
|
||||
On Windows and Linux, closing all windows will generally quit an application entirely.
|
||||
To implement this pattern in your Electron app, listen for the app module's
|
||||
[`window-all-closed`][window-all-closed] event, and call [`app.quit()`][app-quit]
|
||||
to exit your app if the user is not on macOS.
|
||||
|
||||
```js
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
```
|
||||
|
||||
### Open a window if none are open (macOS)
|
||||
|
||||
In contrast, macOS apps generally continue running even without any windows open.
|
||||
Activating the app when no windows are available should open a new one.
|
||||
|
||||
To implement this feature, listen for the app module's [`activate`][activate]
|
||||
event, and call your existing `createWindow()` method if no BrowserWindows are open.
|
||||
|
||||
Because windows cannot be created before the `ready` event, you should only listen for
|
||||
`activate` events after your app is initialized. Do this by only listening for activate
|
||||
events inside your existing `whenReady()` callback.
|
||||
|
||||
```js
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on('activate', () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Final starter code
|
||||
|
||||
```fiddle docs/fiddles/tutorial-first-app
|
||||
|
||||
```
|
||||
|
||||
## Optional: Debugging from VS Code
|
||||
|
||||
If you want to debug your application using VS Code, you have need attach VS Code to
|
||||
both the main and renderer processes. Here is a sample configuration for you to
|
||||
run. Create a launch.json configuration in a new `.vscode` folder in your project:
|
||||
|
||||
```json title='.vscode/launch.json'
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Main + renderer",
|
||||
"configurations": ["Main", "Renderer"],
|
||||
"stopAll": true
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Renderer",
|
||||
"port": 9222,
|
||||
"request": "attach",
|
||||
"type": "pwa-chrome",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Main",
|
||||
"type": "pwa-node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
|
||||
},
|
||||
"args": [".", "--remote-debugging-port=9222"],
|
||||
"outputCapture": "std",
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The "Main + renderer" option will appear when you select "Run and Debug"
|
||||
from the sidebar, allowing you to set breakpoints and inspect all the variables among
|
||||
other things in both the main and renderer processes.
|
||||
|
||||
What we have done in the `launch.json` file is to create 3 configurations:
|
||||
|
||||
- `Main` is used to start the main process and also expose port 9222 for remote debugging
|
||||
(`--remote-debugging-port=9222`). This is the port that we will use to attach the debugger
|
||||
for the `Renderer`. Because the main process is a Node.js process, the type is set to
|
||||
`pwa-node` (`pwa-` is the prefix that tells VS Code to use the latest JavaScript debugger).
|
||||
- `Renderer` is used to debug the renderer process. Because the main process is the one
|
||||
that creates the process, we have to "attach" to it (`"request": "attach"`) instead of
|
||||
creating a new one.
|
||||
The renderer process is a web one, so the debugger we have to use is `pwa-chrome`.
|
||||
- `Main + renderer` is a [compound task] that executes the previous ones simultaneously.
|
||||
|
||||
:::caution
|
||||
|
||||
Because we are attaching to a process in `Renderer`, it is possible that the first lines of
|
||||
your code will be skipped as the debugger will not have had enough time to connect before they are
|
||||
being executed.
|
||||
You can work around this by refreshing the page or setting a timeout before executing the code
|
||||
in development mode.
|
||||
|
||||
:::
|
||||
|
||||
:::info Further reading
|
||||
|
||||
If you want to dig deeper in the debugging area, the following guides provide more information:
|
||||
|
||||
- [Application Debugging]
|
||||
- [DevTools Extensions][devtools extension]
|
||||
|
||||
:::
|
||||
|
||||
## Summary
|
||||
|
||||
Electron applications are set up using npm packages. The Electron executable should be installed
|
||||
in your project's `devDependencies` and can be run in development mode using a script in your
|
||||
package.json file.
|
||||
|
||||
The executable runs the JavaScript entry point found in the `main` property of your package.json.
|
||||
This file controls Electron's **main process**, which runs an instance of Node.js and is
|
||||
responsible for your app's lifecycle, displaying native interfaces, performing privileged operations,
|
||||
and managing renderer processes.
|
||||
|
||||
**Renderer processes** (or renderers for short) are responsible for display graphical content. You can
|
||||
load a web page into a renderer by pointing it to either a web address or a local HTML file.
|
||||
Renderers behave very similarly to regular web pages and have access to the same web APIs.
|
||||
|
||||
In the next section of the tutorial, we will be learning how to augment the renderer process with
|
||||
privileged APIs and how to communicate between processes.
|
||||
|
||||
<!-- Links -->
|
||||
|
||||
[activate]: ../api/app.md#event-activate-macos
|
||||
[advanced-installation]: installation.md
|
||||
[app]: ../api/app.md
|
||||
[app-quit]: ../api/app.md#appquit
|
||||
[app-ready]: ../api/app.md#event-ready
|
||||
[app-when-ready]: ../api/app.md#appwhenready
|
||||
[application debugging]: ./application-debugging.md
|
||||
[browser-window]: ../api/browser-window.md
|
||||
[commonjs]: https://nodejs.org/docs/../api/modules.html#modules_modules_commonjs_modules
|
||||
[compound task]: https://code.visualstudio.com/Docs/editor/tasks#_compound-tasks
|
||||
[devtools extension]: ./devtools-extension.md
|
||||
[event emitters]: https://nodejs.org/api/events.html#events
|
||||
[gitignore]: https://git-scm.com/docs/gitignore
|
||||
[gitignore-template]: https://github.com/github/gitignore/blob/main/Node.gitignore
|
||||
[installation]: ./installation.md
|
||||
[node-platform]: https://nodejs.org/api/process.html#process_process_platform
|
||||
[package-json-main]: https://docs.npmjs.com/cli/v7/configuring-npm/package-json#main
|
||||
[package-scripts]: https://docs.npmjs.com/cli/v7/using-npm/scripts
|
||||
[process-model]: process-model.md
|
||||
[react]: https://reactjs.org
|
||||
[repl]: ./repl.md
|
||||
[sandbox]: ./sandbox.md
|
||||
[webpack]: https://webpack.js.org
|
||||
[window-all-closed]: ../api/app.md#event-window-all-closed
|
||||
[wsl]: https://docs.microsoft.com/en-us/windows/wsl/about#what-is-wsl-2
|
||||
|
||||
<!-- Tutorial links -->
|
||||
|
||||
[prerequisites]: tutorial-1-prerequisites.md
|
||||
[building your first app]: tutorial-2-first-app.md
|
||||
[preload]: tutorial-3-preload.md
|
||||
[features]: tutorial-4-adding-features.md
|
||||
[packaging]: tutorial-5-packaging.md
|
||||
[updates]: tutorial-6-publishing-updating.md
|
||||
271
docs/tutorial/tutorial-3-preload.md
Normal file
271
docs/tutorial/tutorial-3-preload.md
Normal file
@@ -0,0 +1,271 @@
|
||||
---
|
||||
title: 'Using Preload Scripts'
|
||||
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.'
|
||||
slug: tutorial-preload
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
:::info Follow along the tutorial
|
||||
|
||||
This is **part 3** of the Electron tutorial.
|
||||
|
||||
1. [Prerequisites][prerequisites]
|
||||
1. [Building your First App][building your first app]
|
||||
1. **[Using Preload Scripts][preload]**
|
||||
1. [Adding Features][features]
|
||||
1. [Packaging Your Application][packaging]
|
||||
1. [Publishing and Updating][updates]
|
||||
|
||||
:::
|
||||
|
||||
## Learning goals
|
||||
|
||||
In this part of the tutorial, you will learn what a preload script is and how to use one
|
||||
to securely expose privileged APIs into the renderer process. You will also learn how to
|
||||
communicate between main and renderer processes with Electron's inter-process
|
||||
communication (IPC) modules.
|
||||
|
||||
## What is a preload script?
|
||||
|
||||
Electron's main process is a Node.js environment that has full operating system access.
|
||||
On top of [Electron modules][modules], you can also access [Node.js built-ins][node-api],
|
||||
as well as any packages installed via npm. On the other hand, renderer processes run web
|
||||
pages and do not run Node.js by default for security reasons.
|
||||
|
||||
To bridge Electron's different process types together, we will need to use a special script
|
||||
called a **preload**.
|
||||
|
||||
## Augmenting the renderer with a preload script
|
||||
|
||||
A BrowserWindow's preload script runs in a context that has access to both the HTML DOM
|
||||
and a Node.js environment. Preload scripts are injected before a web page loads in the renderer,
|
||||
similar to a Chrome extension's [content scripts][content-script]. To add features to your renderer
|
||||
that require privileged access, you can define [global] objects through the
|
||||
[contextBridge][contextbridge] API.
|
||||
|
||||
To demonstrate this concept, you will create a preload script that exposes your app's
|
||||
versions of Chrome, Node, and Electron into the renderer.
|
||||
|
||||
Add a new `preload.js` script that exposes selected properties of Electron's `process.versions`
|
||||
object to the renderer process in a `versions` global variable.
|
||||
|
||||
```js title="preload.js"
|
||||
const { contextBridge } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('versions', {
|
||||
node: () => process.versions.node,
|
||||
chrome: () => process.versions.chrome,
|
||||
electron: () => process.versions.electron,
|
||||
// we can also expose variables, not just functions
|
||||
})
|
||||
```
|
||||
|
||||
To attach this script to your renderer process, pass its path to the
|
||||
`webPreferences.preload` option in the BrowserWindow constructor:
|
||||
|
||||
```js {8-10} title="main.js"
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
},
|
||||
})
|
||||
|
||||
win.loadFile('index.html')
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
})
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
There are two Node.js concepts that are used here:
|
||||
|
||||
- The [`__dirname`][dirname] string points to the path of the currently executing script
|
||||
(in this case, your project's root folder).
|
||||
- The [`path.join`][path-join] API joins multiple path segments together, creating a
|
||||
combined path string that works across all platforms.
|
||||
|
||||
:::
|
||||
|
||||
At this point, the renderer has access to the `versions` global, so let's display that
|
||||
information in the window. This variable can be accessed via `window.versions` or simply
|
||||
`versions`. Create a `renderer.js` script that uses the [`document.getElementById`]
|
||||
DOM API to replace the displayed text for the HTML element with `info` as its `id` property.
|
||||
|
||||
```js title="renderer.js"
|
||||
const information = document.getElementById('info')
|
||||
information.innerText = `This app is using Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`
|
||||
```
|
||||
|
||||
Then, modify your `index.html` by adding a new element with `info` as its `id` property,
|
||||
and attach your `renderer.js` script:
|
||||
|
||||
```html {18,20} title="index.html"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'"
|
||||
/>
|
||||
<meta
|
||||
http-equiv="X-Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'"
|
||||
/>
|
||||
<title>Hello from Electron renderer!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello from Electron renderer!</h1>
|
||||
<p>👋</p>
|
||||
<p id="info"></p>
|
||||
</body>
|
||||
<script src="./renderer.js"></script>
|
||||
</html>
|
||||
```
|
||||
|
||||
After following the above steps, your app should look something like this:
|
||||
|
||||

|
||||
|
||||
And the code should look like this:
|
||||
|
||||
```fiddle docs/fiddles/tutorial-preload
|
||||
|
||||
```
|
||||
|
||||
## Communicating between processes
|
||||
|
||||
As we have mentioned above, Electron's main and renderer process have distinct responsibilities
|
||||
and are not interchangeable. This means it is not possible to access the Node.js APIs directly
|
||||
from the renderer process, nor the HTML Document Object Model (DOM) from the main process.
|
||||
|
||||
The solution for this problem is to use Electron's `ipcMain` and `ipcRenderer` modules for
|
||||
inter-process communication (IPC). To send a message from your web page to the main process,
|
||||
you can set up a main process handler with `ipcMain.handle` and
|
||||
then expose a function that calls `ipcRenderer.invoke` to trigger the handler in your preload script.
|
||||
|
||||
To illustrate, we will add a global function to the renderer called `ping()`
|
||||
that will return a string from the main process.
|
||||
|
||||
First, set up the `invoke` call in your preload script:
|
||||
|
||||
```js {1,7} title="preload.js"
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('versions', {
|
||||
node: () => process.versions.node,
|
||||
chrome: () => process.versions.chrome,
|
||||
electron: () => process.versions.electron,
|
||||
ping: () => ipcRenderer.invoke('ping'),
|
||||
// we can also expose variables, not just functions
|
||||
})
|
||||
```
|
||||
|
||||
:::caution IPC security
|
||||
|
||||
Notice how we wrap the `ipcRenderer.invoke('ping')` call in a helper function rather
|
||||
than expose the `ipcRenderer` module directly via context bridge. You **never** want to
|
||||
directly expose the entire `ipcRenderer` module via preload. This would give your renderer
|
||||
the ability to send arbitrary IPC messages to the main process, which becomes a powerful
|
||||
attack vector for malicious code.
|
||||
|
||||
:::
|
||||
|
||||
Then, set up your `handle` listener in the main process. We do this _before_
|
||||
loading the HTML file so that the handler is guaranteed to be ready before
|
||||
you send out the `invoke` call from the renderer.
|
||||
|
||||
```js {1,11} title="main.js"
|
||||
const { ipcMain } = require('electron')
|
||||
|
||||
const createWindow = () => {
|
||||
const win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
},
|
||||
})
|
||||
ipcMain.handle('ping', () => 'pong')
|
||||
win.loadFile('index.html')
|
||||
}
|
||||
```
|
||||
|
||||
Once you have the sender and receiver set up, you can now send messages from the renderer
|
||||
to the main process through the `'ping'` channel you just defined.
|
||||
|
||||
```js title='renderer.js'
|
||||
const func = async () => {
|
||||
const response = await window.versions.ping()
|
||||
console.log(response) // prints out 'pong'
|
||||
}
|
||||
|
||||
func()
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
For more in-depth explanations on using the `ipcRenderer` and `ipcMain` modules,
|
||||
check out the full [Inter-Process Communication][ipc] guide.
|
||||
|
||||
:::
|
||||
|
||||
## Summary
|
||||
|
||||
A preload script contains code that runs before your web page is loaded into the browser
|
||||
window. It has access to both DOM APIs and Node.js environment, and is often used to
|
||||
expose privileged APIs to the renderer via the `contextBridge` API.
|
||||
|
||||
Because the main and renderer processes have very different responsibilities, Electron
|
||||
apps often use the preload script to set up inter-process communication (IPC) interfaces
|
||||
to pass arbitrary messages between the two kinds of processes.
|
||||
|
||||
In the next part of the tutorial, we will be showing you resources on adding more
|
||||
functionality to your app, then teaching you distributing your app to users.
|
||||
|
||||
<!-- Links -->
|
||||
|
||||
[advanced-installation]: ./installation.md
|
||||
[application debugging]: ./application-debugging.md
|
||||
[app]: ../api/app.md
|
||||
[app-ready]: ../api/app.md#event-ready
|
||||
[app-when-ready]: ../api/app.md#appwhenready
|
||||
[browser-window]: ../api/browser-window.md
|
||||
[commonjs]: https://nodejs.org/docs/latest/api/modules.html#modules_modules_commonjs_modules
|
||||
[compound task]: https://code.visualstudio.com/Docs/editor/tasks#_compound-tasks
|
||||
[content-script]: https://developer.chrome.com/docs/extensions/mv3/content_scripts/
|
||||
[contextbridge]: ../api/context-bridge.md
|
||||
[context-isolation]: ./context-isolation.md
|
||||
[`document.getelementbyid`]: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById
|
||||
[devtools-extension]: ./devtools-extension.md
|
||||
[dirname]: https://nodejs.org/api/modules.html#modules_dirname
|
||||
[global]: https://developer.mozilla.org/en-US/docs/Glossary/Global_object
|
||||
[ipc]: ./ipc.md
|
||||
[mdn-csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
|
||||
[modules]: ../api/app.md
|
||||
[node-api]: https://nodejs.org/dist/latest/docs/api/
|
||||
[package-json-main]: https://docs.npmjs.com/cli/v7/configuring-npm/package-json#main
|
||||
[package-scripts]: https://docs.npmjs.com/cli/v7/using-npm/scripts
|
||||
[path-join]: https://nodejs.org/api/path.html#path_path_join_paths
|
||||
[process-model]: ./process-model.md
|
||||
[react]: https://reactjs.org
|
||||
[sandbox]: ./sandbox.md
|
||||
[webpack]: https://webpack.js.org
|
||||
|
||||
<!-- Tutorial links -->
|
||||
|
||||
[prerequisites]: tutorial-1-prerequisites.md
|
||||
[building your first app]: tutorial-2-first-app.md
|
||||
[preload]: tutorial-3-preload.md
|
||||
[features]: tutorial-4-adding-features.md
|
||||
[packaging]: tutorial-5-packaging.md
|
||||
[updates]: tutorial-6-publishing-updating.md
|
||||
77
docs/tutorial/tutorial-4-adding-features.md
Normal file
77
docs/tutorial/tutorial-4-adding-features.md
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: 'Adding Features'
|
||||
description: 'In this step of the tutorial, we will share some resources you should read to add features to your application'
|
||||
slug: tutorial-adding-features
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
:::info Follow along the tutorial
|
||||
|
||||
This is **part 4** of the Electron tutorial.
|
||||
|
||||
1. [Prerequisites][prerequisites]
|
||||
1. [Building your First App][building your first app]
|
||||
1. [Using Preload Scripts][preload]
|
||||
1. **[Adding Features][features]**
|
||||
1. [Packaging Your Application][packaging]
|
||||
1. [Publishing and Updating][updates]
|
||||
|
||||
:::
|
||||
|
||||
## Adding application complexity
|
||||
|
||||
If you have been following along, you should have a functional Electron application
|
||||
with a static user interface. From this starting point, you can generally progress
|
||||
in developing your app in two broad directions:
|
||||
|
||||
1. Adding complexity to your renderer process' web app code
|
||||
1. Deeper integrations with the operating system and Node.js
|
||||
|
||||
It is important to understand the distinction between these two broad concepts. For the
|
||||
first point, Electron-specific resources are not necessary. Building a pretty to-do
|
||||
list in Electron is just pointing your Electron BrowserWindow to a pretty
|
||||
to-do list web app. Ultimately, you are building your renderer's UI using the same tools
|
||||
(HTML, CSS, JavaScript) that you would on the web. Therefore, Electron's docs will
|
||||
not go in-depth on how to use standard web tools.
|
||||
|
||||
On the other hand, Electron also provides a rich set of tools that allow
|
||||
you to integrate with the desktop environment, from creating tray icons to adding
|
||||
global shortcuts to displaying native menus. It also gives you all the power of a
|
||||
Node.js environment in the main process. This set of capabilities separates
|
||||
Electron applications from running a website in a browser tab, and are the
|
||||
focus of Electron's documentation.
|
||||
|
||||
## How-to examples
|
||||
|
||||
Electron's documentation has many tutorials to help you with more advanced topics
|
||||
and deeper operating system integrations. To get started, check out the
|
||||
[How-To Examples][how-to] doc.
|
||||
|
||||
:::note Let us know if something is missing!
|
||||
|
||||
If you can't find what you are looking for, please let us know on [GitHub] or in
|
||||
our [Discord server][discord]!
|
||||
|
||||
:::
|
||||
|
||||
## What's next?
|
||||
|
||||
For the rest of the tutorial, we will be shifting away from application code
|
||||
and giving you a look at how you can get your app from your developer machine
|
||||
into end users' hands.
|
||||
|
||||
<!-- Link labels -->
|
||||
|
||||
[discord]: https://discord.com/invite/APGC3k5yaH
|
||||
[github]: https://github.com/electron/electronjs.org-new/issues/new
|
||||
[how to]: ./examples.md
|
||||
[node-platform]: https://nodejs.org/api/process.html#process_process_platform
|
||||
|
||||
<!-- Tutorial links -->
|
||||
|
||||
[prerequisites]: tutorial-1-prerequisites.md
|
||||
[building your first app]: tutorial-2-first-app.md
|
||||
[preload]: tutorial-3-preload.md
|
||||
[features]: tutorial-4-adding-features.md
|
||||
[packaging]: tutorial-5-packaging.md
|
||||
[updates]: tutorial-6-publishing-updating.md
|
||||
225
docs/tutorial/tutorial-5-packaging.md
Normal file
225
docs/tutorial/tutorial-5-packaging.md
Normal file
@@ -0,0 +1,225 @@
|
||||
---
|
||||
title: 'Packaging Your Application'
|
||||
description: 'To distribute your app with Electron, you need to package it and create installers.'
|
||||
slug: tutorial-packaging
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
:::info Follow along the tutorial
|
||||
|
||||
This is **part 5** of the Electron tutorial.
|
||||
|
||||
1. [Prerequisites][prerequisites]
|
||||
1. [Building your First App][building your first app]
|
||||
1. [Using Preload Scripts][preload]
|
||||
1. [Adding Features][features]
|
||||
1. **[Packaging Your Application][packaging]**
|
||||
1. [Publishing and Updating][updates]
|
||||
|
||||
:::
|
||||
|
||||
## Learning goals
|
||||
|
||||
In this part of the tutorial, we'll be going over the basics of packaging and distributing
|
||||
your app with [Electron Forge].
|
||||
|
||||
## Using Electron Forge
|
||||
|
||||
Electron does not have any tooling for packaging and distribution bundled into its core
|
||||
modules. Once you have a working Electron app in dev mode, you need to use
|
||||
additional tooling to create a packaged app you can distribute to your users (also known
|
||||
as a **distributable**). Distributables can be either installers (e.g. MSI on Windows) or
|
||||
portable executable files (e.g. `.app` on macOS).
|
||||
|
||||
Electron Forge is an all-in-one tool that handles the packaging and distribution of Electron
|
||||
apps. Under the hood, it combines a lot of existing Electron tools (e.g. [`electron-packager`],
|
||||
[`@electron/osx-sign`], [`electron-winstaller`], etc.) into a single interface so you do not
|
||||
have to worry about wiring them all together.
|
||||
|
||||
### Importing your project into Forge
|
||||
|
||||
You can install Electron Forge's CLI in your project's `devDependencies` and import your
|
||||
existing project with a handy conversion script.
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev @electron-forge/cli
|
||||
npx electron-forge import
|
||||
```
|
||||
|
||||
Once the conversion script is done, Forge should have added a few scripts
|
||||
to your `package.json` file.
|
||||
|
||||
```json title='package.json'
|
||||
//...
|
||||
"scripts": {
|
||||
"start": "electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make"
|
||||
},
|
||||
//...
|
||||
```
|
||||
|
||||
:::info CLI documentation
|
||||
|
||||
For more information on `make` and other Forge APIs, check out
|
||||
the [Electron Forge CLI documentation].
|
||||
|
||||
:::
|
||||
|
||||
You should also notice that your package.json now has a few more packages installed
|
||||
under your `devDependencies`, and contains an added `config.forge` field with an array
|
||||
of makers configured. **Makers** are Forge plugins that create distributables from
|
||||
your source code. You should see multiple makers in the pre-populated configuration,
|
||||
one for each target platform.
|
||||
|
||||
### Creating a distributable
|
||||
|
||||
To create a distributable, use your project's new `make` script, which runs the
|
||||
`electron-forge make` command.
|
||||
|
||||
```sh npm2yarn
|
||||
npm run make
|
||||
```
|
||||
|
||||
This `make` command contains two steps:
|
||||
|
||||
1. It will first run `electron-forge package` under the hood, which bundles your app
|
||||
code together with the Electron binary. The packaged code is generated into a folder.
|
||||
1. It will then use this packaged app folder to create a separate distributable for each
|
||||
configured maker.
|
||||
|
||||
After the script runs, you should see an `out` folder containing both the distributable
|
||||
and a folder containing the packaged application code.
|
||||
|
||||
```plain title='macOS output example'
|
||||
out/
|
||||
├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
|
||||
├── ...
|
||||
└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app
|
||||
```
|
||||
|
||||
The distributable in the `out/make` folder should be ready to launch! You have now
|
||||
created your first bundled Electron application.
|
||||
|
||||
:::tip Distributable formats
|
||||
|
||||
Electron Forge can be configured to create distributables in different OS-specific formats
|
||||
(e.g. DMG, deb, MSI, etc.). See Forge's [Makers] documentation for all configuration options.
|
||||
|
||||
:::
|
||||
|
||||
:::note Packaging without Electron Forge
|
||||
|
||||
If you want to manually package your code, or if you're just interested understanding the
|
||||
mechanics behind packaging an Electron app, check out the full [Application Packaging]
|
||||
documentation.
|
||||
|
||||
:::
|
||||
|
||||
## Important: signing your code
|
||||
|
||||
In order to distribute desktop applications to end users, we _highly recommended_ for you
|
||||
to **code sign** your Electron app. Code signing is an important part of shipping
|
||||
desktop applications, and is mandatory for the auto-update step in the final part
|
||||
of the tutorial.
|
||||
|
||||
Code signing is a security technology that you use to certify that a desktop app was
|
||||
created by a known source. Windows and macOS have their own OS-specific code signing
|
||||
systems that will make it difficult for users to download or launch unsigned applications.
|
||||
|
||||
If you already have code signing certificates for Windows and macOS, you can set your
|
||||
credentials in your Forge configuration. Otherwise, please refer to the full
|
||||
[Code Signing] documentation to learn how to purchase a certificate and for more information
|
||||
on the desktop app code signing process.
|
||||
|
||||
On macOS, code signing is done at the app packaging level. On Windows, distributable installers
|
||||
are signed instead.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="macos" label="macOS" default>
|
||||
|
||||
```json title='package.json' {6-18}
|
||||
{
|
||||
//...
|
||||
"config": {
|
||||
"forge": {
|
||||
//...
|
||||
"packagerConfig": {
|
||||
"osxSign": {
|
||||
"identity": "Developer ID Application: Felix Rieseberg (LT94ZKYDCJ)",
|
||||
"hardened-runtime": true,
|
||||
"entitlements": "entitlements.plist",
|
||||
"entitlements-inherit": "entitlements.plist",
|
||||
"signature-flags": "library"
|
||||
},
|
||||
"osxNotarize": {
|
||||
"appleId": "felix@felix.fun",
|
||||
"appleIdPassword": "this-is-a-secret"
|
||||
}
|
||||
}
|
||||
//...
|
||||
}
|
||||
}
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="windows" label="Windows">
|
||||
|
||||
```json title='package.json' {6-14}
|
||||
{
|
||||
//...
|
||||
"config": {
|
||||
"forge": {
|
||||
//...
|
||||
"makers": [
|
||||
{
|
||||
"name": "@electron-forge/maker-squirrel",
|
||||
"config": {
|
||||
"certificateFile": "./cert.pfx",
|
||||
"certificatePassword": "this-is-a-secret"
|
||||
}
|
||||
}
|
||||
]
|
||||
//...
|
||||
}
|
||||
}
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Summary
|
||||
|
||||
Electron applications need to be packaged to be distributed to users. In this tutorial,
|
||||
you imported your app into Electron Forge and configured it to package your app and
|
||||
generate installers.
|
||||
|
||||
In order for your application to be trusted by the user's system, you need to digitally
|
||||
certify that the distributable is authentic and untampered by code signing it. Your app
|
||||
can be signed through Forge once you configure it to use your code signing certificate
|
||||
information.
|
||||
|
||||
[`@electron/osx-sign`]: https://github.com/electron/osx-sign
|
||||
[application packaging]: ./application-distribution.md
|
||||
[code signing]: ./code-signing.md
|
||||
[`electron-packager`]: https://github.com/electron/electron-packager
|
||||
[`electron-winstaller`]: https://github.com/electron/windows-installer
|
||||
[electron forge]: https://www.electronforge.io
|
||||
[electron forge cli documentation]: https://www.electronforge.io/cli#commands
|
||||
[makers]: https://www.electronforge.io/config/makers
|
||||
|
||||
<!-- Tutorial links -->
|
||||
|
||||
[prerequisites]: tutorial-1-prerequisites.md
|
||||
[building your first app]: tutorial-2-first-app.md
|
||||
[preload]: tutorial-3-preload.md
|
||||
[features]: tutorial-4-adding-features.md
|
||||
[packaging]: tutorial-5-packaging.md
|
||||
[updates]: tutorial-6-publishing-updating.md
|
||||
251
docs/tutorial/tutorial-6-publishing-updating.md
Normal file
251
docs/tutorial/tutorial-6-publishing-updating.md
Normal file
@@ -0,0 +1,251 @@
|
||||
---
|
||||
title: 'Publishing and Updating'
|
||||
description: "There are several ways to update an Electron application. The easiest and officially supported one is taking advantage of the built-in Squirrel framework and Electron's autoUpdater module."
|
||||
slug: tutorial-publishing-updating
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
:::info Follow along the tutorial
|
||||
|
||||
This is **part 6** of the Electron tutorial.
|
||||
|
||||
1. [Prerequisites][prerequisites]
|
||||
1. [Building your First App][building your first app]
|
||||
1. [Using Preload Scripts][preload]
|
||||
1. [Adding Features][features]
|
||||
1. [Packaging Your Application][packaging]
|
||||
1. **[Publishing and Updating][updates]**
|
||||
|
||||
:::
|
||||
|
||||
## Learning goals
|
||||
|
||||
If you've been following along, this is the last step of the tutorial! In this part,
|
||||
you will publish your app to GitHub releases and integrate automatic updates
|
||||
into your app code.
|
||||
|
||||
## Using update.electronjs.org
|
||||
|
||||
The Electron maintainers provide a free auto-updating service for open-source apps
|
||||
at https://update.electronjs.org. Its requirements are:
|
||||
|
||||
- Your app runs on macOS or Windows
|
||||
- Your app has a public GitHub repository
|
||||
- Builds are published to [GitHub releases]
|
||||
- Builds are [code signed][code-signed]
|
||||
|
||||
At this point, we'll assume that you have already pushed all your
|
||||
code to a public GitHub repository.
|
||||
|
||||
:::info Alternative update services
|
||||
|
||||
If you're using an alternate repository host (e.g. GitLab or Bitbucket) or if
|
||||
you need to keep your code repository private, please refer to our
|
||||
[step-by-step guide][update-server] on hosting your own Electron update server.
|
||||
|
||||
:::
|
||||
|
||||
## Publishing a GitHub release
|
||||
|
||||
Electron Forge has [Publisher] plugins that can automate the distribution
|
||||
of your packaged application to various sources. In this tutorial, we will
|
||||
be using the GitHub Publisher, which will allow us to publish
|
||||
our code to GitHub releases.
|
||||
|
||||
### Generating a personal access token
|
||||
|
||||
Forge cannot publish to any repository on GitHub without permission. You
|
||||
need to pass in an authenticated token that gives Forge access to
|
||||
your GitHub releases. The easiest way to do this is to
|
||||
[create a new personal access token (PAT)][new-pat]
|
||||
with the `public_repo` scope, which gives write access to your public repositories.
|
||||
**Make sure to keep this token a secret.**
|
||||
|
||||
### Setting up the GitHub Publisher
|
||||
|
||||
#### Installing the module
|
||||
|
||||
Forge's [GitHub Publisher] is a plugin that
|
||||
needs to be installed in your project's `devDependencies`:
|
||||
|
||||
```sh npm2yarn
|
||||
npm install --save-dev @electron-forge/publisher-github
|
||||
```
|
||||
|
||||
#### Configuring the publisher in Forge
|
||||
|
||||
Once you have it installed, you need to set it up in your Forge
|
||||
configuration. A full list of options is documented in the Forge's
|
||||
[`PublisherGitHubConfig`] API docs.
|
||||
|
||||
```json title='package.json' {6-16}
|
||||
{
|
||||
//...
|
||||
"config": {
|
||||
"forge": {
|
||||
"publishers": [
|
||||
{
|
||||
"name": "@electron-forge/publisher-github",
|
||||
"config": {
|
||||
"repository": {
|
||||
"owner": "github-user-name",
|
||||
"name": "github-repo-name"
|
||||
},
|
||||
"prerelease": false,
|
||||
"draft": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
:::tip Drafting releases before publishing
|
||||
|
||||
Notice that you have configured Forge to publish your release as a draft.
|
||||
This will allow you to see the release with its generated artifacts
|
||||
without actually publishing it to your end users. You can manually
|
||||
publish your releases via GitHub after writing release notes and
|
||||
double-checking that your distributables work.
|
||||
|
||||
:::
|
||||
|
||||
#### Setting up your authentication token
|
||||
|
||||
You also need to make the Publisher aware of your authentication token.
|
||||
By default, it will use the value stored in the `GITHUB_TOKEN` environment
|
||||
variable.
|
||||
|
||||
### Running the publish command
|
||||
|
||||
Add Forge's [publish command] to your npm scripts.
|
||||
|
||||
```json {6} title='package.json'
|
||||
//...
|
||||
"scripts": {
|
||||
"start": "electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish"
|
||||
},
|
||||
//...
|
||||
```
|
||||
|
||||
This command will run your configured makers and publish the output distributables to a new
|
||||
GitHub release.
|
||||
|
||||
```sh npm2yarn
|
||||
npm run publish
|
||||
```
|
||||
|
||||
By default, this will only publish a single distributable for your host operating system and
|
||||
architecture. You can publish for different architectures by passing in the `--arch` flag to your
|
||||
Forge commands.
|
||||
|
||||
The name of this release will correspond to the `version` field in your project's package.json file.
|
||||
|
||||
:::tip Tagging releases
|
||||
|
||||
Optionally, you can also [tag your releases in Git][git-tag] so that your
|
||||
release is associated with a labeled point in your code history. npm comes
|
||||
with a handy [`npm version`](https://docs.npmjs.com/cli/v8/commands/npm-version)
|
||||
command that can handle the version bumping and tagging for you.
|
||||
|
||||
:::
|
||||
|
||||
#### Bonus: Publishing in GitHub Actions
|
||||
|
||||
Publishing locally can be painful, especially because you can only create distributables
|
||||
for your host operating system (i.e. you can't publish a Window `.exe` file from macOS).
|
||||
|
||||
A solution for this would be to publish your app via automation workflows
|
||||
such as [GitHub Actions], which can run tasks in the
|
||||
cloud on Ubuntu, macOS, and Windows. This is the exact approach taken by [Electron Fiddle].
|
||||
You can refer to Fiddle's [Build and Release pipeline][fiddle-build]
|
||||
and [Forge configuration][fiddle-forge-config]
|
||||
for more details.
|
||||
|
||||
## Instrumenting your updater code
|
||||
|
||||
Now that we have a functional release system via GitHub releases, we now need to tell our
|
||||
Electron app to download an update whenever a new release is out. Electron apps do this
|
||||
via the [autoUpdater] module, which reads from an update server feed to check if a new version
|
||||
is available for download.
|
||||
|
||||
The update.electronjs.org service provides an updater-compatible feed. For example, Electron
|
||||
Fiddle v0.28.0 will check the endpoint at https://update.electronjs.org/electron/fiddle/darwin/v0.28.0
|
||||
to see if a newer GitHub release is available.
|
||||
|
||||
After your release is published to GitHub, the update.electronjs.org service should work
|
||||
for your application. The only step left is to configure the feed with the autoUpdater module.
|
||||
|
||||
To make this process easier, the Electron team maintains the [`update-electron-app`] module,
|
||||
which sets up the autoUpdater boilerplate for update.electronjs.org in one function
|
||||
call — no configuration required. This module will search for the update.electronjs.org
|
||||
feed that matches your project's package.json `"repository"` field.
|
||||
|
||||
First, install the module as a runtime dependency.
|
||||
|
||||
```sh npm2yarn
|
||||
npm install update-electron-app
|
||||
```
|
||||
|
||||
Then, import the module and call it immediately in the main process.
|
||||
|
||||
```js title='main.js'
|
||||
require('update-electron-app')()
|
||||
```
|
||||
|
||||
And that is all it takes! Once your application is packaged, it will update itself for each new
|
||||
GitHub release that you publish.
|
||||
|
||||
## Summary
|
||||
|
||||
In this tutorial, we configured Electron Forge's GitHub Publisher to upload your app's
|
||||
distributables to GitHub releases. Since distributables cannot always be generated
|
||||
between platforms, we recommend setting up your building and publishing flow
|
||||
in a Continuous Integration pipeline if you do not have access to machines.
|
||||
|
||||
Electron applications can self-update by pointing the autoUpdater module to an update server feed.
|
||||
update.electronjs.org is a free update server provided by Electron for open-source applications
|
||||
published on GitHub releases. Configuring your Electron app to use this service is as easy as
|
||||
installing and importing the `update-electron-app` module.
|
||||
|
||||
If your application is not eligible for update.electronjs.org, you should instead deploy your
|
||||
own update server and configure the autoUpdater module yourself.
|
||||
|
||||
:::info 🌟 You're done!
|
||||
|
||||
From here, you have officially completed our tutorial to Electron. Feel free to explore the
|
||||
rest of our docs and happy developing! If you have questions, please stop by our community
|
||||
[Discord server].
|
||||
|
||||
:::
|
||||
|
||||
[autoupdater]: ../api/auto-updater.md
|
||||
[code-signed]: ./code-signing.md
|
||||
[discord server]: https://discord.com/invite/APGC3k5yaH
|
||||
[electron fiddle]: https://electronjs.org/fiddle
|
||||
[fiddle-build]: https://github.com/electron/fiddle/blob/master/.github/workflows/build.yaml
|
||||
[fiddle-forge-config]: https://github.com/electron/fiddle/blob/master/forge.config.js
|
||||
[github actions]: https://github.com/features/actions
|
||||
[github publisher]: https://www.electronforge.io/config/publishers/github
|
||||
[github releases]: https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository
|
||||
[git tag]: https://git-scm.com/book/en/v2/Git-Basics-Tagging
|
||||
[new-pat]: https://github.com/settings/tokens/new
|
||||
[publish command]: https://www.electronforge.io/cli#publish
|
||||
[publisher]: https://www.electronforge.io/config/publishers
|
||||
[`publishergithubconfig`]: https://js.electronforge.io/publisher/github/interfaces/publishergithubconfig
|
||||
[`update-electron-app`]: https://github.com/electron/update-electron-app
|
||||
[update-server]: ./updates.md
|
||||
|
||||
<!-- Tutorial links -->
|
||||
|
||||
[prerequisites]: tutorial-1-prerequisites.md
|
||||
[building your first app]: tutorial-2-first-app.md
|
||||
[preload]: tutorial-3-preload.md
|
||||
[features]: tutorial-4-adding-features.md
|
||||
[packaging]: tutorial-5-packaging.md
|
||||
[updates]: tutorial-6-publishing-updating.md
|
||||
@@ -1,11 +1,16 @@
|
||||
# Updating Applications
|
||||
---
|
||||
title: 'Updating Applications'
|
||||
description: "There are several ways to update an Electron application. The easiest and officially supported one is taking advantage of the built-in Squirrel framework and Electron's autoUpdater module."
|
||||
slug: updates
|
||||
hide_title: false
|
||||
---
|
||||
|
||||
There are several ways to update an Electron application. The easiest and
|
||||
officially supported one is taking advantage of the built-in
|
||||
There are several ways to provide automatic updates to your Electron application.
|
||||
The easiest and officially supported one is taking advantage of the built-in
|
||||
[Squirrel](https://github.com/Squirrel) framework and
|
||||
Electron's [autoUpdater](../api/auto-updater.md) module.
|
||||
|
||||
## Using `update.electronjs.org`
|
||||
## Using update.electronjs.org
|
||||
|
||||
The Electron team maintains [update.electronjs.org], a free and open-source
|
||||
webservice that Electron apps can use to self-update. The service is designed
|
||||
@@ -13,72 +18,77 @@ for Electron apps that meet the following criteria:
|
||||
|
||||
- App runs on macOS or Windows
|
||||
- App has a public GitHub repository
|
||||
- Builds are published to GitHub Releases
|
||||
- Builds are code-signed
|
||||
- Builds are published to [GitHub Releases][gh-releases]
|
||||
- Builds are [code-signed](./code-signing.md)
|
||||
|
||||
The easiest way to use this service is by installing [update-electron-app],
|
||||
a Node.js module preconfigured for use with update.electronjs.org.
|
||||
|
||||
Install the module:
|
||||
Install the module using your Node.js package manager of choice:
|
||||
|
||||
```sh
|
||||
```sh npm2yarn
|
||||
npm install update-electron-app
|
||||
```
|
||||
|
||||
Invoke the updater from your app's main process file:
|
||||
Then, invoke the updater from your app's main process file:
|
||||
|
||||
```js
|
||||
```js title="main.js"
|
||||
require('update-electron-app')()
|
||||
```
|
||||
|
||||
By default, this module will check for updates at app startup, then every ten
|
||||
minutes. When an update is found, it will automatically be downloaded in the background. When the download completes, a dialog is displayed allowing the user
|
||||
to restart the app.
|
||||
minutes. When an update is found, it will automatically be downloaded in the background.
|
||||
When the download completes, a dialog is displayed allowing the user to restart the app.
|
||||
|
||||
If you need to customize your configuration, you can
|
||||
[pass options to `update-electron-app`][update-electron-app]
|
||||
[pass options to update-electron-app][update-electron-app]
|
||||
or
|
||||
[use the update service directly][update.electronjs.org].
|
||||
|
||||
## Deploying an Update Server
|
||||
## Using other update services
|
||||
|
||||
If you're developing a private Electron application, or if you're not
|
||||
publishing releases to GitHub Releases, it may be necessary to run your own
|
||||
update server.
|
||||
|
||||
### Step 1: Deploying an update server
|
||||
|
||||
Depending on your needs, you can choose from one of these:
|
||||
|
||||
- [Hazel][hazel] – Update server for private or open-source apps which can be
|
||||
deployed for free on [Vercel][vercel]. It pulls from [GitHub Releases][gh-releases]
|
||||
and leverages the power of GitHub's CDN.
|
||||
deployed for free on [Vercel][vercel]. It pulls from [GitHub Releases][gh-releases]
|
||||
and leverages the power of GitHub's CDN.
|
||||
- [Nuts][nuts] – Also uses [GitHub Releases][gh-releases], but caches app
|
||||
updates on disk and supports private repositories.
|
||||
updates on disk and supports private repositories.
|
||||
- [electron-release-server][electron-release-server] – Provides a dashboard for
|
||||
handling releases and does not require releases to originate on GitHub.
|
||||
handling releases and does not require releases to originate on GitHub.
|
||||
- [Nucleus][nucleus] – A complete update server for Electron apps maintained by
|
||||
Atlassian. Supports multiple applications and channels; uses a static file store
|
||||
to minify server cost.
|
||||
Atlassian. Supports multiple applications and channels; uses a static file store
|
||||
to minify server cost.
|
||||
|
||||
## Implementing Updates in Your App
|
||||
Once you've deployed your update server, you can instrument your app code to receive and
|
||||
apply the updates with Electron's [autoUpdater] module.
|
||||
|
||||
Once you've deployed your update server, continue with importing the required
|
||||
modules in your code. The following code might vary for different server
|
||||
software, but it works like described when using
|
||||
[Hazel][hazel].
|
||||
### Step 2: Receiving updates in your app
|
||||
|
||||
**Important:** Please ensure that the code below will only be executed in
|
||||
your packaged app, and not in development. You can use
|
||||
[electron-is-dev](https://github.com/sindresorhus/electron-is-dev) to check for
|
||||
the environment.
|
||||
First, import the required modules in your main process code. The following code might
|
||||
vary for different server software, but it works like described when using [Hazel][hazel].
|
||||
|
||||
```javascript
|
||||
:::warning Check your execution environment!
|
||||
|
||||
Please ensure that the code below will only be executed in your packaged app, and not in development.
|
||||
You can use the [app.isPackaged](../api/app.md#appispackaged-readonly) API to check the environment.
|
||||
|
||||
:::
|
||||
|
||||
```javascript title='main.js'
|
||||
const { app, autoUpdater, dialog } = require('electron')
|
||||
```
|
||||
|
||||
Next, construct the URL of the update server and tell
|
||||
Next, construct the URL of the update server feed and tell
|
||||
[autoUpdater](../api/auto-updater.md) about it:
|
||||
|
||||
```javascript
|
||||
```javascript title='main.js'
|
||||
const server = 'https://your-deployment-url.com'
|
||||
const url = `${server}/update/${process.platform}/${app.getVersion()}`
|
||||
|
||||
@@ -87,32 +97,32 @@ autoUpdater.setFeedURL({ url })
|
||||
|
||||
As the final step, check for updates. The example below will check every minute:
|
||||
|
||||
```javascript
|
||||
```javascript title='main.js'
|
||||
setInterval(() => {
|
||||
autoUpdater.checkForUpdates()
|
||||
}, 60000)
|
||||
```
|
||||
|
||||
Once your application is [packaged](../tutorial/application-distribution.md),
|
||||
Once your application is [packaged](./application-distribution.md),
|
||||
it will receive an update for each new
|
||||
[GitHub Release](https://help.github.com/articles/creating-releases/) that you
|
||||
publish.
|
||||
|
||||
## Applying Updates
|
||||
### Step 3: Notifying users when updates are available
|
||||
|
||||
Now that you've configured the basic update mechanism for your application, you
|
||||
need to ensure that the user will get notified when there's an update. This
|
||||
can be achieved using the autoUpdater API
|
||||
[events](../api/auto-updater.md#events):
|
||||
can be achieved using the [autoUpdater API events](../api/auto-updater.md#events):
|
||||
|
||||
```javascript
|
||||
```javascript title="main.js"
|
||||
autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => {
|
||||
const dialogOpts = {
|
||||
type: 'info',
|
||||
buttons: ['Restart', 'Later'],
|
||||
title: 'Application Update',
|
||||
message: process.platform === 'win32' ? releaseNotes : releaseName,
|
||||
detail: 'A new version has been downloaded. Restart the application to apply the updates.'
|
||||
detail:
|
||||
'A new version has been downloaded. Restart the application to apply the updates.',
|
||||
}
|
||||
|
||||
dialog.showMessageBox(dialogOpts).then((returnValue) => {
|
||||
@@ -125,16 +135,22 @@ Also make sure that errors are
|
||||
[being handled](../api/auto-updater.md#event-error). Here's an example
|
||||
for logging them to `stderr`:
|
||||
|
||||
```javascript
|
||||
autoUpdater.on('error', message => {
|
||||
```javascript title="main.js"
|
||||
autoUpdater.on('error', (message) => {
|
||||
console.error('There was a problem updating the application')
|
||||
console.error(message)
|
||||
})
|
||||
```
|
||||
|
||||
## Handling Updates Manually
|
||||
:::info Handling updates manually
|
||||
|
||||
Because the requests made by Auto Update aren't under your direct control, you may find situations that are difficult to handle (such as if the update server is behind authentication). The `url` field does support files, which means that with some effort, you can sidestep the server-communication aspect of the process. [Here's an example of how this could work](https://github.com/electron/electron/issues/5020#issuecomment-477636990).
|
||||
Because the requests made by autoUpdate aren't under your direct control, you may find situations
|
||||
that are difficult to handle (such as if the update server is behind authentication). The `url`
|
||||
field supports the `file://` protocol, which means that with some effort, you can sidestep the
|
||||
server-communication aspect of the process by loading your update from a local directory.
|
||||
[Here's an example of how this could work](https://github.com/electron/electron/issues/5020#issuecomment-477636990).
|
||||
|
||||
:::
|
||||
|
||||
[vercel]: https://vercel.com
|
||||
[hazel]: https://github.com/vercel/hazel
|
||||
|
||||
@@ -41,10 +41,9 @@ as quoted from [MSDN][msdn-jumplist]:
|
||||
> confuse the user who does not expect that portion of the destination list to
|
||||
> change.
|
||||
|
||||

|
||||

|
||||
|
||||
> NOTE: The screenshot above is an example of general tasks of
|
||||
Internet Explorer
|
||||
> NOTE: The screenshot above is an example of general tasks for Microsoft Edge
|
||||
|
||||
Unlike the dock menu in macOS which is a real menu, user tasks in Windows work
|
||||
like application shortcuts. For example, when a user clicks a task, the program
|
||||
@@ -109,7 +108,7 @@ As quoted from [MSDN][msdn-thumbnail]:
|
||||
> For example, Windows Media Player might offer standard media transport controls
|
||||
> such as play, pause, mute, and stop.
|
||||
|
||||

|
||||

|
||||
|
||||
> NOTE: The screenshot above is an example of thumbnail toolbar of Windows
|
||||
Media Player
|
||||
@@ -176,7 +175,7 @@ As quoted from [MSDN][msdn-icon-overlay]:
|
||||
> network status, messenger status, or new mail. The user should not be
|
||||
> presented with constantly changing overlays or animations.
|
||||
|
||||

|
||||

|
||||
|
||||
> NOTE: The screenshot above is an example of overlay on a taskbar button
|
||||
|
||||
|
||||
@@ -178,6 +178,7 @@ template("electron_paks") {
|
||||
"${root_gen_dir}/third_party/blink/public/strings/blink_accessibility_strings_",
|
||||
"${root_gen_dir}/third_party/blink/public/strings/blink_strings_",
|
||||
"${root_gen_dir}/device/bluetooth/strings/bluetooth_strings_",
|
||||
"${root_gen_dir}/extensions/strings/extensions_strings_",
|
||||
"${root_gen_dir}/services/strings/services_strings_",
|
||||
"${root_gen_dir}/ui/strings/app_locale_settings_",
|
||||
"${root_gen_dir}/ui/strings/ax_strings_",
|
||||
@@ -187,6 +188,7 @@ template("electron_paks") {
|
||||
"//chrome/app/resources:platform_locale_settings",
|
||||
"//components/strings:components_strings",
|
||||
"//device/bluetooth/strings",
|
||||
"//extensions/strings",
|
||||
"//services/strings",
|
||||
"//third_party/blink/public/strings",
|
||||
"//third_party/blink/public/strings:accessibility_strings",
|
||||
|
||||
@@ -23,6 +23,7 @@ filenames = {
|
||||
|
||||
lib_sources_linux = [
|
||||
"shell/browser/browser_linux.cc",
|
||||
"shell/browser/electron_browser_main_parts_linux.cc",
|
||||
"shell/browser/lib/power_observer_linux.cc",
|
||||
"shell/browser/lib/power_observer_linux.h",
|
||||
"shell/browser/linux/unity_service.cc",
|
||||
|
||||
@@ -500,6 +500,7 @@ WebContents.prototype._callWindowOpenHandler = function (event: Electron.Event,
|
||||
if (!this._windowOpenHandler) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const response = this._windowOpenHandler(details);
|
||||
|
||||
if (typeof response !== 'object') {
|
||||
@@ -656,7 +657,15 @@ WebContents.prototype._init = function () {
|
||||
postBody,
|
||||
disposition
|
||||
};
|
||||
const options = this._callWindowOpenHandler(event, details);
|
||||
|
||||
let options: ReturnType<typeof this._callWindowOpenHandler>;
|
||||
try {
|
||||
options = this._callWindowOpenHandler(event, details);
|
||||
} catch (err) {
|
||||
event.preventDefault();
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!event.defaultPrevented) {
|
||||
openGuestWindow({
|
||||
event,
|
||||
@@ -684,7 +693,16 @@ WebContents.prototype._init = function () {
|
||||
referrer,
|
||||
postBody
|
||||
};
|
||||
windowOpenOverriddenOptions = this._callWindowOpenHandler(event, details);
|
||||
|
||||
let result: ReturnType<typeof this._callWindowOpenHandler>;
|
||||
try {
|
||||
result = this._callWindowOpenHandler(event, details);
|
||||
} catch (err) {
|
||||
event.preventDefault();
|
||||
throw err;
|
||||
}
|
||||
|
||||
windowOpenOverriddenOptions = result;
|
||||
if (!event.defaultPrevented) {
|
||||
const secureOverrideWebPreferences = windowOpenOverriddenOptions ? {
|
||||
// Allow setting of backgroundColor as a webPreference even though
|
||||
|
||||
@@ -77,6 +77,19 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition
|
||||
...browserWindowOptions
|
||||
});
|
||||
|
||||
if (!guest) {
|
||||
// When we open a new window from a link (via OpenURLFromTab),
|
||||
// the browser process is responsible for initiating navigation
|
||||
// in the new window.
|
||||
window.loadURL(url, {
|
||||
httpReferrer: referrer,
|
||||
...(postData && {
|
||||
postData,
|
||||
extraHeaders: formatPostDataHeaders(postData as Electron.UploadRawData[])
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
handleWindowLifecycleEvents({ embedder, frameName, guest: window });
|
||||
|
||||
embedder.emit('did-create-window', window, { url, frameName, options: browserWindowOptions, disposition, referrer, postData });
|
||||
@@ -235,6 +248,15 @@ export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {
|
||||
};
|
||||
}
|
||||
|
||||
function formatPostDataHeaders (postData: PostData) {
|
||||
if (!postData) return;
|
||||
|
||||
const { contentType, boundary } = parseContentTypeFormat(postData);
|
||||
if (boundary != null) { return `content-type: ${contentType}; boundary=${boundary}`; }
|
||||
|
||||
return `content-type: ${contentType}`;
|
||||
}
|
||||
|
||||
const MULTIPART_CONTENT_TYPE = 'multipart/form-data';
|
||||
const URL_ENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded';
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { webFrame } from 'electron';
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
const { mainFrame: webFrame } = process._linkedBinding('electron_renderer_web_frame');
|
||||
|
||||
let shouldLog: boolean | null = null;
|
||||
|
||||
const { platform, execPath, env } = process;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "18.2.4",
|
||||
"version": "18.3.9",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
4
patches/angle/.patches
Normal file
4
patches/angle/.patches
Normal file
@@ -0,0 +1,4 @@
|
||||
cherry-pick-9768648fffc9.patch
|
||||
cherry-pick-801b904aea7d.patch
|
||||
cherry-pick-03aa5ae75c29.patch
|
||||
cherry-pick-6661eb4900da.patch
|
||||
76
patches/angle/cherry-pick-03aa5ae75c29.patch
Normal file
76
patches/angle/cherry-pick-03aa5ae75c29.patch
Normal file
@@ -0,0 +1,76 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Geoff Lang <geofflang@google.com>
|
||||
Date: Wed, 1 Jun 2022 11:22:42 -0400
|
||||
Subject: M102: Ignore eglBind/ReleaseTexImage calls for lost contexts.
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
eglBindTexImage and eglReleaseTexImage no-op when no context is
|
||||
current. Extend this to lost contexts to match the behaviour of making
|
||||
a GL call on a lost context.
|
||||
|
||||
This avoids potential unexpected bad accesses in the backends.
|
||||
|
||||
Bug: chromium:1316578
|
||||
Change-Id: I7b309c297e0c803019720733dee2950abb4c4b5f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3683869
|
||||
Reviewed-by: Jamie Madill <jmadill@chromium.org>
|
||||
Reviewed-by: Alexis Hétu <sugoi@google.com>
|
||||
Reviewed-by: Alexis Hétu <sugoi@chromium.org>
|
||||
Commit-Queue: Geoff Lang <geofflang@chromium.org>
|
||||
(cherry picked from commit bfab7e60a15dc6f72e34406d3f2a3996cd8d0be2)
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3691180
|
||||
|
||||
diff --git a/src/libANGLE/validationEGL.cpp b/src/libANGLE/validationEGL.cpp
|
||||
index 7e4b3adb8db7ea32c98228155e0a702796f0da4c..4309fd84e74fbb4a21edfb9b90bfc1fcda5a9d4a 100644
|
||||
--- a/src/libANGLE/validationEGL.cpp
|
||||
+++ b/src/libANGLE/validationEGL.cpp
|
||||
@@ -4863,7 +4863,7 @@ bool ValidateBindTexImage(const ValidationContext *val,
|
||||
}
|
||||
|
||||
gl::Context *context = val->eglThread->getContext();
|
||||
- if (context)
|
||||
+ if (context && !context->isContextLost())
|
||||
{
|
||||
gl::TextureType type = egl_gl::EGLTextureTargetToTextureType(surface->getTextureTarget());
|
||||
gl::Texture *textureObject = context->getTextureByType(type);
|
||||
diff --git a/src/libGLESv2/egl_stubs.cpp b/src/libGLESv2/egl_stubs.cpp
|
||||
index 0554b7f40c65d6a2690380fe7483818886e20533..645f53ba038e3a5ad580eead0d9135cd274c57f8 100644
|
||||
--- a/src/libGLESv2/egl_stubs.cpp
|
||||
+++ b/src/libGLESv2/egl_stubs.cpp
|
||||
@@ -61,7 +61,7 @@ EGLBoolean BindTexImage(Thread *thread, Display *display, Surface *eglSurface, E
|
||||
GetDisplayIfValid(display), EGL_FALSE);
|
||||
|
||||
gl::Context *context = thread->getContext();
|
||||
- if (context)
|
||||
+ if (context && !context->isContextLost())
|
||||
{
|
||||
gl::TextureType type =
|
||||
egl_gl::EGLTextureTargetToTextureType(eglSurface->getTextureTarget());
|
||||
@@ -573,15 +573,18 @@ EGLBoolean ReleaseTexImage(Thread *thread, Display *display, Surface *eglSurface
|
||||
{
|
||||
ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglReleaseTexImage",
|
||||
GetDisplayIfValid(display), EGL_FALSE);
|
||||
- gl::Texture *texture = eglSurface->getBoundTexture();
|
||||
-
|
||||
- if (texture)
|
||||
+ gl::Context *context = thread->getContext();
|
||||
+ if (context && !context->isContextLost())
|
||||
{
|
||||
- ANGLE_EGL_TRY_RETURN(thread, eglSurface->releaseTexImage(thread->getContext(), buffer),
|
||||
- "eglReleaseTexImage", GetSurfaceIfValid(display, eglSurface),
|
||||
- EGL_FALSE);
|
||||
- }
|
||||
+ gl::Texture *texture = eglSurface->getBoundTexture();
|
||||
|
||||
+ if (texture)
|
||||
+ {
|
||||
+ ANGLE_EGL_TRY_RETURN(thread, eglSurface->releaseTexImage(thread->getContext(), buffer),
|
||||
+ "eglReleaseTexImage", GetSurfaceIfValid(display, eglSurface),
|
||||
+ EGL_FALSE);
|
||||
+ }
|
||||
+ }
|
||||
thread->setSuccess();
|
||||
return EGL_TRUE;
|
||||
}
|
||||
27
patches/angle/cherry-pick-6661eb4900da.patch
Normal file
27
patches/angle/cherry-pick-6661eb4900da.patch
Normal file
@@ -0,0 +1,27 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jamie Madill <jmadill@chromium.org>
|
||||
Date: Mon, 2 May 2022 15:42:23 -0400
|
||||
Subject: Fix validation cache when deleting a Transform Feedback.
|
||||
|
||||
Bug: chromium:1320024
|
||||
Change-Id: I76ef85a3c65c663c138d8caebd4ef2c0da53cd4f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3621780
|
||||
Commit-Queue: Jamie Madill <jmadill@chromium.org>
|
||||
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
(cherry picked from commit 84e42c3b04da9e2c9d93d35bb6f2b1830fef22f4)
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3650697
|
||||
Reviewed-by: Geoff Lang <geofflang@chromium.org>
|
||||
|
||||
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
|
||||
index 390f7bc6790abe5d84b05f97160966eca46fee05..86da3fd82fff38c911e8678a1f6c415583b38381 100755
|
||||
--- a/src/libANGLE/Context.cpp
|
||||
+++ b/src/libANGLE/Context.cpp
|
||||
@@ -3075,6 +3075,7 @@ void Context::detachTransformFeedback(TransformFeedbackID transformFeedback)
|
||||
if (mState.removeTransformFeedbackBinding(this, transformFeedback))
|
||||
{
|
||||
bindTransformFeedback(GL_TRANSFORM_FEEDBACK, {0});
|
||||
+ mStateCache.onActiveTransformFeedbackChange(this);
|
||||
}
|
||||
}
|
||||
|
||||
238
patches/angle/cherry-pick-801b904aea7d.patch
Normal file
238
patches/angle/cherry-pick-801b904aea7d.patch
Normal file
@@ -0,0 +1,238 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jamie Madill <jmadill@chromium.org>
|
||||
Date: Fri, 20 May 2022 10:26:15 -0400
|
||||
Subject: D3D: Fix race condition with parallel shader compile.
|
||||
|
||||
Bug: chromium:1317673
|
||||
Change-Id: I0fb7c9a66248852e41e8700e80c295393ef941e8
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3651153
|
||||
Reviewed-by: Jie A Chen <jie.a.chen@intel.com>
|
||||
Reviewed-by: Lingfeng Yang <lfy@google.com>
|
||||
Commit-Queue: Jamie Madill <jmadill@chromium.org>
|
||||
(cherry picked from commit 4a20c9143abbf29c649cf643182735e8952089e3)
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3691050
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
|
||||
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
|
||||
index 59fe34fc46a1bfbc7b4be1aad6ad84b6da303b5b..3256357a80d69739661ec1fd32220d3037145875 100644
|
||||
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
|
||||
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
|
||||
@@ -1687,12 +1687,6 @@ class ProgramD3D::GetVertexExecutableTask : public ProgramD3D::GetExecutableTask
|
||||
angle::Result run() override
|
||||
{
|
||||
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetVertexExecutableTask::run");
|
||||
- if (!mProgram->mState.getAttachedShader(gl::ShaderType::Vertex))
|
||||
- {
|
||||
- return angle::Result::Continue;
|
||||
- }
|
||||
-
|
||||
- mProgram->updateCachedInputLayoutFromShader();
|
||||
|
||||
ANGLE_TRY(mProgram->getVertexExecutableForCachedInputLayout(this, &mExecutable, &mInfoLog));
|
||||
|
||||
@@ -2147,6 +2141,11 @@ std::unique_ptr<LinkEvent> ProgramD3D::link(const gl::Context *context,
|
||||
|
||||
linkResources(resources);
|
||||
|
||||
+ if (mState.getAttachedShader(gl::ShaderType::Vertex))
|
||||
+ {
|
||||
+ updateCachedInputLayoutFromShader();
|
||||
+ }
|
||||
+
|
||||
return compileProgramExecutables(context, infoLog);
|
||||
}
|
||||
}
|
||||
diff --git a/src/tests/gl_tests/ParallelShaderCompileTest.cpp b/src/tests/gl_tests/ParallelShaderCompileTest.cpp
|
||||
index bcd88ef01308759085c8244ad2058efe68686f2b..a98aff540c642617bba366b2e238519942d7e349 100644
|
||||
--- a/src/tests/gl_tests/ParallelShaderCompileTest.cpp
|
||||
+++ b/src/tests/gl_tests/ParallelShaderCompileTest.cpp
|
||||
@@ -58,9 +58,10 @@ class ParallelShaderCompileTest : public ANGLETest
|
||||
Task(int id) : mID(id) {}
|
||||
virtual ~Task() {}
|
||||
|
||||
- virtual bool compile() = 0;
|
||||
- virtual bool isCompileCompleted() = 0;
|
||||
- virtual bool link() = 0;
|
||||
+ virtual bool compile() = 0;
|
||||
+ virtual bool isCompileCompleted() = 0;
|
||||
+ virtual bool link() = 0;
|
||||
+ virtual void postLink() {}
|
||||
virtual void runAndVerify(ParallelShaderCompileTest *test) = 0;
|
||||
|
||||
bool isLinkCompleted()
|
||||
@@ -71,7 +72,7 @@ class ParallelShaderCompileTest : public ANGLETest
|
||||
}
|
||||
|
||||
protected:
|
||||
- std::string insertRandomString(const std::string &source)
|
||||
+ static std::string InsertRandomString(const std::string &source)
|
||||
{
|
||||
RNG rng;
|
||||
std::ostringstream ostream;
|
||||
@@ -80,7 +81,7 @@ class ParallelShaderCompileTest : public ANGLETest
|
||||
return ostream.str();
|
||||
}
|
||||
|
||||
- GLuint CompileShader(GLenum type, const std::string &source)
|
||||
+ static GLuint CompileShader(GLenum type, const std::string &source)
|
||||
{
|
||||
GLuint shader = glCreateShader(type);
|
||||
|
||||
@@ -90,7 +91,14 @@ class ParallelShaderCompileTest : public ANGLETest
|
||||
return shader;
|
||||
}
|
||||
|
||||
- bool checkShader(GLuint shader)
|
||||
+ static void RecompileShader(GLuint shader, const std::string &source)
|
||||
+ {
|
||||
+ const char *sourceArray[1] = {source.c_str()};
|
||||
+ glShaderSource(shader, 1, sourceArray, nullptr);
|
||||
+ glCompileShader(shader);
|
||||
+ }
|
||||
+
|
||||
+ static bool CheckShader(GLuint shader)
|
||||
{
|
||||
GLint compileResult;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
|
||||
@@ -129,7 +137,7 @@ class ParallelShaderCompileTest : public ANGLETest
|
||||
TaskRunner() {}
|
||||
~TaskRunner() {}
|
||||
|
||||
- void run(ParallelShaderCompileTest *test)
|
||||
+ void run(ParallelShaderCompileTest *test, unsigned int pollInterval)
|
||||
{
|
||||
|
||||
std::vector<std::unique_ptr<T>> compileTasks;
|
||||
@@ -151,6 +159,7 @@ class ParallelShaderCompileTest : public ANGLETest
|
||||
if (task->isCompileCompleted())
|
||||
{
|
||||
bool isLinking = task->link();
|
||||
+ task->postLink();
|
||||
ASSERT_TRUE(isLinking);
|
||||
linkTasks.push_back(std::move(task));
|
||||
compileTasks.erase(compileTasks.begin() + i);
|
||||
@@ -158,7 +167,10 @@ class ParallelShaderCompileTest : public ANGLETest
|
||||
}
|
||||
++i;
|
||||
}
|
||||
- angle::Sleep(kPollInterval);
|
||||
+ if (pollInterval != 0)
|
||||
+ {
|
||||
+ angle::Sleep(pollInterval);
|
||||
+ }
|
||||
}
|
||||
|
||||
while (!linkTasks.empty())
|
||||
@@ -173,9 +185,16 @@ class ParallelShaderCompileTest : public ANGLETest
|
||||
linkTasks.erase(linkTasks.begin() + i);
|
||||
continue;
|
||||
}
|
||||
+ else
|
||||
+ {
|
||||
+ task->postLink();
|
||||
+ }
|
||||
++i;
|
||||
}
|
||||
- angle::Sleep(kPollInterval);
|
||||
+ if (pollInterval != 0)
|
||||
+ {
|
||||
+ angle::Sleep(pollInterval);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -192,9 +211,9 @@ class ParallelShaderCompileTest : public ANGLETest
|
||||
bool compile() override
|
||||
{
|
||||
mVertexShader =
|
||||
- CompileShader(GL_VERTEX_SHADER, insertRandomString(essl1_shaders::vs::Simple()));
|
||||
+ CompileShader(GL_VERTEX_SHADER, InsertRandomString(essl1_shaders::vs::Simple()));
|
||||
mFragmentShader = CompileShader(GL_FRAGMENT_SHADER,
|
||||
- insertRandomString(essl1_shaders::fs::UniformColor()));
|
||||
+ InsertRandomString(essl1_shaders::fs::UniformColor()));
|
||||
return (mVertexShader != 0 && mFragmentShader != 0);
|
||||
}
|
||||
|
||||
@@ -213,7 +232,7 @@ class ParallelShaderCompileTest : public ANGLETest
|
||||
bool link() override
|
||||
{
|
||||
mProgram = 0;
|
||||
- if (checkShader(mVertexShader) && checkShader(mFragmentShader))
|
||||
+ if (CheckShader(mVertexShader) && CheckShader(mFragmentShader))
|
||||
{
|
||||
mProgram = glCreateProgram();
|
||||
glAttachShader(mProgram, mVertexShader);
|
||||
@@ -244,10 +263,25 @@ class ParallelShaderCompileTest : public ANGLETest
|
||||
ASSERT_GL_NO_ERROR();
|
||||
}
|
||||
|
||||
+ protected:
|
||||
+ void recompile()
|
||||
+ {
|
||||
+ RecompileShader(mVertexShader, essl1_shaders::vs::Simple());
|
||||
+ RecompileShader(mFragmentShader, essl1_shaders::fs::UniformColor());
|
||||
+ }
|
||||
+
|
||||
private:
|
||||
- GLColor mColor;
|
||||
GLuint mVertexShader;
|
||||
GLuint mFragmentShader;
|
||||
+ GLColor mColor;
|
||||
+ };
|
||||
+
|
||||
+ class ClearColorWithDrawRecompile : public ClearColorWithDraw
|
||||
+ {
|
||||
+ public:
|
||||
+ ClearColorWithDrawRecompile(int taskID) : ClearColorWithDraw(taskID) {}
|
||||
+
|
||||
+ void postLink() override { recompile(); }
|
||||
};
|
||||
|
||||
class ImageLoadStore : public Task
|
||||
@@ -268,7 +302,7 @@ void main()
|
||||
imageStore(uImage_2, ivec2(gl_LocalInvocationID.xy), value);
|
||||
})";
|
||||
|
||||
- mShader = CompileShader(GL_COMPUTE_SHADER, insertRandomString(kCSSource));
|
||||
+ mShader = CompileShader(GL_COMPUTE_SHADER, InsertRandomString(kCSSource));
|
||||
return mShader != 0;
|
||||
}
|
||||
|
||||
@@ -282,7 +316,7 @@ void main()
|
||||
bool link() override
|
||||
{
|
||||
mProgram = 0;
|
||||
- if (checkShader(mShader))
|
||||
+ if (CheckShader(mShader))
|
||||
{
|
||||
mProgram = glCreateProgram();
|
||||
glAttachShader(mProgram, mShader);
|
||||
@@ -370,7 +404,18 @@ TEST_P(ParallelShaderCompileTest, LinkAndDrawManyPrograms)
|
||||
ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
|
||||
|
||||
TaskRunner<ClearColorWithDraw> runner;
|
||||
- runner.run(this);
|
||||
+ runner.run(this, kPollInterval);
|
||||
+}
|
||||
+
|
||||
+// Tests no crash in case that the Shader starts another compile while the Program being attached
|
||||
+// to is still linking.
|
||||
+// crbug.com/1317673
|
||||
+TEST_P(ParallelShaderCompileTest, LinkProgramAndRecompileShader)
|
||||
+{
|
||||
+ ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
|
||||
+
|
||||
+ TaskRunner<ClearColorWithDrawRecompile> runner;
|
||||
+ runner.run(this, 0);
|
||||
}
|
||||
|
||||
class ParallelShaderCompileTestES31 : public ParallelShaderCompileTest
|
||||
@@ -389,7 +434,7 @@ TEST_P(ParallelShaderCompileTestES31, LinkAndDispatchManyPrograms)
|
||||
ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
|
||||
|
||||
TaskRunner<ImageLoadStore> runner;
|
||||
- runner.run(this);
|
||||
+ runner.run(this, kPollInterval);
|
||||
}
|
||||
|
||||
ANGLE_INSTANTIATE_TEST_ES2(ParallelShaderCompileTest);
|
||||
26
patches/angle/cherry-pick-9768648fffc9.patch
Normal file
26
patches/angle/cherry-pick-9768648fffc9.patch
Normal file
@@ -0,0 +1,26 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: SeongHwan Park <ggabu423@gmail.com>
|
||||
Date: Tue, 31 May 2022 02:41:32 +0900
|
||||
Subject: Fix to invalidate cache when binding Transform Feedback.
|
||||
|
||||
Bug: chromium:1330379
|
||||
Change-Id: I091116286ac511c50f9abcffa4d3cf350be920b4
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3677115
|
||||
Commit-Queue: Jamie Madill <jmadill@chromium.org>
|
||||
Reviewed-by: Jamie Madill <jmadill@chromium.org>
|
||||
(cherry picked from commit d96cee6685099f6bcc392a4d20d28c8ec484673a)
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3691799
|
||||
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
|
||||
|
||||
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
|
||||
index 685b235cc5962c4f55f539e387dda8c7edb023bc..390f7bc6790abe5d84b05f97160966eca46fee05 100755
|
||||
--- a/src/libANGLE/Context.cpp
|
||||
+++ b/src/libANGLE/Context.cpp
|
||||
@@ -1323,6 +1323,7 @@ void Context::bindTransformFeedback(GLenum target, TransformFeedbackID transform
|
||||
TransformFeedback *transformFeedback =
|
||||
checkTransformFeedbackAllocation(transformFeedbackHandle);
|
||||
mState.setTransformFeedbackBinding(this, transformFeedback);
|
||||
+ mStateCache.onActiveTransformFeedbackChange(this);
|
||||
}
|
||||
|
||||
void Context::bindProgramPipeline(ProgramPipelineID pipelineHandle)
|
||||
@@ -102,12 +102,12 @@ chore_do_not_use_chrome_windows_in_cryptotoken_webrequestsender.patch
|
||||
process_singleton.patch
|
||||
fix_expose_decrementcapturercount_in_web_contents_impl.patch
|
||||
add_ui_scopedcliboardwriter_writeunsaferawdata.patch
|
||||
feat_add_data_parameter_to_processsingleton.patch
|
||||
mas_gate_private_enterprise_APIs.patch
|
||||
load_v8_snapshot_in_browser_process.patch
|
||||
fix_patch_out_permissions_checks_in_exclusive_access.patch
|
||||
fix_adapt_exclusive_access_for_electron_needs.patch
|
||||
fix_aspect_ratio_with_max_size.patch
|
||||
fix_dont_delete_SerialPortManager_on_main_thread.patch
|
||||
feat_add_data_transfer_to_requestsingleinstancelock.patch
|
||||
fix_crash_when_saving_edited_pdf_files.patch
|
||||
port_autofill_colors_to_the_color_pipeline.patch
|
||||
build_disable_partition_alloc_on_mac.patch
|
||||
@@ -119,3 +119,33 @@ cherry-pick-cf64617c1cc5.patch
|
||||
cherry-pick-e2b8856012e0.patch
|
||||
cherry-pick-6b66a45021a0.patch
|
||||
fix_xkb_keysym_reverse_look_up_for_lacros.patch
|
||||
custom_protocols_plzserviceworker.patch
|
||||
pa_support_16kb_pagesize_on_linux_arm64.patch
|
||||
cherry-pick-f1504440487f.patch
|
||||
cherry-pick-21139756239b.patch
|
||||
cherry-pick-2782c7bc5bbe.patch
|
||||
cherry-pick-f3d01ff794dc.patch
|
||||
cherry-pick-919b1ffe1fe7.patch
|
||||
cherry-pick-f1dd785e021e.patch
|
||||
cherry-pick-b03797bdb1df.patch
|
||||
posix_replace_doubleforkandexec_with_forkandspawn.patch
|
||||
cherry-pick-ecad352cd614.patch
|
||||
cherry-pick-f427936d32db.patch
|
||||
cherry-pick-22c61cfae5d1.patch
|
||||
remove_default_window_title.patch
|
||||
cherry-pick-d7a5d6b38ea8.patch
|
||||
cherry-pick-22abbad430b6.patch
|
||||
cherry-pick-3cbd5973d704.patch
|
||||
cherry-pick-902f0d144a5b.patch
|
||||
cherry-pick-664e0d8b4cfb.patch
|
||||
chore_add_electron_deps_to_gitignores.patch
|
||||
chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch
|
||||
do_not_reduce_page_size_from_64k_to_4k_on_linux_arm64.patch
|
||||
cherry-pick-94a8bdafc8c6.patch
|
||||
fix_mac_build_with_enable_plugins_false.patch
|
||||
fix_windows_build_with_enable_plugins_false.patch
|
||||
m104_background_fetch_passing.patch
|
||||
cherry-pick-c643d18a078d.patch
|
||||
merge_104_speculative_fix_for_isvalidcodepointinindex_range_crash.patch
|
||||
cherry-pick-60d8559e150a.patch
|
||||
cherry-pick-54e32332750c.patch
|
||||
|
||||
114
patches/chromium/cherry-pick-21139756239b.patch
Normal file
114
patches/chromium/cherry-pick-21139756239b.patch
Normal file
@@ -0,0 +1,114 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ari Chivukula <arichiv@chromium.org>
|
||||
Date: Tue, 17 May 2022 18:17:07 +0000
|
||||
Subject: {M102 PICK} [IndexedDB] Use-After-Free Fix
|
||||
|
||||
We can't guarantee order in the task the constructor dispatches the same
|
||||
way we could before due to all the async changes. Let's be sure all the
|
||||
objects exist before using them now. Long term, we need to address
|
||||
ownership of the idb context.
|
||||
|
||||
Bug: 1324864, 1218100
|
||||
Change-Id: Id5753297a4c966432028a1e7e063c5f1bed6f619
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3646994
|
||||
Reviewed-by: Ayu Ishii <ayui@chromium.org>
|
||||
Commit-Queue: Srinivas Sista <srinivassista@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5005@{#812}
|
||||
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
|
||||
|
||||
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
|
||||
index 9472e762729312bf68073c95056ce09e324148da..219904d80798f71f0be23ee9ffeb5c58d030fb55 100644
|
||||
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
|
||||
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
|
||||
@@ -167,38 +167,32 @@ IndexedDBContextImpl::IndexedDBContextImpl(
|
||||
std::move(quota_client_remote),
|
||||
storage::QuotaClientType::kIndexedDatabase,
|
||||
{blink::mojom::StorageType::kTemporary});
|
||||
+ IDBTaskRunner()->PostTask(
|
||||
+ FROM_HERE, base::BindOnce(&IndexedDBContextImpl::BindPipesOnIDBSequence,
|
||||
+ weak_factory_.GetWeakPtr(),
|
||||
+ std::move(quota_client_receiver),
|
||||
+ std::move(blob_storage_context),
|
||||
+ std::move(file_system_access_context)));
|
||||
+}
|
||||
|
||||
- // This is safe because the IndexedDBContextImpl must be destructed on the
|
||||
- // IDBTaskRunner, and this task will always happen before that.
|
||||
- idb_task_runner_->PostTask(
|
||||
- FROM_HERE,
|
||||
- base::BindOnce(
|
||||
- [](mojo::Remote<storage::mojom::BlobStorageContext>*
|
||||
- blob_storage_context,
|
||||
- mojo::Remote<storage::mojom::FileSystemAccessContext>*
|
||||
- file_system_access_context,
|
||||
- mojo::Receiver<storage::mojom::QuotaClient>* quota_client_receiver,
|
||||
- mojo::PendingRemote<storage::mojom::BlobStorageContext>
|
||||
- pending_blob_storage_context,
|
||||
- mojo::PendingRemote<storage::mojom::FileSystemAccessContext>
|
||||
- pending_file_system_access_context,
|
||||
- mojo::PendingReceiver<storage::mojom::QuotaClient>
|
||||
- quota_client_pending_receiver) {
|
||||
- quota_client_receiver->Bind(
|
||||
- std::move(quota_client_pending_receiver));
|
||||
- if (pending_blob_storage_context) {
|
||||
- blob_storage_context->Bind(
|
||||
- std::move(pending_blob_storage_context));
|
||||
- }
|
||||
- if (pending_file_system_access_context) {
|
||||
- file_system_access_context->Bind(
|
||||
- std::move(pending_file_system_access_context));
|
||||
- }
|
||||
- },
|
||||
- &blob_storage_context_, &file_system_access_context_,
|
||||
- "a_client_receiver_, std::move(blob_storage_context),
|
||||
- std::move(file_system_access_context),
|
||||
- std::move(quota_client_receiver)));
|
||||
+void IndexedDBContextImpl::BindPipesOnIDBSequence(
|
||||
+ mojo::PendingReceiver<storage::mojom::QuotaClient>
|
||||
+ pending_quota_client_receiver,
|
||||
+ mojo::PendingRemote<storage::mojom::BlobStorageContext>
|
||||
+ pending_blob_storage_context,
|
||||
+ mojo::PendingRemote<storage::mojom::FileSystemAccessContext>
|
||||
+ pending_file_system_access_context) {
|
||||
+ DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
|
||||
+ if (pending_quota_client_receiver) {
|
||||
+ quota_client_receiver_.Bind(std::move(pending_quota_client_receiver));
|
||||
+ }
|
||||
+ if (pending_blob_storage_context) {
|
||||
+ blob_storage_context_.Bind(std::move(pending_blob_storage_context));
|
||||
+ }
|
||||
+ if (pending_file_system_access_context) {
|
||||
+ file_system_access_context_.Bind(
|
||||
+ std::move(pending_file_system_access_context));
|
||||
+ }
|
||||
}
|
||||
|
||||
void IndexedDBContextImpl::Bind(
|
||||
diff --git a/content/browser/indexed_db/indexed_db_context_impl.h b/content/browser/indexed_db/indexed_db_context_impl.h
|
||||
index 4b874b244778d831e0fa8ca3bbfd7e751897923e..ad2f984e130ece867c0f0dc7ca9ae52b8ec12ae9 100644
|
||||
--- a/content/browser/indexed_db/indexed_db_context_impl.h
|
||||
+++ b/content/browser/indexed_db/indexed_db_context_impl.h
|
||||
@@ -224,6 +224,14 @@ class CONTENT_EXPORT IndexedDBContextImpl
|
||||
|
||||
~IndexedDBContextImpl() override;
|
||||
|
||||
+ void BindPipesOnIDBSequence(
|
||||
+ mojo::PendingReceiver<storage::mojom::QuotaClient>
|
||||
+ pending_quota_client_receiver,
|
||||
+ mojo::PendingRemote<storage::mojom::BlobStorageContext>
|
||||
+ pending_blob_storage_context,
|
||||
+ mojo::PendingRemote<storage::mojom::FileSystemAccessContext>
|
||||
+ pending_file_system_access_context);
|
||||
+
|
||||
// Binds receiver on bucket retrieval to ensure that a bucket always exists
|
||||
// for a storage key.
|
||||
void BindIndexedDBWithBucket(
|
||||
@@ -282,6 +290,8 @@ class CONTENT_EXPORT IndexedDBContextImpl
|
||||
mojo::Receiver<storage::mojom::QuotaClient> quota_client_receiver_;
|
||||
const std::unique_ptr<storage::FilesystemProxy> filesystem_proxy_;
|
||||
|
||||
+ // weak_factory_->GetWeakPtr() may be used on any thread, but the resulting
|
||||
+ // pointer must only be checked/used on idb_task_runner_.
|
||||
base::WeakPtrFactory<IndexedDBContextImpl> weak_factory_{this};
|
||||
};
|
||||
|
||||
427
patches/chromium/cherry-pick-22abbad430b6.patch
Normal file
427
patches/chromium/cherry-pick-22abbad430b6.patch
Normal file
@@ -0,0 +1,427 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Austin Eng <enga@chromium.org>
|
||||
Date: Thu, 23 Jun 2022 03:12:49 +0000
|
||||
Subject: WebGPU: Mark the context lost on GPU context lost
|
||||
|
||||
M102 merge issues:
|
||||
- dawn_control_client_holder.h/cc:
|
||||
GetWGPUInstance() not present in M102
|
||||
|
||||
Fixes a bug where completely destructing the context instead of
|
||||
marking it lost when receiving a context lost notification freed
|
||||
memory still accessible by the page.
|
||||
|
||||
Fixed: 1336014
|
||||
Change-Id: I662e531102af91362b4f62700bfbee507fc44d1f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3708646
|
||||
Commit-Queue: Austin Eng <enga@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1017003}
|
||||
(cherry picked from commit 6c7f327b7a15aabd3fc5d57e9c05b95d02f1cd36)
|
||||
|
||||
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
|
||||
index 1c72562d4f58eb77742bfd9539e5d132683949fa..c56507ad59bc111a22c0abd072c1bd21ac3d8808 100644
|
||||
--- a/third_party/blink/renderer/modules/BUILD.gn
|
||||
+++ b/third_party/blink/renderer/modules/BUILD.gn
|
||||
@@ -662,6 +662,7 @@ source_set("unit_tests") {
|
||||
"//third_party/blink/renderer/modules/peerconnection:test_support",
|
||||
"//third_party/blink/renderer/modules/storage:unit_tests",
|
||||
"//third_party/blink/renderer/modules/webcodecs:unit_tests",
|
||||
+ "//third_party/blink/renderer/modules/webgpu:unit_tests",
|
||||
"//third_party/blink/renderer/modules/webtransport:unit_tests",
|
||||
"//third_party/blink/renderer/platform",
|
||||
"//third_party/blink/renderer/platform:test_support",
|
||||
diff --git a/third_party/blink/renderer/modules/webgpu/BUILD.gn b/third_party/blink/renderer/modules/webgpu/BUILD.gn
|
||||
index dcfece568d0e0e2bc5dbd6383fb1a0c2235a296c..71f2880467c50c145d5a68b4cfac62cba61f8e27 100644
|
||||
--- a/third_party/blink/renderer/modules/webgpu/BUILD.gn
|
||||
+++ b/third_party/blink/renderer/modules/webgpu/BUILD.gn
|
||||
@@ -91,3 +91,17 @@ blink_modules_sources("webgpu") {
|
||||
"//third_party/dawn/include/dawn:headers",
|
||||
]
|
||||
}
|
||||
+
|
||||
+source_set("unit_tests") {
|
||||
+ testonly = true
|
||||
+ sources = [ "gpu_context_lost_test.cc" ]
|
||||
+
|
||||
+ deps = [
|
||||
+ "//base/test:test_support",
|
||||
+ "//third_party/blink/renderer/controller:blink_bindings_test_sources",
|
||||
+ "//third_party/blink/renderer/core",
|
||||
+ "//third_party/blink/renderer/core:testing",
|
||||
+ "//third_party/blink/renderer/modules",
|
||||
+ "//third_party/blink/renderer/platform:test_support",
|
||||
+ ]
|
||||
+}
|
||||
diff --git a/third_party/blink/renderer/modules/webgpu/DEPS b/third_party/blink/renderer/modules/webgpu/DEPS
|
||||
index ded05aa68e59cadddb678d5254119ae3dad1b47e..9e777ab0473f0bb9cbd46e4d6c1498f1a12a6f40 100644
|
||||
--- a/third_party/blink/renderer/modules/webgpu/DEPS
|
||||
+++ b/third_party/blink/renderer/modules/webgpu/DEPS
|
||||
@@ -9,6 +9,7 @@ include_rules = [
|
||||
"+gpu/command_buffer/client/raster_interface.h",
|
||||
"+gpu/command_buffer/client/shared_image_interface.h",
|
||||
"+gpu/command_buffer/client/webgpu_interface.h",
|
||||
+ "+gpu/command_buffer/client/webgpu_interface_stub.h",
|
||||
"+media/base/video_frame.h",
|
||||
"+media/renderers/paint_canvas_video_renderer.h",
|
||||
"+services/metrics/public/cpp/ukm_builders.h",
|
||||
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.cc b/third_party/blink/renderer/modules/webgpu/gpu.cc
|
||||
index 8a1f4dcaf605bc656a6499bd415d9db4db9e8ed4..d86dd384e71894f9aaefaf4491c4fc2aabefbea9 100644
|
||||
--- a/third_party/blink/renderer/modules/webgpu/gpu.cc
|
||||
+++ b/third_party/blink/renderer/modules/webgpu/gpu.cc
|
||||
@@ -287,4 +287,9 @@ void BoxedMappableWGPUBufferHandles::ClearAndDestroyAll(
|
||||
contents_.clear();
|
||||
}
|
||||
|
||||
+void GPU::SetDawnControlClientHolderForTesting(
|
||||
+ scoped_refptr<DawnControlClientHolder> dawn_control_client) {
|
||||
+ dawn_control_client_ = std::move(dawn_control_client);
|
||||
+}
|
||||
+
|
||||
} // namespace blink
|
||||
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.h b/third_party/blink/renderer/modules/webgpu/gpu.h
|
||||
index a7348a1c18a2004734275cb4d6f1f4aa68b11d8e..17362a2ee1b3111b52c93ffd0e3222c3de893479 100644
|
||||
--- a/third_party/blink/renderer/modules/webgpu/gpu.h
|
||||
+++ b/third_party/blink/renderer/modules/webgpu/gpu.h
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
|
||||
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
|
||||
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
|
||||
+#include "third_party/blink/renderer/modules/modules_export.h"
|
||||
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
|
||||
#include "third_party/blink/renderer/platform/supplementable.h"
|
||||
|
||||
@@ -47,9 +48,9 @@ struct BoxedMappableWGPUBufferHandles
|
||||
HashSet<void*> contents_;
|
||||
};
|
||||
|
||||
-class GPU final : public ScriptWrappable,
|
||||
- public Supplement<NavigatorBase>,
|
||||
- public ExecutionContextLifecycleObserver {
|
||||
+class MODULES_EXPORT GPU final : public ScriptWrappable,
|
||||
+ public Supplement<NavigatorBase>,
|
||||
+ public ExecutionContextLifecycleObserver {
|
||||
DEFINE_WRAPPERTYPEINFO();
|
||||
|
||||
public:
|
||||
@@ -86,6 +87,9 @@ class GPU final : public ScriptWrappable,
|
||||
return mappable_buffer_handles_.get();
|
||||
}
|
||||
|
||||
+ void SetDawnControlClientHolderForTesting(
|
||||
+ scoped_refptr<DawnControlClientHolder> dawn_control_client);
|
||||
+
|
||||
private:
|
||||
void OnRequestAdapterCallback(ScriptState* script_state,
|
||||
const GPURequestAdapterOptions* options,
|
||||
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_context_lost_test.cc b/third_party/blink/renderer/modules/webgpu/gpu_context_lost_test.cc
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..1063295a543eaee5650cdaf5b115284b8c2f095e
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/renderer/modules/webgpu/gpu_context_lost_test.cc
|
||||
@@ -0,0 +1,229 @@
|
||||
+// Copyright 2022 The Chromium Authors. All rights reserved.
|
||||
+// Use of this source code is governed by a BSD-style license that can be
|
||||
+// found in the LICENSE file.
|
||||
+
|
||||
+#include "base/callback.h"
|
||||
+#include "base/run_loop.h"
|
||||
+#include "base/test/mock_callback.h"
|
||||
+#include "gpu/command_buffer/client/webgpu_interface_stub.h"
|
||||
+#include "testing/gmock/include/gmock/gmock.h"
|
||||
+#include "testing/gtest/include/gtest/gtest.h"
|
||||
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
|
||||
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
|
||||
+#include "third_party/blink/renderer/core/frame/local_frame.h"
|
||||
+#include "third_party/blink/renderer/core/frame/navigator.h"
|
||||
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
|
||||
+#include "third_party/blink/renderer/modules/webgpu/gpu.h"
|
||||
+#include "third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h"
|
||||
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h"
|
||||
+
|
||||
+namespace blink {
|
||||
+
|
||||
+namespace {
|
||||
+
|
||||
+class WebGPUContextProviderForTest
|
||||
+ : public WebGraphicsContext3DProviderForTests {
|
||||
+ public:
|
||||
+ explicit WebGPUContextProviderForTest(
|
||||
+ base::MockCallback<base::OnceClosure>* destruction_callback)
|
||||
+ : WebGraphicsContext3DProviderForTests(
|
||||
+ std::make_unique<gpu::webgpu::WebGPUInterfaceStub>()),
|
||||
+ destruction_callback_(destruction_callback) {}
|
||||
+ ~WebGPUContextProviderForTest() override {
|
||||
+ if (destruction_callback_) {
|
||||
+ destruction_callback_->Run();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ static WebGPUContextProviderForTest* From(
|
||||
+ scoped_refptr<DawnControlClientHolder>& dawn_control_client) {
|
||||
+ return static_cast<WebGPUContextProviderForTest*>(
|
||||
+ dawn_control_client->GetContextProviderWeakPtr()->ContextProvider());
|
||||
+ }
|
||||
+
|
||||
+ void ClearDestructionCallback() { destruction_callback_ = nullptr; }
|
||||
+
|
||||
+ void SetLostContextCallback(
|
||||
+ base::RepeatingClosure lost_context_callback) override {
|
||||
+ lost_context_callback_ = std::move(lost_context_callback);
|
||||
+ }
|
||||
+
|
||||
+ void CallLostContextCallback() { lost_context_callback_.Run(); }
|
||||
+
|
||||
+ private:
|
||||
+ base::MockCallback<base::OnceClosure>* destruction_callback_;
|
||||
+ base::RepeatingClosure lost_context_callback_;
|
||||
+};
|
||||
+
|
||||
+class WebGPUContextLostTest : public testing::Test {
|
||||
+ protected:
|
||||
+ void SetUp() override { page_ = std::make_unique<DummyPageHolder>(); }
|
||||
+
|
||||
+ std::tuple<ExecutionContext*, GPU*> SetUpGPU(V8TestingScope* v8_test_scope) {
|
||||
+ ExecutionContext* execution_context =
|
||||
+ ExecutionContext::From(v8_test_scope->GetScriptState());
|
||||
+
|
||||
+ Navigator* navigator = page_->GetFrame().DomWindow()->navigator();
|
||||
+ GPU* gpu = MakeGarbageCollected<GPU>(*navigator);
|
||||
+ return std::make_tuple(execution_context, gpu);
|
||||
+ }
|
||||
+
|
||||
+ std::unique_ptr<DummyPageHolder> page_;
|
||||
+};
|
||||
+
|
||||
+// Test that the context provider is destructed after the last reference to
|
||||
+// its owning DawnControlClientHolder is dropped.
|
||||
+TEST_F(WebGPUContextLostTest, DestructedAfterLastRefDropped) {
|
||||
+ V8TestingScope v8_test_scope;
|
||||
+ ExecutionContext* execution_context =
|
||||
+ ExecutionContext::From(v8_test_scope.GetScriptState());
|
||||
+
|
||||
+ base::MockCallback<base::OnceClosure> destruction_callback;
|
||||
+ auto context_provider =
|
||||
+ std::make_unique<WebGPUContextProviderForTest>(&destruction_callback);
|
||||
+
|
||||
+ auto dawn_control_client = DawnControlClientHolder::Create(
|
||||
+ std::move(context_provider),
|
||||
+ execution_context->GetTaskRunner(TaskType::kWebGPU));
|
||||
+
|
||||
+ // Drop the last reference to the DawnControlClientHolder which will
|
||||
+ // now destroy the context provider.
|
||||
+ EXPECT_CALL(destruction_callback, Run()).Times(1);
|
||||
+ dawn_control_client = nullptr;
|
||||
+}
|
||||
+
|
||||
+// Test that the GPU lost context callback marks the context lost, but does not
|
||||
+// destruct it.
|
||||
+TEST_F(WebGPUContextLostTest, GPULostContext) {
|
||||
+ V8TestingScope v8_test_scope;
|
||||
+ auto [execution_context, gpu] = SetUpGPU(&v8_test_scope);
|
||||
+
|
||||
+ base::MockCallback<base::OnceClosure> destruction_callback;
|
||||
+ auto context_provider =
|
||||
+ std::make_unique<WebGPUContextProviderForTest>(&destruction_callback);
|
||||
+
|
||||
+ auto dawn_control_client = DawnControlClientHolder::Create(
|
||||
+ std::move(context_provider),
|
||||
+ execution_context->GetTaskRunner(TaskType::kWebGPU));
|
||||
+
|
||||
+ gpu->SetDawnControlClientHolderForTesting(dawn_control_client);
|
||||
+
|
||||
+ // Trigger the lost context callback, but the context should not be destroyed.
|
||||
+ EXPECT_CALL(destruction_callback, Run()).Times(0);
|
||||
+ WebGPUContextProviderForTest::From(dawn_control_client)
|
||||
+ ->CallLostContextCallback();
|
||||
+ testing::Mock::VerifyAndClear(&destruction_callback);
|
||||
+
|
||||
+ // The context should be marked lost.
|
||||
+ EXPECT_TRUE(dawn_control_client->IsContextLost());
|
||||
+
|
||||
+ // The context provider should still be live.
|
||||
+ auto context_provider_weak_ptr =
|
||||
+ dawn_control_client->GetContextProviderWeakPtr();
|
||||
+ EXPECT_NE(context_provider_weak_ptr, nullptr);
|
||||
+
|
||||
+ // Clear the destruction callback since it is stack-allocated in this frame.
|
||||
+ static_cast<WebGPUContextProviderForTest*>(
|
||||
+ context_provider_weak_ptr->ContextProvider())
|
||||
+ ->ClearDestructionCallback();
|
||||
+}
|
||||
+
|
||||
+// Test that the GPU lost context callback marks the context lost, and then when
|
||||
+// the context is recreated, the context still lives until the previous
|
||||
+// DawnControlClientHolder is destroyed.
|
||||
+TEST_F(WebGPUContextLostTest, RecreatedAfterGPULostContext) {
|
||||
+ V8TestingScope v8_test_scope;
|
||||
+ auto [execution_context, gpu] = SetUpGPU(&v8_test_scope);
|
||||
+
|
||||
+ base::MockCallback<base::OnceClosure> destruction_callback;
|
||||
+ auto context_provider =
|
||||
+ std::make_unique<WebGPUContextProviderForTest>(&destruction_callback);
|
||||
+
|
||||
+ auto dawn_control_client = DawnControlClientHolder::Create(
|
||||
+ std::move(context_provider),
|
||||
+ execution_context->GetTaskRunner(TaskType::kWebGPU));
|
||||
+
|
||||
+ gpu->SetDawnControlClientHolderForTesting(dawn_control_client);
|
||||
+
|
||||
+ // Trigger the lost context callback, but the context should not be destroyed.
|
||||
+ EXPECT_CALL(destruction_callback, Run()).Times(0);
|
||||
+ WebGPUContextProviderForTest::From(dawn_control_client)
|
||||
+ ->CallLostContextCallback();
|
||||
+ testing::Mock::VerifyAndClear(&destruction_callback);
|
||||
+
|
||||
+ // The context should be marked lost.
|
||||
+ EXPECT_TRUE(dawn_control_client->IsContextLost());
|
||||
+
|
||||
+ // The context provider should still be live.
|
||||
+ auto context_provider_weak_ptr =
|
||||
+ dawn_control_client->GetContextProviderWeakPtr();
|
||||
+ EXPECT_NE(context_provider_weak_ptr, nullptr);
|
||||
+
|
||||
+ // Make a new context provider and DawnControlClientHolder
|
||||
+ base::MockCallback<base::OnceClosure> destruction_callback2;
|
||||
+ auto context_provider2 =
|
||||
+ std::make_unique<WebGPUContextProviderForTest>(&destruction_callback2);
|
||||
+
|
||||
+ auto dawn_control_client2 = DawnControlClientHolder::Create(
|
||||
+ std::move(context_provider2),
|
||||
+ execution_context->GetTaskRunner(TaskType::kWebGPU));
|
||||
+
|
||||
+ // Set the new context, but the previous context should still not be
|
||||
+ // destroyed.
|
||||
+ EXPECT_CALL(destruction_callback, Run()).Times(0);
|
||||
+ gpu->SetDawnControlClientHolderForTesting(dawn_control_client2);
|
||||
+ testing::Mock::VerifyAndClear(&destruction_callback);
|
||||
+
|
||||
+ // Drop the last reference to the previous DawnControlClientHolder which will
|
||||
+ // now destroy the previous context provider.
|
||||
+ EXPECT_CALL(destruction_callback, Run()).Times(1);
|
||||
+ dawn_control_client = nullptr;
|
||||
+ testing::Mock::VerifyAndClear(&destruction_callback);
|
||||
+
|
||||
+ // Clear the destruction callback since it is stack-allocated in this frame.
|
||||
+ static_cast<WebGPUContextProviderForTest*>(
|
||||
+ dawn_control_client2->GetContextProviderWeakPtr()->ContextProvider())
|
||||
+ ->ClearDestructionCallback();
|
||||
+}
|
||||
+
|
||||
+// Test that ContextDestroyed lifecycle event destructs the context.
|
||||
+TEST_F(WebGPUContextLostTest, ContextDestroyed) {
|
||||
+ V8TestingScope v8_test_scope;
|
||||
+ auto [execution_context, gpu] = SetUpGPU(&v8_test_scope);
|
||||
+
|
||||
+ base::MockCallback<base::OnceClosure> destruction_callback;
|
||||
+ auto context_provider =
|
||||
+ std::make_unique<WebGPUContextProviderForTest>(&destruction_callback);
|
||||
+
|
||||
+ auto dawn_control_client = DawnControlClientHolder::Create(
|
||||
+ std::move(context_provider),
|
||||
+ execution_context->GetTaskRunner(TaskType::kWebGPU));
|
||||
+
|
||||
+ gpu->SetDawnControlClientHolderForTesting(dawn_control_client);
|
||||
+
|
||||
+ // Trigger the context destroyed lifecycle event. The context should not be
|
||||
+ // destroyed yet.
|
||||
+ EXPECT_CALL(destruction_callback, Run()).Times(0);
|
||||
+ gpu->ContextDestroyed();
|
||||
+ testing::Mock::VerifyAndClear(&destruction_callback);
|
||||
+
|
||||
+ // The context should be marked lost.
|
||||
+ EXPECT_TRUE(dawn_control_client->IsContextLost());
|
||||
+
|
||||
+ // Getting the context provider should return null.
|
||||
+ EXPECT_EQ(dawn_control_client->GetContextProviderWeakPtr(), nullptr);
|
||||
+
|
||||
+ // The context is destructed in a posted task with a fresh callstack to avoid
|
||||
+ // re-entrancy issues. Expectations should resolve by the end of the next
|
||||
+ // task.
|
||||
+ EXPECT_CALL(destruction_callback, Run()).Times(1);
|
||||
+ base::RunLoop loop;
|
||||
+ execution_context->GetTaskRunner(TaskType::kWebGPU)
|
||||
+ ->PostTask(FROM_HERE, loop.QuitClosure());
|
||||
+ loop.Run();
|
||||
+ testing::Mock::VerifyAndClear(&destruction_callback);
|
||||
+}
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
+} // namespace blink
|
||||
diff --git a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc
|
||||
index be5b392fa83a4197958f9f032eac4dd41af61f61..6a6283972e7bf597fdf720eefafb57dc4e3263a2 100644
|
||||
--- a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc
|
||||
+++ b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc
|
||||
@@ -17,9 +17,17 @@ scoped_refptr<DawnControlClientHolder> DawnControlClientHolder::Create(
|
||||
auto dawn_control_client_holder =
|
||||
base::MakeRefCounted<DawnControlClientHolder>(std::move(context_provider),
|
||||
std::move(task_runner));
|
||||
+ // The context lost callback occurs when the client receives
|
||||
+ // OnGpuControlLostContext. This can happen on fatal errors when the GPU
|
||||
+ // channel is disconnected: the GPU process crashes, the GPU process fails to
|
||||
+ // deserialize a message, etc. We mark the context lost, but NOT destroy the
|
||||
+ // entire WebGraphicsContext3DProvider as that would free services for mapping
|
||||
+ // shared memory. There may still be outstanding mapped GPUBuffers pointing to
|
||||
+ // this memory.
|
||||
dawn_control_client_holder->context_provider_->ContextProvider()
|
||||
->SetLostContextCallback(WTF::BindRepeating(
|
||||
- &DawnControlClientHolder::Destroy, dawn_control_client_holder));
|
||||
+ &DawnControlClientHolder::MarkContextLost,
|
||||
+ dawn_control_client_holder->weak_ptr_factory_.GetWeakPtr()));
|
||||
return dawn_control_client_holder;
|
||||
}
|
||||
|
||||
@@ -38,7 +46,7 @@ DawnControlClientHolder::DawnControlClientHolder(
|
||||
DawnControlClientHolder::~DawnControlClientHolder() = default;
|
||||
|
||||
void DawnControlClientHolder::Destroy() {
|
||||
- api_channel_->Disconnect();
|
||||
+ MarkContextLost();
|
||||
|
||||
// Destroy the WebGPU context.
|
||||
// This ensures that GPU resources are eagerly reclaimed.
|
||||
@@ -68,8 +76,16 @@ DawnControlClientHolder::GetContextProviderWeakPtr() const {
|
||||
return context_provider_->GetWeakPtr();
|
||||
}
|
||||
|
||||
+void DawnControlClientHolder::MarkContextLost() {
|
||||
+ if (context_lost_) {
|
||||
+ return;
|
||||
+ }
|
||||
+ api_channel_->Disconnect();
|
||||
+ context_lost_ = true;
|
||||
+}
|
||||
+
|
||||
bool DawnControlClientHolder::IsContextLost() const {
|
||||
- return !context_provider_;
|
||||
+ return context_lost_;
|
||||
}
|
||||
|
||||
std::unique_ptr<RecyclableCanvasResource>
|
||||
diff --git a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
|
||||
index 550038892c97e8f5f46e7a08c07821aa083b67d1..9e3c086225aff661e2402b974534d27860f4f446 100644
|
||||
--- a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
|
||||
+++ b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
|
||||
@@ -47,6 +47,7 @@ class PLATFORM_EXPORT DawnControlClientHolder
|
||||
base::WeakPtr<WebGraphicsContext3DProviderWrapper> GetContextProviderWeakPtr()
|
||||
const;
|
||||
const DawnProcTable& GetProcs() const { return procs_; }
|
||||
+ void MarkContextLost();
|
||||
bool IsContextLost() const;
|
||||
std::unique_ptr<RecyclableCanvasResource> GetOrCreateCanvasResource(
|
||||
const SkImageInfo& info,
|
||||
@@ -56,11 +57,14 @@ class PLATFORM_EXPORT DawnControlClientHolder
|
||||
friend class RefCounted<DawnControlClientHolder>;
|
||||
~DawnControlClientHolder();
|
||||
|
||||
+ bool context_lost_ = false;
|
||||
std::unique_ptr<WebGraphicsContext3DProviderWrapper> context_provider_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
|
||||
scoped_refptr<gpu::webgpu::APIChannel> api_channel_;
|
||||
DawnProcTable procs_;
|
||||
WebGPURecyclableResourceCache recyclable_resource_cache_;
|
||||
+
|
||||
+ base::WeakPtrFactory<DawnControlClientHolder> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
97
patches/chromium/cherry-pick-22c61cfae5d1.patch
Normal file
97
patches/chromium/cherry-pick-22c61cfae5d1.patch
Normal file
@@ -0,0 +1,97 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Austin Sullivan <asully@chromium.org>
|
||||
Date: Thu, 12 May 2022 04:52:20 +0000
|
||||
Subject: FSA: Sanitize .url files
|
||||
|
||||
Bug: 1307930
|
||||
Change-Id: I7ed3cca5942a5334ba761d269bdd8961fa9d13fe
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3638698
|
||||
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
|
||||
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
|
||||
Auto-Submit: Austin Sullivan <asully@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1002495}
|
||||
|
||||
diff --git a/content/browser/file_system_access/file_system_chooser.cc b/content/browser/file_system_access/file_system_chooser.cc
|
||||
index 86b9af148a86e64f9f4aedb6e39998bf83668745..f6018224c5f0aea3aba6735d49aaa25d077de600 100644
|
||||
--- a/content/browser/file_system_access/file_system_chooser.cc
|
||||
+++ b/content/browser/file_system_access/file_system_chooser.cc
|
||||
@@ -276,13 +276,15 @@ bool FileSystemChooser::IsShellIntegratedExtension(
|
||||
base::FilePath::StringType extension_lower =
|
||||
base::ToLowerASCII(GetLastExtension(extension));
|
||||
|
||||
- // .lnk and .scf files may be used to execute arbitrary code (see
|
||||
+ // '.lnk' and '.scf' files may be used to execute arbitrary code (see
|
||||
// https://nvd.nist.gov/vuln/detail/CVE-2010-2568 and
|
||||
- // https://crbug.com/1227995, respectively). .local files are used by Windows
|
||||
- // to determine which DLLs to load for an application.
|
||||
+ // https://crbug.com/1227995, respectively). '.local' files are used by
|
||||
+ // Windows to determine which DLLs to load for an application. '.url' files
|
||||
+ // can be used to read arbirtary files (see https://crbug.com/1307930).
|
||||
if ((extension_lower == FILE_PATH_LITERAL("lnk")) ||
|
||||
(extension_lower == FILE_PATH_LITERAL("local")) ||
|
||||
- (extension_lower == FILE_PATH_LITERAL("scf"))) {
|
||||
+ (extension_lower == FILE_PATH_LITERAL("scf")) ||
|
||||
+ (extension_lower == FILE_PATH_LITERAL("url"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
diff --git a/content/browser/file_system_access/file_system_chooser_browsertest.cc b/content/browser/file_system_access/file_system_chooser_browsertest.cc
|
||||
index 9ea4db7807f6bbac799452fd138848b2a650d6fd..79dda31bd228e785d54e5486bb4417a75ee62b3a 100644
|
||||
--- a/content/browser/file_system_access/file_system_chooser_browsertest.cc
|
||||
+++ b/content/browser/file_system_access/file_system_chooser_browsertest.cc
|
||||
@@ -1556,13 +1556,21 @@ IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SuggestedName) {
|
||||
name_infos.push_back({"not_matching.jpg", ListValueOf(".txt"), false,
|
||||
"not_matching.jpg", false});
|
||||
|
||||
- // ".lnk", ".local", and ".scf" extensions should be sanitized.
|
||||
- name_infos.push_back({"dangerous_extension.local", ListValueOf(".local"),
|
||||
- true, "dangerous_extension.download", false});
|
||||
+ // ".lnk", ".local", ".scf", and ".url" extensions should be sanitized.
|
||||
name_infos.push_back({"dangerous_extension.lnk", ListValueOf(".lnk"), true,
|
||||
"dangerous_extension.download", false});
|
||||
+ name_infos.push_back({"dangerous_extension.lnk", ListValueOf(".LNK"), true,
|
||||
+ "dangerous_extension.download", false});
|
||||
+ name_infos.push_back({"dangerous_extension.LNK", ListValueOf(".lnk"), true,
|
||||
+ "dangerous_extension.download", false});
|
||||
+ name_infos.push_back({"dangerous_extension.LNK", ListValueOf(".LNK"), true,
|
||||
+ "dangerous_extension.download", false});
|
||||
+ name_infos.push_back({"dangerous_extension.local", ListValueOf(".local"),
|
||||
+ true, "dangerous_extension.download", false});
|
||||
name_infos.push_back({"dangerous_extension.scf", ListValueOf(".scf"), true,
|
||||
"dangerous_extension.download", false});
|
||||
+ name_infos.push_back({"dangerous_extension.url", ListValueOf(".url"), true,
|
||||
+ "dangerous_extension.download", false});
|
||||
// Compound extensions ending in a dangerous extension should be sanitized.
|
||||
name_infos.push_back({"dangerous_extension.png.local", ListValueOf(".local"),
|
||||
true, "dangerous_extension.png.download", false});
|
||||
@@ -1570,6 +1578,8 @@ IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SuggestedName) {
|
||||
true, "dangerous_extension.png.download", false});
|
||||
name_infos.push_back({"dangerous_extension.png.scf", ListValueOf(".scf"),
|
||||
true, "dangerous_extension.png.download", false});
|
||||
+ name_infos.push_back({"dangerous_extension.png.url", ListValueOf(".url"),
|
||||
+ true, "dangerous_extension.png.download", false});
|
||||
// Compound extensions not ending in a dangerous extension should not be
|
||||
// sanitized.
|
||||
name_infos.push_back({"dangerous_extension.local.png", ListValueOf(".png"),
|
||||
@@ -1578,6 +1588,8 @@ IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SuggestedName) {
|
||||
true, "dangerous_extension.lnk.png", true});
|
||||
name_infos.push_back({"dangerous_extension.scf.png", ListValueOf(".png"),
|
||||
true, "dangerous_extension.scf.png", true});
|
||||
+ name_infos.push_back({"dangerous_extension.url.png", ListValueOf(".png"),
|
||||
+ true, "dangerous_extension.url.png", true});
|
||||
// Invalid characters should be sanitized.
|
||||
name_infos.push_back({R"(inv*l:d\\ch%rבאמת!a<ters🤓.txt)",
|
||||
ListValueOf(".txt"), true,
|
||||
diff --git a/content/browser/file_system_access/file_system_chooser_unittest.cc b/content/browser/file_system_access/file_system_chooser_unittest.cc
|
||||
index 9b27d6305bd00a19d94b5ec49f21a7ecff7ddc48..3082c088f9733de9335437278c0d023954a953d9 100644
|
||||
--- a/content/browser/file_system_access/file_system_chooser_unittest.cc
|
||||
+++ b/content/browser/file_system_access/file_system_chooser_unittest.cc
|
||||
@@ -189,7 +189,7 @@ TEST_F(FileSystemChooserTest, IgnoreShellIntegratedExtensions) {
|
||||
accepts.emplace_back(blink::mojom::ChooseFileSystemEntryAcceptsOption::New(
|
||||
u"", std::vector<std::string>({}),
|
||||
std::vector<std::string>(
|
||||
- {"lnk", "foo.lnk", "foo.bar.local", "text", "local", "scf"})));
|
||||
+ {"lnk", "foo.lnk", "foo.bar.local", "text", "local", "scf", "url"})));
|
||||
SyncShowDialog(std::move(accepts), /*include_accepts_all=*/false);
|
||||
|
||||
ASSERT_TRUE(dialog_params.file_types);
|
||||
139
patches/chromium/cherry-pick-2782c7bc5bbe.patch
Normal file
139
patches/chromium/cherry-pick-2782c7bc5bbe.patch
Normal file
@@ -0,0 +1,139 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Marijn Kruisselbrink <mek@chromium.org>
|
||||
Date: Wed, 27 Apr 2022 20:51:50 +0000
|
||||
Subject: Reland "Close a MessagePort if it is created in a destroyed context."
|
||||
|
||||
This is a reland of commit 068f13cc5aa5f7a6e9faf28d8731275e64cb657b
|
||||
|
||||
This reland changes the timeout in the test from 3 to 2 seconds, because
|
||||
two 3 second timeouts is too long for chrome's default overall test
|
||||
timeout of 6 seconds on non-dcheck release builds.
|
||||
|
||||
Original change's description:
|
||||
> Close a MessagePort if it is created in a destroyed context.
|
||||
>
|
||||
> MessagePort assumes it is only destroyed either after ContextDestroyed,
|
||||
> or after the port has been closed explicitly. As it turns out ports that
|
||||
> were created in an already detached iframe would violate this invariant,
|
||||
> causing issues.
|
||||
>
|
||||
> Bug: 1228661
|
||||
> Change-Id: Ib1abce15f1d1d15f044de19fe0534767db488af0
|
||||
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3561845
|
||||
> Reviewed-by: Jeremy Roman <jbroman@chromium.org>
|
||||
> Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
|
||||
> Cr-Commit-Position: refs/heads/main@{#988859}
|
||||
|
||||
Bug: 1228661
|
||||
Change-Id: Ifc5ec866678667b0d81438e2a2c8e5ada6e19d8c
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3609249
|
||||
Commit-Queue: Jeremy Roman <jbroman@chromium.org>
|
||||
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
|
||||
Auto-Submit: Marijn Kruisselbrink <mek@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#996880}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/messaging/message_port.cc b/third_party/blink/renderer/core/messaging/message_port.cc
|
||||
index 6f67b21803fcdc2498ef207878d1541e04822fca..7f3df8ea567f91cd063122aab63a5c5424ab7919 100644
|
||||
--- a/third_party/blink/renderer/core/messaging/message_port.cc
|
||||
+++ b/third_party/blink/renderer/core/messaging/message_port.cc
|
||||
@@ -55,7 +55,11 @@
|
||||
namespace blink {
|
||||
|
||||
MessagePort::MessagePort(ExecutionContext& execution_context)
|
||||
- : ExecutionContextLifecycleObserver(&execution_context),
|
||||
+ : ExecutionContextLifecycleObserver(execution_context.IsContextDestroyed()
|
||||
+ ? nullptr
|
||||
+ : &execution_context),
|
||||
+ // Ports in a destroyed context start out in a closed state.
|
||||
+ closed_(execution_context.IsContextDestroyed()),
|
||||
task_runner_(execution_context.GetTaskRunner(TaskType::kPostedMessage)) {}
|
||||
|
||||
MessagePort::~MessagePort() {
|
||||
@@ -168,10 +172,21 @@ void MessagePort::Entangle(MessagePortDescriptor port) {
|
||||
DCHECK(port.IsValid());
|
||||
DCHECK(!connector_);
|
||||
|
||||
+ // If the context was already destroyed, there is no reason to actually
|
||||
+ // entangle the port and create a Connector. No messages will ever be able to
|
||||
+ // be sent or received anyway, as StartReceiving will never be called.
|
||||
+ if (!GetExecutionContext())
|
||||
+ return;
|
||||
+
|
||||
port_ = std::move(port);
|
||||
connector_ = std::make_unique<mojo::Connector>(
|
||||
port_.TakeHandleToEntangle(GetExecutionContext()),
|
||||
mojo::Connector::SINGLE_THREADED_SEND);
|
||||
+ // The raw `this` is safe despite `this` being a garbage collected object
|
||||
+ // because we make sure that:
|
||||
+ // 1. This object will not be garbage collected while it is connected and
|
||||
+ // the execution context is not destroyed, and
|
||||
+ // 2. when the execution context is destroyed, the connector_ is reset.
|
||||
connector_->set_incoming_receiver(this);
|
||||
connector_->set_connection_error_handler(
|
||||
WTF::Bind(&MessagePort::close, WrapWeakPersistent(this)));
|
||||
diff --git a/third_party/blink/renderer/core/messaging/message_port.h b/third_party/blink/renderer/core/messaging/message_port.h
|
||||
index 83d7901d99ad01ba039ea1ffa3dbee2595fc31ff..f9baba3c6d13992508da48a13c97bb10c8ec56e0 100644
|
||||
--- a/third_party/blink/renderer/core/messaging/message_port.h
|
||||
+++ b/third_party/blink/renderer/core/messaging/message_port.h
|
||||
@@ -148,7 +148,7 @@ class CORE_EXPORT MessagePort : public EventTargetWithInlineData,
|
||||
std::unique_ptr<mojo::Connector> connector_;
|
||||
|
||||
bool started_ = false;
|
||||
- bool closed_ = false;
|
||||
+ bool closed_;
|
||||
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
|
||||
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/webmessaging/message-channels/detached-iframe.window.js b/third_party/blink/web_tests/external/wpt/webmessaging/message-channels/detached-iframe.window.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..c1effaf141b7246320883e293b58dabbc3572123
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/webmessaging/message-channels/detached-iframe.window.js
|
||||
@@ -0,0 +1,47 @@
|
||||
+// META: title=MessageChannel in a detached iframe test
|
||||
+// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
|
||||
+// Pull in the with_iframe helper function from the service worker tests
|
||||
+
|
||||
+
|
||||
+const IframeAction = {
|
||||
+ REMOVE_BEFORE_CREATION: 'remove-before-creation',
|
||||
+ REMOVE_AFTER_CREATION: 'remove-after-creation',
|
||||
+};
|
||||
+
|
||||
+async function detached_frame_test(t, action) {
|
||||
+ const iframe = await with_iframe('about:blank');
|
||||
+ const iframe_MessageChannel = iframe.contentWindow.MessageChannel;
|
||||
+
|
||||
+ if (action === IframeAction.REMOVE_BEFORE_CREATION) {
|
||||
+ iframe.remove();
|
||||
+ }
|
||||
+
|
||||
+ (() => {
|
||||
+ const mc = new iframe_MessageChannel();
|
||||
+ mc.port1.postMessage("boo");
|
||||
+ mc.port2.onmessage = t.unreached_func("message event received");
|
||||
+ mc.port2.onmessageerror = t.unreached_func("message event received");
|
||||
+ })();
|
||||
+
|
||||
+ if (action === IframeAction.REMOVE_AFTER_CREATION) {
|
||||
+ iframe.remove();
|
||||
+ }
|
||||
+
|
||||
+ // TODO(https://github.com/web-platform-tests/wpt/issues/7899): Change to
|
||||
+ // some sort of cross-browser GC trigger.
|
||||
+ if (self.gc) self.gc();
|
||||
+
|
||||
+ // We are testing that neither of the above two events fire. We assume that a 2 second timeout
|
||||
+ // is good enough. We can't use any other API for an end condition because each MessagePort has
|
||||
+ // its own independent port message queue, which has no ordering guarantees relative to other
|
||||
+ // APIs.
|
||||
+ await new Promise(resolve => t.step_timeout(resolve, 2000));
|
||||
+}
|
||||
+
|
||||
+promise_test(async (t) => {
|
||||
+ return detached_frame_test(t, IframeAction.REMOVE_AFTER_CREATION);
|
||||
+}, 'MessageChannel created from a detached iframe should not send messages (remove after create)');
|
||||
+
|
||||
+promise_test(async (t) => {
|
||||
+ return detached_frame_test(t, IframeAction.REMOVE_BEFORE_CREATION);
|
||||
+}, 'MessageChannel created from a detached iframe should not send messages (remove before create)');
|
||||
102
patches/chromium/cherry-pick-3cbd5973d704.patch
Normal file
102
patches/chromium/cherry-pick-3cbd5973d704.patch
Normal file
@@ -0,0 +1,102 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
|
||||
Date: Mon, 18 Jul 2022 20:44:41 +0000
|
||||
Subject: Keep refptr to ServiceWorkerVersion in MaybeTimeoutRequest
|
||||
|
||||
The callback in ServiceWorkerVersion::MaybeTimeoutRequest may reduce the
|
||||
reference to the version object, which can be the last reference to it.
|
||||
In thet case, the object can be destroyed and the `inflight_requests_`
|
||||
field access after the callback become invalid.
|
||||
This CL keeps this to avoid the object destruction.
|
||||
|
||||
(cherry picked from commit 5926fa916d9ad53c77e31ee757e1979275d7466c)
|
||||
|
||||
Bug: 1339844
|
||||
Change-Id: I6564627bad0527dea007ca73261c5636dab56755
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3739330
|
||||
Commit-Queue: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
|
||||
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
|
||||
Reviewed-by: Sergei Glazunov <glazunov@google.com>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1023475}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3766241
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Owners-Override: Srinivas Sista <srinivassista@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5005@{#1263}
|
||||
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
|
||||
|
||||
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
|
||||
index 0b4b32d13db8958f579085a41bc5e20b26543f61..5797e3f954ae0769a91c8f259e19e50e2bf31a94 100644
|
||||
--- a/content/browser/service_worker/service_worker_version.cc
|
||||
+++ b/content/browser/service_worker/service_worker_version.cc
|
||||
@@ -1993,18 +1993,17 @@ void ServiceWorkerVersion::OnTimeoutTimer() {
|
||||
|
||||
MarkIfStale();
|
||||
|
||||
+ // Global `this` protecter.
|
||||
+ // callbacks initiated by this function sometimes reduce refcnt to 0
|
||||
+ // to make this instance freed.
|
||||
+ scoped_refptr<ServiceWorkerVersion> protect_this(this);
|
||||
+
|
||||
// Stopping the worker hasn't finished within a certain period.
|
||||
if (GetTickDuration(stop_time_) > kStopWorkerTimeout) {
|
||||
DCHECK_EQ(EmbeddedWorkerStatus::STOPPING, running_status());
|
||||
ReportError(blink::ServiceWorkerStatusCode::kErrorTimeout,
|
||||
"DETACH_STALLED_IN_STOPPING");
|
||||
|
||||
- // Detach the worker. Remove |this| as a listener first; otherwise
|
||||
- // OnStoppedInternal might try to restart before the new worker
|
||||
- // is created. Also, protect |this|, since swapping out the
|
||||
- // EmbeddedWorkerInstance could destroy our ServiceWorkerHost which could in
|
||||
- // turn destroy |this|.
|
||||
- scoped_refptr<ServiceWorkerVersion> protect_this(this);
|
||||
embedded_worker_->RemoveObserver(this);
|
||||
embedded_worker_->Detach();
|
||||
embedded_worker_ = std::make_unique<EmbeddedWorkerInstance>(this);
|
||||
@@ -2031,7 +2030,6 @@ void ServiceWorkerVersion::OnTimeoutTimer() {
|
||||
DCHECK(running_status() == EmbeddedWorkerStatus::STARTING ||
|
||||
running_status() == EmbeddedWorkerStatus::STOPPING)
|
||||
<< static_cast<int>(running_status());
|
||||
- scoped_refptr<ServiceWorkerVersion> protect(this);
|
||||
FinishStartWorker(blink::ServiceWorkerStatusCode::kErrorTimeout);
|
||||
if (running_status() == EmbeddedWorkerStatus::STARTING)
|
||||
embedded_worker_->Stop();
|
||||
@@ -2040,17 +2038,22 @@ void ServiceWorkerVersion::OnTimeoutTimer() {
|
||||
|
||||
// Requests have not finished before their expiration.
|
||||
bool stop_for_timeout = false;
|
||||
- auto timeout_iter = request_timeouts_.begin();
|
||||
- while (timeout_iter != request_timeouts_.end()) {
|
||||
+ std::set<InflightRequestTimeoutInfo> request_timeouts;
|
||||
+ request_timeouts.swap(request_timeouts_);
|
||||
+ auto timeout_iter = request_timeouts.begin();
|
||||
+ while (timeout_iter != request_timeouts.end()) {
|
||||
const InflightRequestTimeoutInfo& info = *timeout_iter;
|
||||
- if (!RequestExpired(info.expiration))
|
||||
+ if (!RequestExpired(info.expiration)) {
|
||||
break;
|
||||
+ }
|
||||
if (MaybeTimeoutRequest(info)) {
|
||||
stop_for_timeout =
|
||||
stop_for_timeout || info.timeout_behavior == KILL_ON_TIMEOUT;
|
||||
}
|
||||
- timeout_iter = request_timeouts_.erase(timeout_iter);
|
||||
+ timeout_iter = request_timeouts.erase(timeout_iter);
|
||||
}
|
||||
+ DCHECK(request_timeouts_.empty());
|
||||
+ request_timeouts_.swap(request_timeouts);
|
||||
if (stop_for_timeout && running_status() != EmbeddedWorkerStatus::STOPPING)
|
||||
embedded_worker_->Stop();
|
||||
|
||||
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
|
||||
index dcf0dfaf515f8058438d727b30fb4c508a7211a9..a88bc2513f988a00b538baa596a1e713c2600cf8 100644
|
||||
--- a/content/browser/service_worker/service_worker_version.h
|
||||
+++ b/content/browser/service_worker/service_worker_version.h
|
||||
@@ -863,6 +863,8 @@ class CONTENT_EXPORT ServiceWorkerVersion
|
||||
bool is_browser_startup_complete,
|
||||
blink::ServiceWorkerStatusCode status);
|
||||
|
||||
+ // The caller of MaybeTimeoutRequest must increase reference count of |this|
|
||||
+ // to avoid it deleted during the execution.
|
||||
bool MaybeTimeoutRequest(const InflightRequestTimeoutInfo& info);
|
||||
void SetAllRequestExpirations(const base::TimeTicks& expiration);
|
||||
|
||||
141
patches/chromium/cherry-pick-54e32332750c.patch
Normal file
141
patches/chromium/cherry-pick-54e32332750c.patch
Normal file
@@ -0,0 +1,141 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Min Qin <qinmin@chromium.org>
|
||||
Date: Tue, 12 Jul 2022 19:48:53 +0000
|
||||
Subject: Sanitize default file name in windows select file dialog
|
||||
|
||||
On windows, '%' is a special character and can be used for environment
|
||||
variables. So if the default file name is '%DATADIR%', it can actually
|
||||
refer to another directory and thus causing weird behaviors.
|
||||
And '%' cannot be escaped when used in the file dialog. Both "^%" and
|
||||
"%%" don't work. This CL mitigates the issue by replacing '%' with '_'.
|
||||
This only affects the default file name when showing the dialog. Power
|
||||
users can still change the file name by adding '%' if needed.
|
||||
|
||||
BUG=1308422
|
||||
|
||||
(cherry picked from commit 9cdce354cb3b0da5b4b311638973d407be7712b6)
|
||||
|
||||
Change-Id: Ibb275f5c3c2c9458c20d1e97ad527f7c95184eaa
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3688608
|
||||
Reviewed-by: Robert Liao <robliao@chromium.org>
|
||||
Commit-Queue: Min Qin <qinmin@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1014602}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3758469
|
||||
Cr-Commit-Position: refs/branch-heads/5112@{#822}
|
||||
Cr-Branched-From: b13d3fe7b3c47a56354ef54b221008afa754412e-refs/heads/main@{#1012729}
|
||||
|
||||
diff --git a/ui/shell_dialogs/execute_select_file_win.cc b/ui/shell_dialogs/execute_select_file_win.cc
|
||||
index 063d4c7c96cba45448d4d7b0e7a8ddd3a6f67937..162dbc3aeb4ead16a415228b40fb67008e89fe63 100644
|
||||
--- a/ui/shell_dialogs/execute_select_file_win.cc
|
||||
+++ b/ui/shell_dialogs/execute_select_file_win.cc
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "base/callback.h"
|
||||
#include "base/files/file.h"
|
||||
#include "base/files/file_util.h"
|
||||
+#include "base/strings/string_tokenizer.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/win/com_init_util.h"
|
||||
#include "base/win/registry.h"
|
||||
@@ -50,7 +51,7 @@ bool SetDefaultPath(IFileDialog* file_dialog,
|
||||
default_folder = default_path;
|
||||
} else {
|
||||
default_folder = default_path.DirName();
|
||||
- default_file_name = default_path.BaseName();
|
||||
+ default_file_name = GetSanitizedFileName(default_path.BaseName());
|
||||
}
|
||||
|
||||
// Do not fail the file dialog operation if the specified folder is invalid.
|
||||
@@ -371,6 +372,33 @@ std::wstring AppendExtensionIfNeeded(const std::wstring& filename,
|
||||
return return_value;
|
||||
}
|
||||
|
||||
+base::FilePath GetSanitizedFileName(const base::FilePath& file_name) {
|
||||
+ base::StringTokenizerT<std::wstring, std::wstring::const_iterator> t(
|
||||
+ file_name.value(), L"%");
|
||||
+ t.set_options(base::StringTokenizer::RETURN_EMPTY_TOKENS);
|
||||
+ std::wstring result;
|
||||
+ bool token_valid = t.GetNext();
|
||||
+ while (token_valid) {
|
||||
+ // Append substring before the first "%".
|
||||
+ result.append(t.token());
|
||||
+ // Done if we are reaching the end delimiter,
|
||||
+ if (!t.GetNext()) {
|
||||
+ break;
|
||||
+ }
|
||||
+ std::wstring string_after_first_percent = t.token();
|
||||
+ token_valid = t.GetNext();
|
||||
+ // If there are no other "%", append the string after
|
||||
+ // the first "%". Otherwise, remove the string between
|
||||
+ // the "%" and continue handing the remaining string.
|
||||
+ if (!token_valid) {
|
||||
+ result.append(L"%");
|
||||
+ result.append(string_after_first_percent);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ return base::FilePath(result);
|
||||
+}
|
||||
+
|
||||
void ExecuteSelectFile(
|
||||
SelectFileDialog::Type type,
|
||||
const std::u16string& title,
|
||||
diff --git a/ui/shell_dialogs/execute_select_file_win.h b/ui/shell_dialogs/execute_select_file_win.h
|
||||
index dc808af395333a2bd81c2bd73faac4c6063fd409..8fa96906c0288d0021c1500bd442a35c0c29dfd2 100644
|
||||
--- a/ui/shell_dialogs/execute_select_file_win.h
|
||||
+++ b/ui/shell_dialogs/execute_select_file_win.h
|
||||
@@ -26,6 +26,12 @@ SHELL_DIALOGS_EXPORT std::wstring AppendExtensionIfNeeded(
|
||||
const std::wstring& filter_selected,
|
||||
const std::wstring& suggested_ext);
|
||||
|
||||
+// Given a file name, return the sanitized version by removing substrings that
|
||||
+// are embedded in double '%' characters as those are reserved for environment
|
||||
+// variables. Implementation detail exported for unit tests.
|
||||
+SHELL_DIALOGS_EXPORT base::FilePath GetSanitizedFileName(
|
||||
+ const base::FilePath& file_name);
|
||||
+
|
||||
// Describes a filter for a file dialog.
|
||||
struct FileFilterSpec {
|
||||
// A human readable description of this filter. E.g. "HTML Files."
|
||||
diff --git a/ui/shell_dialogs/execute_select_file_win_unittest.cc b/ui/shell_dialogs/execute_select_file_win_unittest.cc
|
||||
index 8ea243924a1834dbe88dd53c5c6e8bde46d33b08..6237bf7d640e55929fb3c549674520f20adb0c94 100644
|
||||
--- a/ui/shell_dialogs/execute_select_file_win_unittest.cc
|
||||
+++ b/ui/shell_dialogs/execute_select_file_win_unittest.cc
|
||||
@@ -51,3 +51,38 @@ TEST(ShellDialogsWin, AppendExtensionIfNeeded) {
|
||||
test_cases[i].suggested_ext));
|
||||
}
|
||||
}
|
||||
+
|
||||
+TEST(ShellDialogsWin, GetSanitizedFileName) {
|
||||
+ struct GetSanitizedFileNameTestCase {
|
||||
+ const wchar_t* filename;
|
||||
+ const wchar_t* sanitized_filename;
|
||||
+ } test_cases[] = {
|
||||
+ {L"", L""},
|
||||
+ {L"a.txt", L"a.txt"},
|
||||
+
|
||||
+ // Only 1 "%" in file name.
|
||||
+ {L"%", L"%"},
|
||||
+ {L"%.txt", L"%.txt"},
|
||||
+ {L"ab%c.txt", L"ab%c.txt"},
|
||||
+ {L"abc.t%", L"abc.t%"},
|
||||
+
|
||||
+ // 2 "%" in file name.
|
||||
+ {L"%%", L""},
|
||||
+ {L"%c%", L""},
|
||||
+ {L"%c%d", L"d"},
|
||||
+ {L"d%c%.txt", L"d.txt"},
|
||||
+ {L"ab%c.t%", L"ab"},
|
||||
+ {L"abc.%t%", L"abc."},
|
||||
+
|
||||
+ // More than 2 "%" in file name.
|
||||
+ {L"%ab%c%.txt", L"c%.txt"},
|
||||
+ {L"%abc%.%txt%", L"."},
|
||||
+ {L"%ab%c%.%txt%", L"ctxt%"},
|
||||
+ };
|
||||
+
|
||||
+ for (size_t i = 0; i < std::size(test_cases); ++i) {
|
||||
+ SCOPED_TRACE(base::StringPrintf("i=%zu", i));
|
||||
+ EXPECT_EQ(base::FilePath(test_cases[i].sanitized_filename),
|
||||
+ ui::GetSanitizedFileName(base::FilePath(test_cases[i].filename)));
|
||||
+ }
|
||||
+}
|
||||
41
patches/chromium/cherry-pick-60d8559e150a.patch
Normal file
41
patches/chromium/cherry-pick-60d8559e150a.patch
Normal file
@@ -0,0 +1,41 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Lei Zhang <thestig@chromium.org>
|
||||
Date: Tue, 12 Jul 2022 18:52:14 +0000
|
||||
Subject: M104: Better define "first result" in PDFiumEngine::AddFindResult().
|
||||
|
||||
Currently, changing the PDF layout confuses AddFindResult() and causes
|
||||
it to fail a DCHECK(). Adjust AddFindResult() to avoid the failing
|
||||
DCHECK().
|
||||
|
||||
This is a cherry-pick of https://crrev.com/1021389 without the test
|
||||
changes.
|
||||
|
||||
Bug: 1339745
|
||||
Change-Id: I25c2b6b436700f9aeca4924fef662ad2909f0a8c
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3758626
|
||||
Reviewed-by: K. Moon <kmoon@chromium.org>
|
||||
Commit-Queue: Lei Zhang <thestig@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5112@{#820}
|
||||
Cr-Branched-From: b13d3fe7b3c47a56354ef54b221008afa754412e-refs/heads/main@{#1012729}
|
||||
|
||||
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
|
||||
index 0f8171dd0dd0b438a0406535e5693b22358d81e5..7d90add9e441451e3ae23dc5de4fa305819e0590 100644
|
||||
--- a/pdf/pdfium/pdfium_engine.cc
|
||||
+++ b/pdf/pdfium/pdfium_engine.cc
|
||||
@@ -1981,7 +1981,7 @@ void PDFiumEngine::SearchUsingICU(const std::u16string& term,
|
||||
}
|
||||
|
||||
void PDFiumEngine::AddFindResult(const PDFiumRange& result) {
|
||||
- bool first_result = find_results_.empty();
|
||||
+ bool first_result = find_results_.empty() && !resume_find_index_.has_value();
|
||||
// Figure out where to insert the new location, since we could have
|
||||
// started searching midway and now we wrapped.
|
||||
size_t result_index;
|
||||
@@ -1998,7 +1998,6 @@ void PDFiumEngine::AddFindResult(const PDFiumRange& result) {
|
||||
UpdateTickMarks();
|
||||
client_->NotifyNumberOfFindResultsChanged(find_results_.size(), false);
|
||||
if (first_result) {
|
||||
- DCHECK(!resume_find_index_);
|
||||
DCHECK(!current_find_index_);
|
||||
SelectFindResult(/*forward=*/true);
|
||||
}
|
||||
76
patches/chromium/cherry-pick-664e0d8b4cfb.patch
Normal file
76
patches/chromium/cherry-pick-664e0d8b4cfb.patch
Normal file
@@ -0,0 +1,76 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Etienne Bergeron <etienneb@chromium.org>
|
||||
Date: Tue, 19 Jul 2022 16:46:43 +0000
|
||||
Subject: Fix incorrect text itemization for \r codepoint
|
||||
|
||||
M96 merge issues:
|
||||
render_text_unittest.cc
|
||||
Tests Clusterfuzz_Issue_1298286/1299054 aren't present
|
||||
in M96 and caused a merge conflict.
|
||||
|
||||
The "\r" codepoint should be split to be rendered in a single
|
||||
harfbuzz run (same as "\n").
|
||||
|
||||
We do recognize these sequences as newline:
|
||||
\r
|
||||
\n
|
||||
\r\n
|
||||
|
||||
Previously, the itemization will leave the "\r" with the previous
|
||||
run. This is leading to incorrect multiline lines splitting.
|
||||
|
||||
(cherry picked from commit eee0c5ca752ad50df9986c551cb98226ce078893)
|
||||
|
||||
Bug: 1287804
|
||||
Change-Id: Idfc00a3cf147eb53258d5da9ea105e2d6dc25f05
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3708936
|
||||
Commit-Queue: Etienne Bergeron <etienneb@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1014955}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3726349
|
||||
Reviewed-by: Etienne Bergeron <etienneb@chromium.org>
|
||||
Commit-Queue: Roger Felipe Zanoni da Silva <rzanoni@google.com>
|
||||
Cr-Commit-Position: refs/branch-heads/4664@{#1662}
|
||||
Cr-Branched-From: 24dc4ee75e01a29d390d43c9c264372a169273a7-refs/heads/main@{#929512}
|
||||
|
||||
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
|
||||
index cd19053ad39a8386b348ae89fb2cf59bcfddb01a..cd779904454940f744cd82c5365ece7147633c2b 100644
|
||||
--- a/ui/gfx/render_text_harfbuzz.cc
|
||||
+++ b/ui/gfx/render_text_harfbuzz.cc
|
||||
@@ -201,7 +201,7 @@ GraphemeProperties RetrieveGraphemeProperties(const base::StringPiece16& text,
|
||||
properties.block = ublock_getCode(codepoint);
|
||||
}
|
||||
|
||||
- if (codepoint == '\n' || codepoint == ' ')
|
||||
+ if (codepoint == '\n' || codepoint == '\r' || codepoint == ' ')
|
||||
properties.has_control = true;
|
||||
if (IsBracket(codepoint))
|
||||
properties.has_bracket = true;
|
||||
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
|
||||
index 43d03699495c935cd1203e8195d12571fa92b347..e33d25270efb49ee8498108dfa0473b4f760d732 100644
|
||||
--- a/ui/gfx/render_text_unittest.cc
|
||||
+++ b/ui/gfx/render_text_unittest.cc
|
||||
@@ -1575,6 +1575,9 @@ const RunListCase kBasicsRunListCases[] = {
|
||||
{"multiline_newline1", u"\n\n", "[0][1]", true},
|
||||
{"multiline_newline2", u"\r\n\r\n", "[0->1][2->3]", true},
|
||||
{"multiline_newline3", u"\r\r\n", "[0][1->2]", true},
|
||||
+ {"multiline_newline4", u"x\r\r", "[0][1][2]", true},
|
||||
+ {"multiline_newline5", u"x\n\r\r", "[0][1][2][3]", true},
|
||||
+ {"multiline_newline6", u"x\ny\rz\r\n", "[0][1][2][3][4][5->6]", true},
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ItemizeTextToRunsBasics,
|
||||
@@ -8561,4 +8564,14 @@ TEST_F(RenderTextTest, StringSizeUpdatedWhenDeviceScaleFactorChanges) {
|
||||
}
|
||||
#endif
|
||||
|
||||
+TEST_F(RenderTextTest, Clusterfuzz_Issue_1287804) {
|
||||
+ RenderText* render_text = GetRenderText();
|
||||
+ render_text->SetMaxLines(1);
|
||||
+ render_text->SetText(u">\r\r");
|
||||
+ render_text->SetMultiline(true);
|
||||
+ render_text->SetDisplayRect(Rect(0, 0, 100, 24));
|
||||
+ render_text->SetElideBehavior(ELIDE_TAIL);
|
||||
+ EXPECT_EQ(RangeF(0, 0), render_text->GetCursorSpan(Range(0, 0)));
|
||||
+}
|
||||
+
|
||||
} // namespace gfx
|
||||
41
patches/chromium/cherry-pick-902f0d144a5b.patch
Normal file
41
patches/chromium/cherry-pick-902f0d144a5b.patch
Normal file
@@ -0,0 +1,41 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Justin Novosad <junov@chromium.org>
|
||||
Date: Fri, 15 Jul 2022 23:12:50 +0000
|
||||
Subject: Mitigate bad cast in OffscreenCanvas::GetFontSelector
|
||||
|
||||
This change will cause the browser to crash if the execution context
|
||||
is not a Window or WorkerGlobalScope. This is a temporary solution
|
||||
to handle the case where the execution context is an
|
||||
AudioWorkletGlobalScope. The longer term solution, which will be
|
||||
implemented in a follow-up CL, is to block OffscreenCanvas objects from
|
||||
being transferred to AudioWorklets, as required by the postMessage spec.
|
||||
|
||||
BUG=1334864
|
||||
|
||||
(cherry picked from commit 028c11e59fd41bc22eff06dbec10fe9b0e82bd04)
|
||||
|
||||
Change-Id: Ief5e37eca6dff14098b12cdbe6fc362c3dd87d1d
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3722117
|
||||
Auto-Submit: Justin Novosad <junov@chromium.org>
|
||||
Reviewed-by: Juanmi Huertas <juanmihd@chromium.org>
|
||||
Commit-Queue: Juanmi Huertas <juanmihd@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1017357}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3752921
|
||||
Commit-Queue: Srinivas Sista <srinivassista@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5005@{#1254}
|
||||
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
|
||||
index 584920aad8a3bfd9f00bf0db99fdf1891b13176b..4625aec940a349e94f591e2b96801c99dcb3ef05 100644
|
||||
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
|
||||
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
|
||||
@@ -548,6 +548,9 @@ FontSelector* OffscreenCanvas::GetFontSelector() {
|
||||
if (auto* window = DynamicTo<LocalDOMWindow>(GetExecutionContext())) {
|
||||
return window->document()->GetStyleEngine().GetFontSelector();
|
||||
}
|
||||
+ // TODO(crbug.com/1334864): Temporary mitigation. Remove the following
|
||||
+ // CHECK once a more comprehensive solution has been implemented.
|
||||
+ CHECK(GetExecutionContext()->IsWorkerGlobalScope());
|
||||
return To<WorkerGlobalScope>(GetExecutionContext())->GetFontSelector();
|
||||
}
|
||||
|
||||
393
patches/chromium/cherry-pick-919b1ffe1fe7.patch
Normal file
393
patches/chromium/cherry-pick-919b1ffe1fe7.patch
Normal file
@@ -0,0 +1,393 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Alex Rudenko <alexrudenko@chromium.org>
|
||||
Date: Tue, 26 Apr 2022 05:53:56 +0000
|
||||
Subject: DevTools: store weak references to nodes in inspect_tools
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This CL changes how the configuration for persistent overlays is stored.
|
||||
Instead of trying to save a strong reference to a DOM node, we can
|
||||
store weak references instead. This requires using a HeapHashMap as
|
||||
vectors don't support weak reference elements.
|
||||
|
||||
(cherry picked from commit 2e0a0b83e1340e948a27cdc319bea8f32849d414)
|
||||
|
||||
Fixed: 1314310
|
||||
Change-Id: I27ce12730d7598bc84d01adf421923af9a53dc67
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3593092
|
||||
Reviewed-by: Danil Somsikov <dsv@chromium.org>
|
||||
Reviewed-by: Changhao Han <changhaohan@chromium.org>
|
||||
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
|
||||
Commit-Queue: Alex Rudenko <alexrudenko@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#994866}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3604262
|
||||
Auto-Submit: Alex Rudenko <alexrudenko@chromium.org>
|
||||
Reviewed-by: Simon Zünd <szuend@chromium.org>
|
||||
Commit-Queue: Simon Zünd <szuend@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5005@{#163}
|
||||
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/inspector/inspect_tools.cc b/third_party/blink/renderer/core/inspector/inspect_tools.cc
|
||||
index b3c08bd77e792481ee7ce5754e48a0d429ef0595..6a43cc041e1adefcfca56dac86eb138c0b118127 100644
|
||||
--- a/third_party/blink/renderer/core/inspector/inspect_tools.cc
|
||||
+++ b/third_party/blink/renderer/core/inspector/inspect_tools.cc
|
||||
@@ -477,14 +477,14 @@ bool PersistentTool::HideOnMouseMove() {
|
||||
void PersistentTool::Draw(float scale) {
|
||||
for (auto& entry : grid_node_highlights_) {
|
||||
std::unique_ptr<protocol::Value> highlight =
|
||||
- InspectorGridHighlight(entry.first.Get(), *(entry.second));
|
||||
+ InspectorGridHighlight(entry.key, *(entry.value));
|
||||
if (!highlight)
|
||||
continue;
|
||||
overlay_->EvaluateInOverlay("drawGridHighlight", std::move(highlight));
|
||||
}
|
||||
for (auto& entry : flex_container_configs_) {
|
||||
std::unique_ptr<protocol::Value> highlight =
|
||||
- InspectorFlexContainerHighlight(entry.first.Get(), *(entry.second));
|
||||
+ InspectorFlexContainerHighlight(entry.key, *(entry.value));
|
||||
if (!highlight)
|
||||
continue;
|
||||
overlay_->EvaluateInOverlay("drawFlexContainerHighlight",
|
||||
@@ -492,7 +492,7 @@ void PersistentTool::Draw(float scale) {
|
||||
}
|
||||
for (auto& entry : scroll_snap_configs_) {
|
||||
std::unique_ptr<protocol::Value> highlight =
|
||||
- InspectorScrollSnapHighlight(entry.first.Get(), *(entry.second));
|
||||
+ InspectorScrollSnapHighlight(entry.key, *(entry.value));
|
||||
if (!highlight)
|
||||
continue;
|
||||
overlay_->EvaluateInOverlay("drawScrollSnapHighlight",
|
||||
@@ -500,17 +500,15 @@ void PersistentTool::Draw(float scale) {
|
||||
}
|
||||
for (auto& entry : container_query_configs_) {
|
||||
std::unique_ptr<protocol::Value> highlight =
|
||||
- InspectorContainerQueryHighlight(entry.first.Get(), *(entry.second));
|
||||
+ InspectorContainerQueryHighlight(entry.key, *(entry.value));
|
||||
if (!highlight)
|
||||
continue;
|
||||
overlay_->EvaluateInOverlay("drawContainerQueryHighlight",
|
||||
std::move(highlight));
|
||||
}
|
||||
- for (wtf_size_t i = 0; i < isolated_element_configs_.size(); ++i) {
|
||||
- auto& entry = isolated_element_configs_.at(i);
|
||||
+ for (auto& entry : isolated_element_configs_) {
|
||||
std::unique_ptr<protocol::Value> highlight =
|
||||
- InspectorIsolatedElementHighlight(entry.first.Get(), *(entry.second),
|
||||
- i);
|
||||
+ InspectorIsolatedElementHighlight(entry.key, *(entry.value));
|
||||
if (!highlight)
|
||||
continue;
|
||||
overlay_->EvaluateInOverlay("drawIsolatedElementHighlight",
|
||||
@@ -548,9 +546,11 @@ void PersistentTool::Dispatch(const ScriptValue& message,
|
||||
|
||||
Element* element = nullptr;
|
||||
if (highlight_type == "isolatedElement") {
|
||||
- if (index < static_cast<int>(isolated_element_configs_.size()) &&
|
||||
- index >= 0) {
|
||||
- element = isolated_element_configs_.at(index).first.Get();
|
||||
+ for (auto& entry : isolated_element_configs_) {
|
||||
+ if (entry.value->highlight_index == index) {
|
||||
+ element = entry.key;
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -571,7 +571,7 @@ PersistentTool::GetGridInspectorHighlightsAsJson() const {
|
||||
protocol::ListValue::create();
|
||||
for (auto& entry : grid_node_highlights_) {
|
||||
std::unique_ptr<protocol::Value> highlight =
|
||||
- InspectorGridHighlight(entry.first.Get(), *(entry.second));
|
||||
+ InspectorGridHighlight(entry.key, *(entry.value));
|
||||
if (!highlight)
|
||||
continue;
|
||||
highlights->pushValue(std::move(highlight));
|
||||
@@ -584,6 +584,15 @@ PersistentTool::GetGridInspectorHighlightsAsJson() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
+void PersistentTool::Trace(Visitor* visitor) const {
|
||||
+ InspectTool::Trace(visitor);
|
||||
+ visitor->Trace(grid_node_highlights_);
|
||||
+ visitor->Trace(flex_container_configs_);
|
||||
+ visitor->Trace(scroll_snap_configs_);
|
||||
+ visitor->Trace(container_query_configs_);
|
||||
+ visitor->Trace(isolated_element_configs_);
|
||||
+}
|
||||
+
|
||||
// SourceOrderTool -----------------------------------------------------------
|
||||
|
||||
SourceOrderTool::SourceOrderTool(
|
||||
diff --git a/third_party/blink/renderer/core/inspector/inspect_tools.h b/third_party/blink/renderer/core/inspector/inspect_tools.h
|
||||
index dd55eacf154da0895e04b6113d28e52085dcff6d..92abdca382bd8edd95714be6194eddc84bee7e06 100644
|
||||
--- a/third_party/blink/renderer/core/inspector/inspect_tools.h
|
||||
+++ b/third_party/blink/renderer/core/inspector/inspect_tools.h
|
||||
@@ -145,23 +145,20 @@ class SourceOrderTool : public InspectTool {
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
-
|
||||
-using GridConfigs = Vector<
|
||||
- std::pair<Member<Node>, std::unique_ptr<InspectorGridHighlightConfig>>>;
|
||||
+using GridConfigs = HeapHashMap<WeakMember<Node>,
|
||||
+ std::unique_ptr<InspectorGridHighlightConfig>>;
|
||||
using FlexContainerConfigs =
|
||||
- Vector<std::pair<Member<Node>,
|
||||
- std::unique_ptr<InspectorFlexContainerHighlightConfig>>>;
|
||||
-using ScrollSnapConfigs = Vector<
|
||||
- std::pair<Member<Node>,
|
||||
- std::unique_ptr<InspectorScrollSnapContainerHighlightConfig>>>;
|
||||
-
|
||||
-using ContainerQueryConfigs = Vector<std::pair<
|
||||
- Member<Node>,
|
||||
- std::unique_ptr<InspectorContainerQueryContainerHighlightConfig>>>;
|
||||
-
|
||||
+ HeapHashMap<WeakMember<Node>,
|
||||
+ std::unique_ptr<InspectorFlexContainerHighlightConfig>>;
|
||||
+using ScrollSnapConfigs =
|
||||
+ HeapHashMap<WeakMember<Node>,
|
||||
+ std::unique_ptr<InspectorScrollSnapContainerHighlightConfig>>;
|
||||
+using ContainerQueryConfigs = HeapHashMap<
|
||||
+ WeakMember<Node>,
|
||||
+ std::unique_ptr<InspectorContainerQueryContainerHighlightConfig>>;
|
||||
using IsolatedElementConfigs =
|
||||
- Vector<std::pair<Member<Element>,
|
||||
- std::unique_ptr<InspectorIsolationModeHighlightConfig>>>;
|
||||
+ HeapHashMap<WeakMember<Element>,
|
||||
+ std::unique_ptr<InspectorIsolationModeHighlightConfig>>;
|
||||
|
||||
class PersistentTool : public InspectTool {
|
||||
using InspectTool::InspectTool;
|
||||
@@ -181,6 +178,8 @@ class PersistentTool : public InspectTool {
|
||||
std::unique_ptr<protocol::DictionaryValue> GetGridInspectorHighlightsAsJson()
|
||||
const;
|
||||
|
||||
+ void Trace(Visitor* visitor) const override;
|
||||
+
|
||||
private:
|
||||
bool ForwardEventsToOverlay() override;
|
||||
bool HideOnMouseMove() override;
|
||||
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
|
||||
index 3ad3081f4f6143c1b38fa1ffc5892eb810344d93..1abb20541977adcece43f7e61db8d34f5170e6c6 100644
|
||||
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
|
||||
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
|
||||
@@ -2415,8 +2415,7 @@ std::unique_ptr<protocol::DictionaryValue> InspectorContainerQueryHighlight(
|
||||
|
||||
std::unique_ptr<protocol::DictionaryValue> InspectorIsolatedElementHighlight(
|
||||
Element* element,
|
||||
- const InspectorIsolationModeHighlightConfig& config,
|
||||
- int highlight_index) {
|
||||
+ const InspectorIsolationModeHighlightConfig& config) {
|
||||
LocalFrameView* frame_view = element->GetDocument().View();
|
||||
if (!frame_view)
|
||||
return nullptr;
|
||||
@@ -2428,7 +2427,7 @@ std::unique_ptr<protocol::DictionaryValue> InspectorIsolatedElementHighlight(
|
||||
if (!isolated_element_info)
|
||||
return nullptr;
|
||||
|
||||
- isolated_element_info->setInteger("highlightIndex", highlight_index);
|
||||
+ isolated_element_info->setInteger("highlightIndex", config.highlight_index);
|
||||
return isolated_element_info;
|
||||
}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.h b/third_party/blink/renderer/core/inspector/inspector_highlight.h
|
||||
index ba4e3f9487b05140aef0068bbafc209a2047235b..4c6423063a4f3ee4ace988fd4e45b41592308c03 100644
|
||||
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.h
|
||||
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.h
|
||||
@@ -145,6 +145,7 @@ struct CORE_EXPORT InspectorIsolationModeHighlightConfig {
|
||||
Color resizer_color;
|
||||
Color resizer_handle_color;
|
||||
Color mask_color;
|
||||
+ int highlight_index = 0;
|
||||
};
|
||||
|
||||
struct CORE_EXPORT InspectorHighlightConfig {
|
||||
@@ -300,8 +301,7 @@ std::unique_ptr<protocol::DictionaryValue> InspectorContainerQueryHighlight(
|
||||
|
||||
std::unique_ptr<protocol::DictionaryValue> InspectorIsolatedElementHighlight(
|
||||
Element* element,
|
||||
- const InspectorIsolationModeHighlightConfig& config,
|
||||
- int highlight_index);
|
||||
+ const InspectorIsolationModeHighlightConfig& config);
|
||||
|
||||
// CORE_EXPORT is required to make these functions available for unit tests.
|
||||
std::unique_ptr<protocol::DictionaryValue> CORE_EXPORT
|
||||
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
|
||||
index 6bdf410f2d7606ebc946dba903ed6094d8c7cfb4..17ba8a9fc32d6d8d4573aec0d352889dce8fe15e 100644
|
||||
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
|
||||
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
|
||||
@@ -707,7 +707,7 @@ Response InspectorOverlayAgent::setShowGridOverlays(
|
||||
MakeGarbageCollected<PersistentTool>(this, GetFrontend());
|
||||
}
|
||||
|
||||
- Vector<std::pair<Member<Node>, std::unique_ptr<InspectorGridHighlightConfig>>>
|
||||
+ HeapHashMap<WeakMember<Node>, std::unique_ptr<InspectorGridHighlightConfig>>
|
||||
configs;
|
||||
for (std::unique_ptr<protocol::Overlay::GridNodeHighlightConfig>& config :
|
||||
*grid_node_highlight_configs) {
|
||||
@@ -715,9 +715,8 @@ Response InspectorOverlayAgent::setShowGridOverlays(
|
||||
Response response = dom_agent_->AssertNode(config->getNodeId(), node);
|
||||
if (!response.IsSuccess())
|
||||
return response;
|
||||
- configs.push_back(
|
||||
- std::make_pair(node, InspectorOverlayAgent::ToGridHighlightConfig(
|
||||
- config->getGridHighlightConfig())));
|
||||
+ configs.insert(node, InspectorOverlayAgent::ToGridHighlightConfig(
|
||||
+ config->getGridHighlightConfig()));
|
||||
}
|
||||
|
||||
persistent_tool_->SetGridConfigs(std::move(configs));
|
||||
@@ -735,8 +734,8 @@ Response InspectorOverlayAgent::setShowFlexOverlays(
|
||||
MakeGarbageCollected<PersistentTool>(this, GetFrontend());
|
||||
}
|
||||
|
||||
- Vector<std::pair<Member<Node>,
|
||||
- std::unique_ptr<InspectorFlexContainerHighlightConfig>>>
|
||||
+ HeapHashMap<WeakMember<Node>,
|
||||
+ std::unique_ptr<InspectorFlexContainerHighlightConfig>>
|
||||
configs;
|
||||
|
||||
for (std::unique_ptr<protocol::Overlay::FlexNodeHighlightConfig>& config :
|
||||
@@ -745,9 +744,8 @@ Response InspectorOverlayAgent::setShowFlexOverlays(
|
||||
Response response = dom_agent_->AssertNode(config->getNodeId(), node);
|
||||
if (!response.IsSuccess())
|
||||
return response;
|
||||
- configs.push_back(std::make_pair(
|
||||
- node, InspectorOverlayAgent::ToFlexContainerHighlightConfig(
|
||||
- config->getFlexContainerHighlightConfig())));
|
||||
+ configs.insert(node, InspectorOverlayAgent::ToFlexContainerHighlightConfig(
|
||||
+ config->getFlexContainerHighlightConfig()));
|
||||
}
|
||||
|
||||
persistent_tool_->SetFlexContainerConfigs(std::move(configs));
|
||||
@@ -766,9 +764,8 @@ Response InspectorOverlayAgent::setShowScrollSnapOverlays(
|
||||
MakeGarbageCollected<PersistentTool>(this, GetFrontend());
|
||||
}
|
||||
|
||||
- Vector<
|
||||
- std::pair<Member<Node>,
|
||||
- std::unique_ptr<InspectorScrollSnapContainerHighlightConfig>>>
|
||||
+ HeapHashMap<WeakMember<Node>,
|
||||
+ std::unique_ptr<InspectorScrollSnapContainerHighlightConfig>>
|
||||
configs;
|
||||
|
||||
for (std::unique_ptr<protocol::Overlay::ScrollSnapHighlightConfig>& config :
|
||||
@@ -777,9 +774,9 @@ Response InspectorOverlayAgent::setShowScrollSnapOverlays(
|
||||
Response response = dom_agent_->AssertNode(config->getNodeId(), node);
|
||||
if (!response.IsSuccess())
|
||||
return response;
|
||||
- configs.push_back(std::make_pair(
|
||||
- node, InspectorOverlayAgent::ToScrollSnapContainerHighlightConfig(
|
||||
- config->getScrollSnapContainerHighlightConfig())));
|
||||
+ configs.insert(node,
|
||||
+ InspectorOverlayAgent::ToScrollSnapContainerHighlightConfig(
|
||||
+ config->getScrollSnapContainerHighlightConfig()));
|
||||
}
|
||||
|
||||
persistent_tool_->SetScrollSnapConfigs(std::move(configs));
|
||||
@@ -798,9 +795,8 @@ Response InspectorOverlayAgent::setShowContainerQueryOverlays(
|
||||
MakeGarbageCollected<PersistentTool>(this, GetFrontend());
|
||||
}
|
||||
|
||||
- Vector<std::pair<
|
||||
- Member<Node>,
|
||||
- std::unique_ptr<InspectorContainerQueryContainerHighlightConfig>>>
|
||||
+ HeapHashMap<WeakMember<Node>,
|
||||
+ std::unique_ptr<InspectorContainerQueryContainerHighlightConfig>>
|
||||
configs;
|
||||
|
||||
for (std::unique_ptr<protocol::Overlay::ContainerQueryHighlightConfig>&
|
||||
@@ -809,9 +805,9 @@ Response InspectorOverlayAgent::setShowContainerQueryOverlays(
|
||||
Response response = dom_agent_->AssertNode(config->getNodeId(), node);
|
||||
if (!response.IsSuccess())
|
||||
return response;
|
||||
- configs.push_back(std::make_pair(
|
||||
+ configs.insert(
|
||||
node, InspectorOverlayAgent::ToContainerQueryContainerHighlightConfig(
|
||||
- config->getContainerQueryContainerHighlightConfig())));
|
||||
+ config->getContainerQueryContainerHighlightConfig()));
|
||||
}
|
||||
|
||||
persistent_tool_->SetContainerQueryConfigs(std::move(configs));
|
||||
@@ -830,10 +826,11 @@ Response InspectorOverlayAgent::setShowIsolatedElements(
|
||||
MakeGarbageCollected<PersistentTool>(this, GetFrontend());
|
||||
}
|
||||
|
||||
- Vector<std::pair<Member<Element>,
|
||||
- std::unique_ptr<InspectorIsolationModeHighlightConfig>>>
|
||||
+ HeapHashMap<WeakMember<Element>,
|
||||
+ std::unique_ptr<InspectorIsolationModeHighlightConfig>>
|
||||
configs;
|
||||
|
||||
+ int idx = 0;
|
||||
for (std::unique_ptr<protocol::Overlay::IsolatedElementHighlightConfig>&
|
||||
config : *isolated_element_highlight_configs) {
|
||||
Element* element = nullptr;
|
||||
@@ -841,9 +838,10 @@ Response InspectorOverlayAgent::setShowIsolatedElements(
|
||||
Response response = dom_agent_->AssertElement(config->getNodeId(), element);
|
||||
if (!response.IsSuccess())
|
||||
return response;
|
||||
- configs.push_back(std::make_pair(
|
||||
- element, InspectorOverlayAgent::ToIsolationModeHighlightConfig(
|
||||
- config->getIsolationModeHighlightConfig())));
|
||||
+ configs.insert(element,
|
||||
+ InspectorOverlayAgent::ToIsolationModeHighlightConfig(
|
||||
+ config->getIsolationModeHighlightConfig(), idx));
|
||||
+ idx++;
|
||||
}
|
||||
|
||||
persistent_tool_->SetIsolatedElementConfigs(std::move(configs));
|
||||
@@ -947,16 +945,16 @@ Response InspectorOverlayAgent::getGridHighlightObjectsForTest(
|
||||
std::unique_ptr<protocol::Array<int>> node_ids,
|
||||
std::unique_ptr<protocol::DictionaryValue>* highlights) {
|
||||
PersistentTool persistent_tool(this, GetFrontend());
|
||||
- Vector<std::pair<Member<Node>, std::unique_ptr<InspectorGridHighlightConfig>>>
|
||||
+
|
||||
+ HeapHashMap<WeakMember<Node>, std::unique_ptr<InspectorGridHighlightConfig>>
|
||||
configs;
|
||||
for (const int node_id : *node_ids) {
|
||||
Node* node = nullptr;
|
||||
Response response = dom_agent_->AssertNode(node_id, node);
|
||||
if (!response.IsSuccess())
|
||||
return response;
|
||||
- configs.push_back(
|
||||
- std::make_pair(node, std::make_unique<InspectorGridHighlightConfig>(
|
||||
- InspectorHighlight::DefaultGridConfig())));
|
||||
+ configs.insert(node, std::make_unique<InspectorGridHighlightConfig>(
|
||||
+ InspectorHighlight::DefaultGridConfig()));
|
||||
}
|
||||
persistent_tool.SetGridConfigs(std::move(configs));
|
||||
*highlights = persistent_tool.GetGridInspectorHighlightsAsJson();
|
||||
@@ -1748,7 +1746,8 @@ InspectorOverlayAgent::ToFlexItemHighlightConfig(
|
||||
// static
|
||||
std::unique_ptr<InspectorIsolationModeHighlightConfig>
|
||||
InspectorOverlayAgent::ToIsolationModeHighlightConfig(
|
||||
- protocol::Overlay::IsolationModeHighlightConfig* config) {
|
||||
+ protocol::Overlay::IsolationModeHighlightConfig* config,
|
||||
+ int idx) {
|
||||
if (!config) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1760,6 +1759,7 @@ InspectorOverlayAgent::ToIsolationModeHighlightConfig(
|
||||
InspectorDOMAgent::ParseColor(config->getResizerHandleColor(nullptr));
|
||||
highlight_config->mask_color =
|
||||
InspectorDOMAgent::ParseColor(config->getMaskColor(nullptr));
|
||||
+ highlight_config->highlight_index = idx;
|
||||
|
||||
return highlight_config;
|
||||
}
|
||||
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
|
||||
index 00226ea419fbd6c8004635c5adf205e8e1bbbd4e..7cb62900eedcb7c6de9666a92b701a57ec31f52d 100644
|
||||
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
|
||||
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
|
||||
@@ -161,7 +161,8 @@ class CORE_EXPORT InspectorOverlayAgent final
|
||||
ToFlexItemHighlightConfig(protocol::Overlay::FlexItemHighlightConfig*);
|
||||
static std::unique_ptr<InspectorIsolationModeHighlightConfig>
|
||||
ToIsolationModeHighlightConfig(
|
||||
- protocol::Overlay::IsolationModeHighlightConfig*);
|
||||
+ protocol::Overlay::IsolationModeHighlightConfig*,
|
||||
+ int highlight_index);
|
||||
static absl::optional<LineStyle> ToLineStyle(protocol::Overlay::LineStyle*);
|
||||
static absl::optional<BoxStyle> ToBoxStyle(protocol::Overlay::BoxStyle*);
|
||||
static std::unique_ptr<InspectorHighlightConfig> ToHighlightConfig(
|
||||
414
patches/chromium/cherry-pick-94a8bdafc8c6.patch
Normal file
414
patches/chromium/cherry-pick-94a8bdafc8c6.patch
Normal file
@@ -0,0 +1,414 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Keren Zhu <kerenzhu@chromium.org>
|
||||
Date: Thu, 9 Jun 2022 14:50:26 +0000
|
||||
Subject: Fix use-after-free vulnerability in ComboboxModel
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Stop the Combobox from observing the model when the model gets
|
||||
destroyed. This is done by adding a virtual OnComboboxModelDestroying()
|
||||
to the base observer class and calling it in ~ComboboxModel().
|
||||
|
||||
This fix will prevent the use-after-free that happens when the combobox outlives the model, usually because the model lifetime
|
||||
is managed by non-UI component.
|
||||
|
||||
Bug: 1264288
|
||||
Change-Id: Ia00881a9b674bbc83bbf54dd228490c1cc1290bc
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3693825
|
||||
Commit-Queue: Keren Zhu <kerenzhu@chromium.org>
|
||||
Reviewed-by: Matthias Körber <koerber@google.com>
|
||||
Auto-Submit: Keren Zhu <kerenzhu@chromium.org>
|
||||
Reviewed-by: Peter Boström <pbos@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1012504}
|
||||
|
||||
diff --git a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
|
||||
index 323f85651611ff98e45884e3b0b7211eaa25039b..0d0bb0d21b1d96841a30bfea336af918b6da83f8 100644
|
||||
--- a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
|
||||
+++ b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
|
||||
@@ -130,16 +130,6 @@ int RecentlyUsedFoldersComboModel::GetDefaultIndex() const {
|
||||
return it == items_.end() ? 0 : static_cast<int>(it - items_.begin());
|
||||
}
|
||||
|
||||
-void RecentlyUsedFoldersComboModel::AddObserver(
|
||||
- ui::ComboboxModelObserver* observer) {
|
||||
- observers_.AddObserver(observer);
|
||||
-}
|
||||
-
|
||||
-void RecentlyUsedFoldersComboModel::RemoveObserver(
|
||||
- ui::ComboboxModelObserver* observer) {
|
||||
- observers_.RemoveObserver(observer);
|
||||
-}
|
||||
-
|
||||
void RecentlyUsedFoldersComboModel::BookmarkModelLoaded(BookmarkModel* model,
|
||||
bool ids_reassigned) {}
|
||||
|
||||
@@ -176,7 +166,7 @@ void RecentlyUsedFoldersComboModel::OnWillRemoveBookmarks(
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
- for (ui::ComboboxModelObserver& observer : observers_)
|
||||
+ for (ui::ComboboxModelObserver& observer : observers())
|
||||
observer.OnComboboxModelChanged(this);
|
||||
}
|
||||
}
|
||||
@@ -219,7 +209,7 @@ void RecentlyUsedFoldersComboModel::BookmarkAllUserNodesRemoved(
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
- for (ui::ComboboxModelObserver& observer : observers_)
|
||||
+ for (ui::ComboboxModelObserver& observer : observers())
|
||||
observer.OnComboboxModelChanged(this);
|
||||
}
|
||||
}
|
||||
diff --git a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h
|
||||
index 8682e750f4407e615264986024e2a7dd0bae038f..0c02c685a02875c7b91ca9e6228864bc04e45c10 100644
|
||||
--- a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h
|
||||
+++ b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h
|
||||
@@ -38,8 +38,6 @@ class RecentlyUsedFoldersComboModel : public ui::ComboboxModel,
|
||||
std::u16string GetItemAt(int index) const override;
|
||||
bool IsItemSeparatorAt(int index) const override;
|
||||
int GetDefaultIndex() const override;
|
||||
- void AddObserver(ui::ComboboxModelObserver* observer) override;
|
||||
- void RemoveObserver(ui::ComboboxModelObserver* observer) override;
|
||||
|
||||
// Overriden from bookmarks::BookmarkModelObserver:
|
||||
void BookmarkModelLoaded(bookmarks::BookmarkModel* model,
|
||||
@@ -90,8 +88,6 @@ class RecentlyUsedFoldersComboModel : public ui::ComboboxModel,
|
||||
const raw_ptr<bookmarks::BookmarkModel> bookmark_model_;
|
||||
|
||||
const raw_ptr<const bookmarks::BookmarkNode> parent_node_;
|
||||
-
|
||||
- base::ObserverList<ui::ComboboxModelObserver> observers_;
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_UI_BOOKMARKS_RECENTLY_USED_FOLDERS_COMBO_MODEL_H_
|
||||
diff --git a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model_unittest.cc b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model_unittest.cc
|
||||
index 60455386b7d5f1da72c92424ccf95fd971e7590a..3bfe6852b8a54a53aafea5eeec2da850097b2f0a 100644
|
||||
--- a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model_unittest.cc
|
||||
+++ b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model_unittest.cc
|
||||
@@ -41,6 +41,8 @@ class TestComboboxModelObserver : public ui::ComboboxModelObserver {
|
||||
changed_ = true;
|
||||
}
|
||||
|
||||
+ void OnComboboxModelDestroying(ui::ComboboxModel* model) override {}
|
||||
+
|
||||
private:
|
||||
bool changed_;
|
||||
};
|
||||
diff --git a/components/autofill/core/browser/ui/address_combobox_model.cc b/components/autofill/core/browser/ui/address_combobox_model.cc
|
||||
index 2f33fb3f5e12e034c8f716fa28d35b6aebc76751..5e425ae742404830948fbadd60b21570155a176a 100644
|
||||
--- a/components/autofill/core/browser/ui/address_combobox_model.cc
|
||||
+++ b/components/autofill/core/browser/ui/address_combobox_model.cc
|
||||
@@ -83,14 +83,6 @@ int AddressComboboxModel::GetDefaultIndex() const {
|
||||
return ui::ComboboxModel::GetDefaultIndex();
|
||||
}
|
||||
|
||||
-void AddressComboboxModel::AddObserver(ui::ComboboxModelObserver* observer) {
|
||||
- observers_.AddObserver(observer);
|
||||
-}
|
||||
-
|
||||
-void AddressComboboxModel::RemoveObserver(ui::ComboboxModelObserver* observer) {
|
||||
- observers_.RemoveObserver(observer);
|
||||
-}
|
||||
-
|
||||
int AddressComboboxModel::AddNewProfile(const AutofillProfile& profile) {
|
||||
profiles_cache_.push_back(std::make_unique<AutofillProfile>(profile));
|
||||
UpdateAddresses();
|
||||
@@ -130,7 +122,7 @@ void AddressComboboxModel::UpdateAddresses() {
|
||||
for (size_t i = 0; i < profiles_cache_.size(); ++i)
|
||||
addresses_.emplace_back(profiles_cache_[i]->guid(), labels[i]);
|
||||
|
||||
- for (auto& observer : observers_) {
|
||||
+ for (auto& observer : observers()) {
|
||||
observer.OnComboboxModelChanged(this);
|
||||
}
|
||||
}
|
||||
diff --git a/components/autofill/core/browser/ui/address_combobox_model.h b/components/autofill/core/browser/ui/address_combobox_model.h
|
||||
index 02d65209d2729deae608481411e1539ba08f5f7e..3a41b30c92543633da29e40e2c64cc4597e322d2 100644
|
||||
--- a/components/autofill/core/browser/ui/address_combobox_model.h
|
||||
+++ b/components/autofill/core/browser/ui/address_combobox_model.h
|
||||
@@ -40,8 +40,6 @@ class AddressComboboxModel : public ui::ComboboxModel {
|
||||
std::u16string GetItemAt(int index) const override;
|
||||
bool IsItemSeparatorAt(int index) const override;
|
||||
int GetDefaultIndex() const override;
|
||||
- void AddObserver(ui::ComboboxModelObserver* observer) override;
|
||||
- void RemoveObserver(ui::ComboboxModelObserver* observer) override;
|
||||
|
||||
// Adds |profile| to model and return its combobox index. The lifespan of
|
||||
// |profile| beyond this call is undefined so a copy must be made.
|
||||
@@ -72,9 +70,6 @@ class AddressComboboxModel : public ui::ComboboxModel {
|
||||
|
||||
// If non empty, the guid of the address that should be selected by default.
|
||||
std::string default_selected_guid_;
|
||||
-
|
||||
- // To be called when the data for the given country code was loaded.
|
||||
- base::ObserverList<ui::ComboboxModelObserver> observers_;
|
||||
};
|
||||
|
||||
} // namespace autofill
|
||||
diff --git a/components/autofill/core/browser/ui/region_combobox_model.cc b/components/autofill/core/browser/ui/region_combobox_model.cc
|
||||
index e17a040aeca5163553a998d6c43e98f595c42779..b23c7b9eeb9a93c3ceda6622daa1a8c4377605e2 100644
|
||||
--- a/components/autofill/core/browser/ui/region_combobox_model.cc
|
||||
+++ b/components/autofill/core/browser/ui/region_combobox_model.cc
|
||||
@@ -66,14 +66,6 @@ bool RegionComboboxModel::IsItemSeparatorAt(int index) const {
|
||||
return regions_[index].first.empty();
|
||||
}
|
||||
|
||||
-void RegionComboboxModel::AddObserver(ui::ComboboxModelObserver* observer) {
|
||||
- observers_.AddObserver(observer);
|
||||
-}
|
||||
-
|
||||
-void RegionComboboxModel::RemoveObserver(ui::ComboboxModelObserver* observer) {
|
||||
- observers_.RemoveObserver(observer);
|
||||
-}
|
||||
-
|
||||
void RegionComboboxModel::OnRegionDataLoaded(
|
||||
const std::vector<const ::i18n::addressinput::RegionData*>& regions) {
|
||||
// The RegionDataLoader will eventually self destruct after this call.
|
||||
@@ -94,7 +86,7 @@ void RegionComboboxModel::OnRegionDataLoaded(
|
||||
failed_to_load_data_ = true;
|
||||
}
|
||||
|
||||
- for (auto& observer : observers_) {
|
||||
+ for (auto& observer : observers()) {
|
||||
observer.OnComboboxModelChanged(this);
|
||||
}
|
||||
}
|
||||
diff --git a/components/autofill/core/browser/ui/region_combobox_model.h b/components/autofill/core/browser/ui/region_combobox_model.h
|
||||
index d1b5d8b9db0c4d9d15e4f2a50a6cba38b33f1be8..52cffd5d00649f8e2be7189bca128ba4c3e8a97d 100644
|
||||
--- a/components/autofill/core/browser/ui/region_combobox_model.h
|
||||
+++ b/components/autofill/core/browser/ui/region_combobox_model.h
|
||||
@@ -54,8 +54,6 @@ class RegionComboboxModel : public ui::ComboboxModel {
|
||||
int GetItemCount() const override;
|
||||
std::u16string GetItemAt(int index) const override;
|
||||
bool IsItemSeparatorAt(int index) const override;
|
||||
- void AddObserver(ui::ComboboxModelObserver* observer) override;
|
||||
- void RemoveObserver(ui::ComboboxModelObserver* observer) override;
|
||||
|
||||
private:
|
||||
// Callback for the RegionDataLoader.
|
||||
@@ -72,9 +70,6 @@ class RegionComboboxModel : public ui::ComboboxModel {
|
||||
// List of <code, name> pairs for ADDRESS_HOME_STATE combobox values;
|
||||
std::vector<std::pair<std::string, std::string>> regions_;
|
||||
|
||||
- // To be called when the data for the given country code was loaded.
|
||||
- base::ObserverList<ui::ComboboxModelObserver> observers_;
|
||||
-
|
||||
// Weak pointer factory.
|
||||
base::WeakPtrFactory<RegionComboboxModel> weak_factory_{this};
|
||||
};
|
||||
diff --git a/ui/base/models/combobox_model.cc b/ui/base/models/combobox_model.cc
|
||||
index d307ee0ec592ae01a1d04cef85c0a492b7f7e29e..413adebd0349e304006de0cc0c4e2a4536ebbf06 100644
|
||||
--- a/ui/base/models/combobox_model.cc
|
||||
+++ b/ui/base/models/combobox_model.cc
|
||||
@@ -4,10 +4,18 @@
|
||||
|
||||
#include "ui/base/models/combobox_model.h"
|
||||
|
||||
+#include "ui/base/models/combobox_model_observer.h"
|
||||
#include "ui/base/models/image_model.h"
|
||||
|
||||
namespace ui {
|
||||
|
||||
+ComboboxModel::ComboboxModel() = default;
|
||||
+
|
||||
+ComboboxModel::~ComboboxModel() {
|
||||
+ for (auto& observer : observers_)
|
||||
+ observer.OnComboboxModelDestroying(this);
|
||||
+}
|
||||
+
|
||||
std::u16string ComboboxModel::GetDropDownTextAt(int index) const {
|
||||
return GetItemAt(index);
|
||||
}
|
||||
@@ -36,4 +44,12 @@ bool ComboboxModel::IsItemEnabledAt(int index) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
+void ComboboxModel::AddObserver(ComboboxModelObserver* observer) {
|
||||
+ observers_.AddObserver(observer);
|
||||
+}
|
||||
+
|
||||
+void ComboboxModel::RemoveObserver(ComboboxModelObserver* observer) {
|
||||
+ observers_.RemoveObserver(observer);
|
||||
+}
|
||||
+
|
||||
} // namespace ui
|
||||
diff --git a/ui/base/models/combobox_model.h b/ui/base/models/combobox_model.h
|
||||
index 5264aaa5550859fce39a8485feff16c8fd83d897..eeff7dc8444e77fd598eb7ce05ab18259a9d7ef5 100644
|
||||
--- a/ui/base/models/combobox_model.h
|
||||
+++ b/ui/base/models/combobox_model.h
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "base/component_export.h"
|
||||
+#include "base/observer_list.h"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -17,7 +18,8 @@ class ImageModel;
|
||||
// A data model for a combo box.
|
||||
class COMPONENT_EXPORT(UI_BASE) ComboboxModel {
|
||||
public:
|
||||
- virtual ~ComboboxModel() {}
|
||||
+ ComboboxModel();
|
||||
+ virtual ~ComboboxModel();
|
||||
|
||||
// Returns the number of items in the combo box.
|
||||
virtual int GetItemCount() const = 0;
|
||||
@@ -52,9 +54,17 @@ class COMPONENT_EXPORT(UI_BASE) ComboboxModel {
|
||||
// Returns true if the item at |index| is enabled.
|
||||
virtual bool IsItemEnabledAt(int index) const;
|
||||
|
||||
- // Adds/removes an observer. Override if model supports mutation.
|
||||
- virtual void AddObserver(ComboboxModelObserver* observer) {}
|
||||
- virtual void RemoveObserver(ComboboxModelObserver* observer) {}
|
||||
+ // Adds/removes an observer.
|
||||
+ void AddObserver(ComboboxModelObserver* observer);
|
||||
+ void RemoveObserver(ComboboxModelObserver* observer);
|
||||
+
|
||||
+ protected:
|
||||
+ base::ObserverList<ui::ComboboxModelObserver>& observers() {
|
||||
+ return observers_;
|
||||
+ }
|
||||
+
|
||||
+ private:
|
||||
+ base::ObserverList<ui::ComboboxModelObserver> observers_;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
diff --git a/ui/base/models/combobox_model_observer.h b/ui/base/models/combobox_model_observer.h
|
||||
index 5e00ab68a0c14d0b48317ca287e3f105351a102c..44325ff0aa94b75eb662be1637f44aa5524a33e8 100644
|
||||
--- a/ui/base/models/combobox_model_observer.h
|
||||
+++ b/ui/base/models/combobox_model_observer.h
|
||||
@@ -16,10 +16,13 @@ class ComboboxModel;
|
||||
class COMPONENT_EXPORT(UI_BASE) ComboboxModelObserver
|
||||
: public base::CheckedObserver {
|
||||
public:
|
||||
- // Invoked when |model| has changed in some way. The observer should assume
|
||||
+ // Invoked when `model` has changed in some way. The observer should assume
|
||||
// everything changed.
|
||||
virtual void OnComboboxModelChanged(ComboboxModel* model) = 0;
|
||||
|
||||
+ // Invoked when `model` is destroyed. The observer should stop observing.
|
||||
+ virtual void OnComboboxModelDestroying(ComboboxModel* model) = 0;
|
||||
+
|
||||
protected:
|
||||
~ComboboxModelObserver() override = default;
|
||||
};
|
||||
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
|
||||
index 1e6140cda4daa61036e408347f5a35458cb28121..b49521a46640673b43e428e6c8cd04678326d322 100644
|
||||
--- a/ui/views/controls/combobox/combobox.cc
|
||||
+++ b/ui/views/controls/combobox/combobox.cc
|
||||
@@ -579,6 +579,10 @@ void Combobox::OnComboboxModelChanged(ui::ComboboxModel* model) {
|
||||
OnContentSizeMaybeChanged();
|
||||
}
|
||||
|
||||
+void Combobox::OnComboboxModelDestroying(ui::ComboboxModel* model) {
|
||||
+ SetModel(nullptr);
|
||||
+}
|
||||
+
|
||||
const base::RepeatingClosure& Combobox::GetCallback() const {
|
||||
return callback_;
|
||||
}
|
||||
diff --git a/ui/views/controls/combobox/combobox.h b/ui/views/controls/combobox/combobox.h
|
||||
index fc77f8e49cead5fb689fba3d56c65c1b8e69f298..1a3f533256eca91e4fd4552ee5425b7ca8e7ad7e 100644
|
||||
--- a/ui/views/controls/combobox/combobox.h
|
||||
+++ b/ui/views/controls/combobox/combobox.h
|
||||
@@ -131,6 +131,7 @@ class VIEWS_EXPORT Combobox : public View,
|
||||
protected:
|
||||
// Overridden from ComboboxModelObserver:
|
||||
void OnComboboxModelChanged(ui::ComboboxModel* model) override;
|
||||
+ void OnComboboxModelDestroying(ui::ComboboxModel* model) override;
|
||||
|
||||
// Getters to be used by metadata.
|
||||
const base::RepeatingClosure& GetCallback() const;
|
||||
diff --git a/ui/views/controls/combobox/combobox_unittest.cc b/ui/views/controls/combobox/combobox_unittest.cc
|
||||
index 7ce20acc4b3ecd6470d5f017f9171d0be35c09d2..91661f33074d3453b4f28a410ecc918b793f86c6 100644
|
||||
--- a/ui/views/controls/combobox/combobox_unittest.cc
|
||||
+++ b/ui/views/controls/combobox/combobox_unittest.cc
|
||||
@@ -84,13 +84,6 @@ class TestComboboxModel : public ui::ComboboxModel {
|
||||
return 0;
|
||||
}
|
||||
|
||||
- void AddObserver(ui::ComboboxModelObserver* observer) override {
|
||||
- observers_.AddObserver(observer);
|
||||
- }
|
||||
- void RemoveObserver(ui::ComboboxModelObserver* observer) override {
|
||||
- observers_.RemoveObserver(observer);
|
||||
- }
|
||||
-
|
||||
void SetSeparators(const std::set<int>& separators) {
|
||||
separators_ = separators;
|
||||
OnModelChanged();
|
||||
@@ -103,11 +96,10 @@ class TestComboboxModel : public ui::ComboboxModel {
|
||||
|
||||
private:
|
||||
void OnModelChanged() {
|
||||
- for (auto& observer : observers_)
|
||||
+ for (auto& observer : observers())
|
||||
observer.OnComboboxModelChanged(this);
|
||||
}
|
||||
|
||||
- base::ObserverList<ui::ComboboxModelObserver> observers_;
|
||||
std::set<int> separators_;
|
||||
int item_count_ = kItemCount;
|
||||
};
|
||||
@@ -134,20 +126,13 @@ class VectorComboboxModel : public ui::ComboboxModel {
|
||||
}
|
||||
bool IsItemSeparatorAt(int index) const override { return false; }
|
||||
int GetDefaultIndex() const override { return default_index_; }
|
||||
- void AddObserver(ui::ComboboxModelObserver* observer) override {
|
||||
- observers_.AddObserver(observer);
|
||||
- }
|
||||
- void RemoveObserver(ui::ComboboxModelObserver* observer) override {
|
||||
- observers_.RemoveObserver(observer);
|
||||
- }
|
||||
|
||||
void ValuesChanged() {
|
||||
- for (auto& observer : observers_)
|
||||
+ for (auto& observer : observers())
|
||||
observer.OnComboboxModelChanged(this);
|
||||
}
|
||||
|
||||
private:
|
||||
- base::ObserverList<ui::ComboboxModelObserver> observers_;
|
||||
int default_index_ = 0;
|
||||
const raw_ptr<std::vector<std::string>> values_;
|
||||
};
|
||||
@@ -884,6 +869,15 @@ TEST_F(ComboboxTest, SetTooltipTextNotifiesAccessibilityEvent) {
|
||||
EXPECT_EQ(test_tooltip_text, ASCIIToUTF16(name));
|
||||
}
|
||||
|
||||
+// Regression test for crbug.com/1264288.
|
||||
+// Should fail in ASan build before the fix.
|
||||
+TEST_F(ComboboxTest, NoCrashWhenComboboxOutlivesModel) {
|
||||
+ auto model = std::make_unique<TestComboboxModel>();
|
||||
+ auto combobox = std::make_unique<TestCombobox>(model.get());
|
||||
+ model.reset();
|
||||
+ combobox.reset();
|
||||
+}
|
||||
+
|
||||
namespace {
|
||||
|
||||
using ComboboxDefaultTest = ViewsTestBase;
|
||||
diff --git a/ui/views/controls/editable_combobox/editable_combobox.cc b/ui/views/controls/editable_combobox/editable_combobox.cc
|
||||
index 997036a7b11bfe202abfac3c0cc9c4dc8fe0ca8c..34f3f3857c48ff36670e640ea27d64aed1643494 100644
|
||||
--- a/ui/views/controls/editable_combobox/editable_combobox.cc
|
||||
+++ b/ui/views/controls/editable_combobox/editable_combobox.cc
|
||||
@@ -190,10 +190,15 @@ class EditableCombobox::EditableComboboxMenuModel
|
||||
return combobox_model_->GetDropDownIconAt(items_shown_[index].index);
|
||||
}
|
||||
|
||||
+ // ComboboxModelObserver:
|
||||
void OnComboboxModelChanged(ui::ComboboxModel* model) override {
|
||||
UpdateItemsShown();
|
||||
}
|
||||
|
||||
+ void OnComboboxModelDestroying(ui::ComboboxModel* model) override {
|
||||
+ observation_.Reset();
|
||||
+ }
|
||||
+
|
||||
int GetItemCount() const override { return items_shown_.size(); }
|
||||
|
||||
private:
|
||||
195
patches/chromium/cherry-pick-b03797bdb1df.patch
Normal file
195
patches/chromium/cherry-pick-b03797bdb1df.patch
Normal file
@@ -0,0 +1,195 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Xiaocheng Hu <xiaochengh@chromium.org>
|
||||
Date: Mon, 2 May 2022 23:43:40 +0000
|
||||
Subject: Markup sanitization should iterate until markup is stable
|
||||
|
||||
There are cases where parsing a markup and then serializing it does not
|
||||
round trip, which can be used to inject XSS. Current sanitization code
|
||||
only does one round of parsing and serializing, which does not remove
|
||||
XSS injections that hide deeper.
|
||||
|
||||
Hence this patch makes sanitization algorithm iterate until the markup
|
||||
is stable, or declares failure if it doesn't stabilize after many tries.
|
||||
|
||||
(cherry picked from commit 19280353fb92d0ff7d048d7cec28d6a23befbce0)
|
||||
|
||||
Fixed: 1315563
|
||||
Change-Id: I4a3ebe1fda6df0e04a24d863b2b48df2110af209
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3611826
|
||||
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
|
||||
Reviewed-by: Yoshifumi Inoue <yosin@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#997032}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3621618
|
||||
Auto-Submit: Xiaocheng Hu <xiaochengh@chromium.org>
|
||||
Reviewed-by: Joey Arhar <jarhar@chromium.org>
|
||||
Commit-Queue: Joey Arhar <jarhar@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5005@{#363}
|
||||
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc
|
||||
index 4ee332fa16a1bda54e6ba99c8063876fb046adaa..463fd9a37671b92c9af68ff5f3c0e8ddd5f1c62c 100644
|
||||
--- a/third_party/blink/renderer/core/editing/serializers/serialization.cc
|
||||
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
|
||||
@@ -835,6 +835,12 @@ static bool StripSVGUseDataURLs(Node& node) {
|
||||
return stripped;
|
||||
}
|
||||
|
||||
+namespace {
|
||||
+
|
||||
+constexpr unsigned kMaxSanitizationIterations = 16;
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
DocumentFragment* CreateSanitizedFragmentFromMarkupWithContext(
|
||||
Document& document,
|
||||
const String& raw_markup,
|
||||
@@ -844,42 +850,56 @@ DocumentFragment* CreateSanitizedFragmentFromMarkupWithContext(
|
||||
if (raw_markup.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
- Document* staging_document = CreateStagingDocumentForMarkupSanitization(
|
||||
- *document.GetFrame()->GetFrameScheduler()->GetAgentGroupScheduler());
|
||||
- Element* body = staging_document->body();
|
||||
-
|
||||
- DocumentFragment* fragment = CreateFragmentFromMarkupWithContext(
|
||||
- *staging_document, raw_markup, fragment_start, fragment_end, KURL(),
|
||||
- kDisallowScriptingAndPluginContent);
|
||||
- if (!fragment) {
|
||||
- staging_document->GetPage()->WillBeDestroyed();
|
||||
- return nullptr;
|
||||
- }
|
||||
+ // Iterate on parsing, sanitization and serialization until the markup is
|
||||
+ // stable, or if we have exceeded the maximum allowed number of iterations.
|
||||
+ String last_markup;
|
||||
+ String markup = raw_markup;
|
||||
+ for (unsigned iteration = 0;
|
||||
+ iteration < kMaxSanitizationIterations && last_markup != markup;
|
||||
+ ++iteration) {
|
||||
+ last_markup = markup;
|
||||
+
|
||||
+ Document* staging_document = CreateStagingDocumentForMarkupSanitization(
|
||||
+ *document.GetFrame()->GetFrameScheduler()->GetAgentGroupScheduler());
|
||||
+ Element* body = staging_document->body();
|
||||
+
|
||||
+ DocumentFragment* fragment = CreateFragmentFromMarkupWithContext(
|
||||
+ *staging_document, last_markup, fragment_start, fragment_end, KURL(),
|
||||
+ kDisallowScriptingAndPluginContent);
|
||||
+ if (!fragment) {
|
||||
+ staging_document->GetPage()->WillBeDestroyed();
|
||||
+ return nullptr;
|
||||
+ }
|
||||
|
||||
- bool needs_sanitization = false;
|
||||
- if (ContainsStyleElements(*fragment))
|
||||
- needs_sanitization = true;
|
||||
- if (StripSVGUseDataURLs(*fragment))
|
||||
- needs_sanitization = true;
|
||||
+ bool needs_sanitization = false;
|
||||
+ if (ContainsStyleElements(*fragment))
|
||||
+ needs_sanitization = true;
|
||||
+ if (StripSVGUseDataURLs(*fragment))
|
||||
+ needs_sanitization = true;
|
||||
|
||||
- if (!needs_sanitization) {
|
||||
+ if (!needs_sanitization) {
|
||||
+ markup = CreateMarkup(fragment);
|
||||
+ } else {
|
||||
+ body->appendChild(fragment);
|
||||
+ staging_document->UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
|
||||
+
|
||||
+ // This sanitizes stylesheets in the markup into element inline styles
|
||||
+ markup = CreateMarkup(Position::FirstPositionInNode(*body),
|
||||
+ Position::LastPositionInNode(*body),
|
||||
+ CreateMarkupOptions::Builder()
|
||||
+ .SetShouldAnnotateForInterchange(true)
|
||||
+ .SetIsForMarkupSanitization(true)
|
||||
+ .Build());
|
||||
+ }
|
||||
staging_document->GetPage()->WillBeDestroyed();
|
||||
- return CreateFragmentFromMarkupWithContext(
|
||||
- document, raw_markup, fragment_start, fragment_end, base_url,
|
||||
- kDisallowScriptingAndPluginContent);
|
||||
+
|
||||
+ fragment_start = 0;
|
||||
+ fragment_end = markup.length();
|
||||
}
|
||||
|
||||
- body->appendChild(fragment);
|
||||
- staging_document->UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
|
||||
-
|
||||
- // This sanitizes stylesheets in the markup into element inline styles
|
||||
- String markup = CreateMarkup(Position::FirstPositionInNode(*body),
|
||||
- Position::LastPositionInNode(*body),
|
||||
- CreateMarkupOptions::Builder()
|
||||
- .SetShouldAnnotateForInterchange(true)
|
||||
- .SetIsForMarkupSanitization(true)
|
||||
- .Build());
|
||||
- staging_document->GetPage()->WillBeDestroyed();
|
||||
+ // Sanitization failed because markup can't stabilize.
|
||||
+ if (last_markup != markup)
|
||||
+ return nullptr;
|
||||
|
||||
return CreateFragmentFromMarkup(document, markup, base_url,
|
||||
kDisallowScriptingAndPluginContent);
|
||||
diff --git a/third_party/blink/web_tests/editing/pasteboard/paste-svg-use.html b/third_party/blink/web_tests/editing/pasteboard/paste-svg-use.html
|
||||
index c03ca90d492a5e2af883f68811baa7660ef1cdc2..1af77f8faa4501f577e28297f013e170775bc86a 100644
|
||||
--- a/third_party/blink/web_tests/editing/pasteboard/paste-svg-use.html
|
||||
+++ b/third_party/blink/web_tests/editing/pasteboard/paste-svg-use.html
|
||||
@@ -32,6 +32,6 @@ selection_test(
|
||||
<use href=data:application/xml;base64,PHN2ZyBpZD0neCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJz4KPGEgaHJlZj0namF2YXNjcmlwdDphbGVydCgxMjMpJz4KICAgIDxyZWN0IHdpZHRoPScxMDAlJyBoZWlnaHQ9JzEwMCUnIGZpbGw9J2xpZ2h0Ymx1ZScgLz4KICAgICA8dGV4dCB4PScwJyB5PScwJyBmaWxsPSdibGFjayc+CiAgICAgICA8dHNwYW4geD0nMCcgZHk9JzEuMmVtJz5Pb3BzLCB0aGVyZSdzIHNvbWV0aGluZyB3cm9uZyB3aXRoIHRoZSBwYWdlITwvdHNwYW4+CiAgICAgPHRzcGFuIHg9JzAnIGR5PScxLjJlbSc+UGxlYXNlIGNsaWNrIGhlcmUgdG8gcmVsb2FkLjwvdHNwYW4+Cjwvc3ZnPg==#x>"></noscript>asdasd`);
|
||||
selection.document.execCommand('paste');
|
||||
},
|
||||
- '<div contenteditable>|<noscript><u title="</noscript><div contenteditable="false"><svg></svg></div></div>',
|
||||
+ '<div contenteditable><div><br></div><div> <u title="</div><div> </div><div> ">asdasd|</div></div>',
|
||||
'Paste blocks data URI in SVG use element injection via <noscript>');
|
||||
</script>
|
||||
diff --git a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..9e0ab2ee740f85ea4e9de9d3f1d2ac43bee5a985
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html
|
||||
@@ -0,0 +1,44 @@
|
||||
+<!doctype html>
|
||||
+<meta charset="utf-8">
|
||||
+<title>Async Clipboard.read() should sanitize text/html</title>
|
||||
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#dom-clipboard-read">
|
||||
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1315563">
|
||||
+<script src="/resources/testharness.js"></script>
|
||||
+<script src="/resources/testharnessreport.js"></script>
|
||||
+<script src="/resources/testdriver.js"></script>
|
||||
+<script src="/resources/testdriver-vendor.js"></script>
|
||||
+
|
||||
+<p><button id="button">Put payload in the clipboard</button></p>
|
||||
+<div id="output"></div>
|
||||
+
|
||||
+<script>
|
||||
+let testFailed = false;
|
||||
+function fail() {
|
||||
+ testFailed = true;
|
||||
+}
|
||||
+
|
||||
+button.onclick = () => document.execCommand('copy');
|
||||
+document.oncopy = ev => {
|
||||
+ ev.preventDefault();
|
||||
+ ev.clipboardData.setData(
|
||||
+ 'text/html',
|
||||
+ `<form><math><mtext></form><form><mglyph><xmp></math><img src=invalid onerror=fail()></xmp>`);
|
||||
+};
|
||||
+
|
||||
+promise_test(async test => {
|
||||
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
|
||||
+ await test_driver.click(button);
|
||||
+
|
||||
+ const items = await navigator.clipboard.read();
|
||||
+ const htmlBlob = await items[0].getType("text/html");
|
||||
+ const html = await htmlBlob.text();
|
||||
+
|
||||
+ // This inserts an image with `onerror` handler if `html` is not properly sanitized
|
||||
+ output.innerHTML = html;
|
||||
+
|
||||
+ // Allow the 'error' event to be dispatched asynchronously
|
||||
+ await new Promise(resolve => test.step_timeout(resolve, 100));
|
||||
+
|
||||
+ assert_false(testFailed);
|
||||
+});
|
||||
+</script>
|
||||
25
patches/chromium/cherry-pick-c643d18a078d.patch
Normal file
25
patches/chromium/cherry-pick-c643d18a078d.patch
Normal file
@@ -0,0 +1,25 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Rayan Kanso <rayankans@google.com>
|
||||
Date: Tue, 7 Jun 2022 13:13:36 +0000
|
||||
Subject: Don't expose URL chain in case of CO redirect
|
||||
|
||||
Bug: 1278255
|
||||
Change-Id: If853327b853e29792e5c8d1dfaeecf21d6fec004
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3693143
|
||||
Reviewed-by: Susanne Westphal <swestphal@google.com>
|
||||
Commit-Queue: Rayan Kanso <rayankans@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1011409}
|
||||
|
||||
diff --git a/content/browser/background_fetch/storage/mark_request_complete_task.cc b/content/browser/background_fetch/storage/mark_request_complete_task.cc
|
||||
index 9036ec9a92212fb33b526f64d5fc02afcde38726..76cc94dd0ea8fe515d22c41a3d8b8017f1bdfd6b 100644
|
||||
--- a/content/browser/background_fetch/storage/mark_request_complete_task.cc
|
||||
+++ b/content/browser/background_fetch/storage/mark_request_complete_task.cc
|
||||
@@ -103,6 +103,8 @@ void MarkRequestCompleteTask::StoreResponse(base::OnceClosure done_closure) {
|
||||
BackgroundFetchCrossOriginFilter filter(
|
||||
registration_id_.storage_key().origin(), *request_info_);
|
||||
if (!filter.CanPopulateBody()) {
|
||||
+ // Don't expose the initial URL in case of cross-origin redirects.
|
||||
+ response_->url_list.resize(1);
|
||||
failure_reason_ = proto::BackgroundFetchRegistration::FETCH_ERROR;
|
||||
// No point writing the response to the cache since it won't be exposed.
|
||||
CreateAndStoreCompletedRequest(std::move(done_closure));
|
||||
65
patches/chromium/cherry-pick-d7a5d6b38ea8.patch
Normal file
65
patches/chromium/cherry-pick-d7a5d6b38ea8.patch
Normal file
@@ -0,0 +1,65 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shrek Shao <shrekshao@google.com>
|
||||
Date: Thu, 14 Jul 2022 22:27:14 +0000
|
||||
Subject: Fix dawn write handle data update OOB check
|
||||
|
||||
(cherry picked from commit 0ba6ae3d447de7bc599a191f6792a4e6676f10a3)
|
||||
|
||||
Bug: chromium:1340654
|
||||
Change-Id: I9d87cb868eccc380f707ab6c3c6bdc26c386fbfc
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3738662
|
||||
Commit-Queue: Shrek Shao <shrekshao@google.com>
|
||||
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1021911}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3758974
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Reviewed-by: Austin Eng <enga@chromium.org>
|
||||
Commit-Queue: Austin Eng <enga@chromium.org>
|
||||
Auto-Submit: Shrek Shao <shrekshao@google.com>
|
||||
Cr-Commit-Position: refs/branch-heads/5005@{#1246}
|
||||
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
|
||||
|
||||
diff --git a/gpu/command_buffer/service/dawn_service_memory_transfer_service.cc b/gpu/command_buffer/service/dawn_service_memory_transfer_service.cc
|
||||
index 40a990ce6dc4be540d1d1309485a4b7779c93a3b..579cd3cbdfcd5990db02960413bcac86e41c69b2 100644
|
||||
--- a/gpu/command_buffer/service/dawn_service_memory_transfer_service.cc
|
||||
+++ b/gpu/command_buffer/service/dawn_service_memory_transfer_service.cc
|
||||
@@ -30,7 +30,8 @@ class ReadHandleImpl
|
||||
size_t offset,
|
||||
size_t size,
|
||||
void* serializePointer) override {
|
||||
- DCHECK_LE(size + offset, size_);
|
||||
+ DCHECK_LE(offset, size_);
|
||||
+ DCHECK_LE(size, size_ - offset);
|
||||
// Copy the data into the shared memory allocation.
|
||||
// In the case of buffer mapping, this is the mapped GPU memory which we
|
||||
// copy into client-visible shared memory.
|
||||
@@ -57,10 +58,16 @@ class WriteHandleImpl
|
||||
size_t size) override {
|
||||
// Nothing is serialized because we're using shared memory.
|
||||
DCHECK_EQ(deserialize_size, 0u);
|
||||
- DCHECK_LE(size + offset, size_);
|
||||
DCHECK(mTargetData);
|
||||
DCHECK(ptr_);
|
||||
|
||||
+ if (offset > mDataLength || size > mDataLength - offset) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ if (offset > size_ || size > size_ - offset) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
// Copy from shared memory into the target buffer.
|
||||
// mTargetData will always be the starting address
|
||||
// of the backing buffer after the dawn side change.
|
||||
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
|
||||
index 126c04fce185749e3c20be6db160141a987f46ce..387e034a42d653acdb0bb0b768cc3c46a5114ee5 100644
|
||||
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
|
||||
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
|
||||
@@ -1604,7 +1604,6 @@ error::Error WebGPUDecoderImpl::HandleDawnCommands(
|
||||
"WebGPUDecoderImpl::HandleDawnCommands", "bytes", size);
|
||||
|
||||
if (!wire_server_->HandleCommands(shm_commands, size)) {
|
||||
- NOTREACHED();
|
||||
return error::kLostContext;
|
||||
}
|
||||
|
||||
139
patches/chromium/cherry-pick-ecad352cd614.patch
Normal file
139
patches/chromium/cherry-pick-ecad352cd614.patch
Normal file
@@ -0,0 +1,139 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Ted Meyer <tmathmeyer@chromium.org>
|
||||
Date: Wed, 8 Jun 2022 04:33:20 +0000
|
||||
Subject: Add Stop method to BatchingMediaLog
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Now that ~MediaLog is posted for a later destruction due to garbage
|
||||
collector ownership of CodecLogger, it's possible for the
|
||||
SendQueuedMediaEvents call from ~BatchingMediaLog to reference
|
||||
InspectorMediaEventHandler::inspector_context_ after it has been freed.
|
||||
|
||||
This fix forces BatchingMediaLog to shut down it's logging capabilities
|
||||
when the destruction call is caused by the garbage collector deletion
|
||||
phase
|
||||
|
||||
R=liberato
|
||||
|
||||
(cherry picked from commit 1bbfaf23cd8a1e977cb445a82a4caae107632a59)
|
||||
|
||||
Bug: 1333333
|
||||
Change-Id: I0bdca72a71177c4c5a6a9dc692aad3de4c25f4e2
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3689639
|
||||
Commit-Queue: Ted (Chromium) Meyer <tmathmeyer@chromium.org>
|
||||
Reviewed-by: Eugene Zemtsov <eugene@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1011247}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3694435
|
||||
Auto-Submit: Ted (Chromium) Meyer <tmathmeyer@chromium.org>
|
||||
Reviewed-by: Eugene Zemtsov <ezemtsov@google.com>
|
||||
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5060@{#672}
|
||||
Cr-Branched-From: b83393d0f4038aeaf67f970a024d8101df7348d1-refs/heads/main@{#1002911}
|
||||
|
||||
diff --git a/content/renderer/media/batching_media_log.cc b/content/renderer/media/batching_media_log.cc
|
||||
index c7263471a3b352fe329c26dc258ccf7af2049820..fbd8f5c7720d3636870d63bfcefbdb55d862fef7 100644
|
||||
--- a/content/renderer/media/batching_media_log.cc
|
||||
+++ b/content/renderer/media/batching_media_log.cc
|
||||
@@ -75,6 +75,11 @@ BatchingMediaLog::~BatchingMediaLog() {
|
||||
SendQueuedMediaEvents();
|
||||
}
|
||||
|
||||
+void BatchingMediaLog::Stop() {
|
||||
+ base::AutoLock lock(lock_);
|
||||
+ event_handlers_.clear();
|
||||
+}
|
||||
+
|
||||
void BatchingMediaLog::OnWebMediaPlayerDestroyedLocked() {
|
||||
base::AutoLock lock(lock_);
|
||||
for (const auto& handler : event_handlers_)
|
||||
diff --git a/content/renderer/media/batching_media_log.h b/content/renderer/media/batching_media_log.h
|
||||
index eb757a4ca922bd5456d0e436d10cc540ee9134ae..6c2df688caee4d2fc17e973cdad12f20342e56fe 100644
|
||||
--- a/content/renderer/media/batching_media_log.h
|
||||
+++ b/content/renderer/media/batching_media_log.h
|
||||
@@ -45,6 +45,8 @@ class CONTENT_EXPORT BatchingMediaLog : public media::MediaLog {
|
||||
|
||||
~BatchingMediaLog() override;
|
||||
|
||||
+ void Stop() override;
|
||||
+
|
||||
// Will reset |last_ipc_send_time_| with the value of NowTicks().
|
||||
void SetTickClockForTesting(const base::TickClock* tick_clock);
|
||||
|
||||
diff --git a/media/base/media_log.cc b/media/base/media_log.cc
|
||||
index 3728433c5eb473cc9c82b6cf33b22bbf89471eea..46887da3dc21c504b26d6ff174eb14d68298c6b1 100644
|
||||
--- a/media/base/media_log.cc
|
||||
+++ b/media/base/media_log.cc
|
||||
@@ -49,6 +49,9 @@ std::string MediaLog::GetErrorMessageLocked() {
|
||||
return "";
|
||||
}
|
||||
|
||||
+// Default implementation.
|
||||
+void MediaLog::Stop() {}
|
||||
+
|
||||
void MediaLog::AddMessage(MediaLogMessageLevel level, std::string message) {
|
||||
std::unique_ptr<MediaLogRecord> record(
|
||||
CreateRecord(MediaLogRecord::Type::kMessage));
|
||||
diff --git a/media/base/media_log.h b/media/base/media_log.h
|
||||
index e70a95b72f75f9bbfb114a94396c048bce2a3647..cce6a266327b43837d7a6ccaca7f2969c06fef4b 100644
|
||||
--- a/media/base/media_log.h
|
||||
+++ b/media/base/media_log.h
|
||||
@@ -131,6 +131,10 @@ class MEDIA_EXPORT MediaLog {
|
||||
// even if this occurs, in the "won't crash" sense.
|
||||
virtual std::unique_ptr<MediaLog> Clone();
|
||||
|
||||
+ // Can be used for stopping a MediaLog during a garbage-collected destruction
|
||||
+ // sequence.
|
||||
+ virtual void Stop();
|
||||
+
|
||||
protected:
|
||||
// Ensures only subclasses and factories (e.g. Clone()) can create MediaLog.
|
||||
MediaLog();
|
||||
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_logger.h b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
|
||||
index 2d389c1835365e423d6289fb883cb6cccd4d8e84..fae0d8a4fb41b61316c5a61a1ba3d777562d206d 100644
|
||||
--- a/third_party/blink/renderer/modules/webcodecs/codec_logger.h
|
||||
+++ b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
|
||||
@@ -7,6 +7,10 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
+#include "base/check.h"
|
||||
+#include "base/location.h"
|
||||
+#include "base/memory/scoped_refptr.h"
|
||||
+#include "base/sequence_checker.h"
|
||||
#include "media/base/media_log.h"
|
||||
#include "media/base/media_util.h"
|
||||
#include "media/base/status.h"
|
||||
@@ -69,9 +73,20 @@ class MODULES_EXPORT CodecLogger final {
|
||||
// This allows us to destroy |parent_media_log_| and stop logging,
|
||||
// without causing problems to |media_log_| users.
|
||||
media_log_ = parent_media_log_->Clone();
|
||||
+
|
||||
+ task_runner_ = task_runner;
|
||||
}
|
||||
|
||||
- ~CodecLogger() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
|
||||
+ ~CodecLogger() {
|
||||
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
+ // media logs must be posted for destruction, since they can cause the
|
||||
+ // garbage collector to trigger an immediate cleanup and delete the owning
|
||||
+ // instance of |CodecLogger|.
|
||||
+ if (parent_media_log_) {
|
||||
+ parent_media_log_->Stop();
|
||||
+ task_runner_->DeleteSoon(FROM_HERE, std::move(parent_media_log_));
|
||||
+ }
|
||||
+ }
|
||||
|
||||
void SendPlayerNameInformation(const ExecutionContext& context,
|
||||
std::string loadedAs) {
|
||||
@@ -132,6 +147,9 @@ class MODULES_EXPORT CodecLogger final {
|
||||
// can be safely accessed, and whose raw pointer can be given callbacks.
|
||||
std::unique_ptr<media::MediaLog> media_log_;
|
||||
|
||||
+ // Keep task runner around for posting the media log to upon destruction.
|
||||
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
|
||||
+
|
||||
SEQUENCE_CHECKER(sequence_checker_);
|
||||
};
|
||||
|
||||
68
patches/chromium/cherry-pick-f1504440487f.patch
Normal file
68
patches/chromium/cherry-pick-f1504440487f.patch
Normal file
@@ -0,0 +1,68 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Justin Novosad <junov@chromium.org>
|
||||
Date: Thu, 2 Jun 2022 19:35:57 +0000
|
||||
Subject: PaintOpReader: Harden PaintImage deserialization
|
||||
|
||||
This fix prevents the deserialization of PaintImage pixel data from
|
||||
reading data out of bounds when the block of serialized pixel data isn't
|
||||
large enough to cover the expected amount of data, given the size and
|
||||
format of the image.
|
||||
|
||||
(cherry picked from commit e89ea1489429a9a9e49e70d5d4e8d018fbafb6ac)
|
||||
|
||||
Bug: 1325298
|
||||
Change-Id: Icbeb405d2031d7d8ce4537836d7996ce7885f6d1
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3669596
|
||||
Commit-Queue: Justin Novosad <junov@chromium.org>
|
||||
Reviewed-by: Jonathan Ross <jonross@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1007804}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3687975
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Reviewed-by: Justin Novosad <junov@chromium.org>
|
||||
Auto-Submit: Srinivas Sista <srinivassista@chromium.org>
|
||||
Commit-Queue: Srinivas Sista <srinivassista@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5005@{#1093}
|
||||
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
|
||||
|
||||
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
|
||||
index c56e2ea7a641d679feb348b808519dd17b861081..ecc736b61defabd7cea081b3017bbeda9aeaf0e9 100644
|
||||
--- a/cc/paint/paint_op_reader.cc
|
||||
+++ b/cc/paint/paint_op_reader.cc
|
||||
@@ -329,6 +329,10 @@ void PaintOpReader::Read(PaintImage* image) {
|
||||
|
||||
SkImageInfo image_info =
|
||||
SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType);
|
||||
+ if (pixel_size < image_info.computeMinByteSize()) {
|
||||
+ SetInvalid(DeserializationError::kInsufficientPixelData);
|
||||
+ return;
|
||||
+ }
|
||||
const volatile void* pixel_data = ExtractReadableMemory(pixel_size);
|
||||
if (!valid_)
|
||||
return;
|
||||
diff --git a/cc/paint/paint_op_reader.h b/cc/paint/paint_op_reader.h
|
||||
index 201cdfde5eea3a07e31a3d6a50a5119485d5c1fc..af784145a9365ea2f776a1020145c0b83c63f16d 100644
|
||||
--- a/cc/paint/paint_op_reader.h
|
||||
+++ b/cc/paint/paint_op_reader.h
|
||||
@@ -180,8 +180,9 @@ class CC_PAINT_EXPORT PaintOpReader {
|
||||
kSharedImageProviderNoAccess = 50,
|
||||
kSharedImageProviderSkImageCreationFailed = 51,
|
||||
kZeroSkColorFilterBytes = 52,
|
||||
+ kInsufficientPixelData = 53,
|
||||
|
||||
- kMaxValue = kZeroSkColorFilterBytes,
|
||||
+ kMaxValue = kInsufficientPixelData
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
|
||||
index 0a822d8a7d59debfb250b98f2a2079f256715b52..34569a0fcd539bca6b465e0555c8d44677f57447 100644
|
||||
--- a/tools/metrics/histograms/enums.xml
|
||||
+++ b/tools/metrics/histograms/enums.xml
|
||||
@@ -68766,6 +68766,7 @@ Called by update_net_trust_anchors.py.-->
|
||||
<int value="50" label="SharedImageProvider no access"/>
|
||||
<int value="51" label="SharedImageProvider SkImage creation failed"/>
|
||||
<int value="52" label="Zero SkColorFilter bytes"/>
|
||||
+ <int value="53" label="Insufficient Pixel Data"/>
|
||||
</enum>
|
||||
|
||||
<enum name="PaletteModeCancelType">
|
||||
230
patches/chromium/cherry-pick-f1dd785e021e.patch
Normal file
230
patches/chromium/cherry-pick-f1dd785e021e.patch
Normal file
@@ -0,0 +1,230 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Austin Sullivan <asully@chromium.org>
|
||||
Date: Mon, 16 May 2022 18:20:27 +0000
|
||||
Subject: M102: FSA: Sanitize .scf files
|
||||
|
||||
.scf files can be used to execute code without opening the file.
|
||||
Sanitize these files the same way we sanitize .lnk files.
|
||||
|
||||
Also updates filename sanitization logic to be consistent in blocking
|
||||
.lnk and .local extensions on all OSes.
|
||||
|
||||
(cherry picked from commit 988164c6c4a563c3d4c0dedba295d09472dfc15f)
|
||||
|
||||
Bug: 1227995
|
||||
Change-Id: I4b018f1ba524c783547e18630db9addc9fb126e6
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3089422
|
||||
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
|
||||
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1002147}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3648322
|
||||
Auto-Submit: Austin Sullivan <asully@chromium.org>
|
||||
Commit-Queue: Austin Sullivan <asully@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5005@{#759}
|
||||
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
|
||||
|
||||
diff --git a/content/browser/file_system_access/file_system_access_directory_handle_impl.cc b/content/browser/file_system_access/file_system_access_directory_handle_impl.cc
|
||||
index 56863d50d543470de597e8df62e2f5b43b7b0fbe..df922b5a6cb917457570bb0f11a48d7a1710c627 100644
|
||||
--- a/content/browser/file_system_access/file_system_access_directory_handle_impl.cc
|
||||
+++ b/content/browser/file_system_access/file_system_access_directory_handle_impl.cc
|
||||
@@ -438,10 +438,13 @@ namespace {
|
||||
bool IsShellIntegratedExtension(const base::FilePath::StringType& extension) {
|
||||
base::FilePath::StringType extension_lower = base::ToLowerASCII(extension);
|
||||
|
||||
- // .lnk files may be used to execute arbitrary code (see
|
||||
- // https://nvd.nist.gov/vuln/detail/CVE-2010-2568).
|
||||
- if (extension_lower == FILE_PATH_LITERAL("lnk"))
|
||||
+ // .lnk and .scf files may be used to execute arbitrary code (see
|
||||
+ // https://nvd.nist.gov/vuln/detail/CVE-2010-2568 and
|
||||
+ // https://crbug.com/1227995, respectively).
|
||||
+ if (extension_lower == FILE_PATH_LITERAL("lnk") ||
|
||||
+ extension_lower == FILE_PATH_LITERAL("scf")) {
|
||||
return true;
|
||||
+ }
|
||||
|
||||
// Setting a file's extension to a CLSID may conceal its actual file type on
|
||||
// some Windows versions (see https://nvd.nist.gov/vuln/detail/CVE-2004-0420).
|
||||
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.cc b/content/browser/file_system_access/file_system_access_manager_impl.cc
|
||||
index b265ba0f405cecc03cbcc1c0b217204d3e7b25f6..689f7fc7093b5483fa36af5fd7832a25adf9c23b 100644
|
||||
--- a/content/browser/file_system_access/file_system_access_manager_impl.cc
|
||||
+++ b/content/browser/file_system_access/file_system_access_manager_impl.cc
|
||||
@@ -530,6 +530,16 @@ void FileSystemAccessManagerImpl::SetDefaultPathAndShowPicker(
|
||||
suggested_name_path =
|
||||
net::GenerateFileName(GURL(), std::string(), std::string(),
|
||||
suggested_name, std::string(), std::string());
|
||||
+
|
||||
+ auto suggested_extension = suggested_name_path.Extension();
|
||||
+ // Our version of `IsShellIntegratedExtension()` is more stringent than
|
||||
+ // the version used in `net::GenerateFileName()`. See
|
||||
+ // `FileSystemChooser::IsShellIntegratedExtension()` for details.
|
||||
+ if (FileSystemChooser::IsShellIntegratedExtension(suggested_extension)) {
|
||||
+ suggested_extension = FILE_PATH_LITERAL("download");
|
||||
+ suggested_name_path =
|
||||
+ suggested_name_path.ReplaceExtension(suggested_extension);
|
||||
+ }
|
||||
}
|
||||
|
||||
FileSystemChooser::Options file_system_chooser_options(
|
||||
diff --git a/content/browser/file_system_access/file_system_chooser.cc b/content/browser/file_system_access/file_system_chooser.cc
|
||||
index 6f0acccd3458ed758dbe14c8a3008a52569d8055..86b9af148a86e64f9f4aedb6e39998bf83668745 100644
|
||||
--- a/content/browser/file_system_access/file_system_chooser.cc
|
||||
+++ b/content/browser/file_system_access/file_system_chooser.cc
|
||||
@@ -71,33 +71,6 @@ base::FilePath::StringType GetLastExtension(
|
||||
: extension;
|
||||
}
|
||||
|
||||
-// Returns whether the specified extension receives special handling by the
|
||||
-// Windows shell.
|
||||
-bool IsShellIntegratedExtension(const base::FilePath::StringType& extension) {
|
||||
- // TODO(https://crbug.com/1154757): Figure out some way to unify this with
|
||||
- // net::IsSafePortablePathComponent, with the result probably ending up in
|
||||
- // base/i18n/file_util_icu.h.
|
||||
- base::FilePath::StringType extension_lower = base::ToLowerASCII(extension);
|
||||
-
|
||||
- // .lnk files may be used to execute arbitrary code (see
|
||||
- // https://nvd.nist.gov/vuln/detail/CVE-2010-2568). .local files are used by
|
||||
- // Windows to determine which DLLs to load for an application.
|
||||
- if ((extension_lower == FILE_PATH_LITERAL("local")) ||
|
||||
- (extension_lower == FILE_PATH_LITERAL("lnk"))) {
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
- // Setting a file's extension to a CLSID may conceal its actual file type on
|
||||
- // some Windows versions (see https://nvd.nist.gov/vuln/detail/CVE-2004-0420).
|
||||
- if (!extension_lower.empty() &&
|
||||
- (extension_lower.front() == FILE_PATH_LITERAL('{')) &&
|
||||
- (extension_lower.back() == FILE_PATH_LITERAL('}'))) {
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
- return false;
|
||||
-}
|
||||
-
|
||||
// Extension validation primarily takes place in the renderer. This checks for a
|
||||
// subset of invalid extensions in the event the renderer is compromised.
|
||||
bool IsInvalidExtension(base::FilePath::StringType& extension) {
|
||||
@@ -105,7 +78,7 @@ bool IsInvalidExtension(base::FilePath::StringType& extension) {
|
||||
auto extension16 = base::UTF8ToUTF16(component8);
|
||||
|
||||
return !base::i18n::IsFilenameLegal(extension16) ||
|
||||
- IsShellIntegratedExtension(GetLastExtension(extension));
|
||||
+ FileSystemChooser::IsShellIntegratedExtension(extension);
|
||||
}
|
||||
|
||||
// Converts the accepted mime types and extensions from `option` into a list
|
||||
@@ -290,6 +263,40 @@ void FileSystemChooser::CreateAndShow(
|
||||
/*params=*/nullptr);
|
||||
}
|
||||
|
||||
+// static
|
||||
+bool FileSystemChooser::IsShellIntegratedExtension(
|
||||
+ const base::FilePath::StringType& extension) {
|
||||
+ // TODO(https://crbug.com/1154757): Figure out some way to unify this with
|
||||
+ // net::IsSafePortablePathComponent, with the result probably ending up in
|
||||
+ // base/i18n/file_util_icu.h.
|
||||
+ // - For the sake of consistency across platforms, we sanitize '.lnk' and
|
||||
+ // '.local' files on all platforms (not just Windows)
|
||||
+ // - There are some extensions (i.e. '.scf') we would like to sanitize which
|
||||
+ // `net::GenerateFileName()` does not
|
||||
+ base::FilePath::StringType extension_lower =
|
||||
+ base::ToLowerASCII(GetLastExtension(extension));
|
||||
+
|
||||
+ // .lnk and .scf files may be used to execute arbitrary code (see
|
||||
+ // https://nvd.nist.gov/vuln/detail/CVE-2010-2568 and
|
||||
+ // https://crbug.com/1227995, respectively). .local files are used by Windows
|
||||
+ // to determine which DLLs to load for an application.
|
||||
+ if ((extension_lower == FILE_PATH_LITERAL("lnk")) ||
|
||||
+ (extension_lower == FILE_PATH_LITERAL("local")) ||
|
||||
+ (extension_lower == FILE_PATH_LITERAL("scf"))) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // Setting a file's extension to a CLSID may conceal its actual file type on
|
||||
+ // some Windows versions (see https://nvd.nist.gov/vuln/detail/CVE-2004-0420).
|
||||
+ if (!extension_lower.empty() &&
|
||||
+ (extension_lower.front() == FILE_PATH_LITERAL('{')) &&
|
||||
+ (extension_lower.back() == FILE_PATH_LITERAL('}'))) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
FileSystemChooser::FileSystemChooser(ui::SelectFileDialog::Type type,
|
||||
ResultCallback callback,
|
||||
base::ScopedClosureRunner fullscreen_block)
|
||||
diff --git a/content/browser/file_system_access/file_system_chooser.h b/content/browser/file_system_access/file_system_chooser.h
|
||||
index 07c8e7fda7d96496f58ed9a9e6cba6558c8c37df..925df7f5ef1d1bb94926afbe29beda248ae5aabc 100644
|
||||
--- a/content/browser/file_system_access/file_system_chooser.h
|
||||
+++ b/content/browser/file_system_access/file_system_chooser.h
|
||||
@@ -68,6 +68,12 @@ class CONTENT_EXPORT FileSystemChooser : public ui::SelectFileDialog::Listener {
|
||||
ResultCallback callback,
|
||||
base::ScopedClosureRunner fullscreen_block);
|
||||
|
||||
+ // Returns whether the specified extension receives special handling by the
|
||||
+ // Windows shell. These extensions should be sanitized before being shown in
|
||||
+ // the "save as" file picker.
|
||||
+ static bool IsShellIntegratedExtension(
|
||||
+ const base::FilePath::StringType& extension);
|
||||
+
|
||||
FileSystemChooser(ui::SelectFileDialog::Type type,
|
||||
ResultCallback callback,
|
||||
base::ScopedClosureRunner fullscreen_block);
|
||||
diff --git a/content/browser/file_system_access/file_system_chooser_browsertest.cc b/content/browser/file_system_access/file_system_chooser_browsertest.cc
|
||||
index dc310d04fb29ed4fa9c9cdac7ab726d4ca9f9f37..9ea4db7807f6bbac799452fd138848b2a650d6fd 100644
|
||||
--- a/content/browser/file_system_access/file_system_chooser_browsertest.cc
|
||||
+++ b/content/browser/file_system_access/file_system_chooser_browsertest.cc
|
||||
@@ -1556,22 +1556,28 @@ IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SuggestedName) {
|
||||
name_infos.push_back({"not_matching.jpg", ListValueOf(".txt"), false,
|
||||
"not_matching.jpg", false});
|
||||
|
||||
-#if BUILDFLAG(IS_WIN)
|
||||
- // ".local" and ".lnk" extensions should be sanitized on Windows.
|
||||
+ // ".lnk", ".local", and ".scf" extensions should be sanitized.
|
||||
name_infos.push_back({"dangerous_extension.local", ListValueOf(".local"),
|
||||
true, "dangerous_extension.download", false});
|
||||
name_infos.push_back({"dangerous_extension.lnk", ListValueOf(".lnk"), true,
|
||||
"dangerous_extension.download", false});
|
||||
-#else
|
||||
- // ".local" and ".lnk" extensions should be allowed on other OSes.
|
||||
- // TODO(https://crbug.com/1154757): `expected_exclude_accept_all_option` is
|
||||
- // false here because ".local" and ".lnk" extensions are not allowed in
|
||||
- // `accepts`, but are only sanitized by net::GenerateSafeFileName on Windows.
|
||||
- name_infos.push_back({"dangerous_extension.local", ListValueOf(".local"),
|
||||
- true, "dangerous_extension.local", false});
|
||||
- name_infos.push_back({"dangerous_extension.lnk", ListValueOf(".lnk"), true,
|
||||
- "dangerous_extension.lnk", false});
|
||||
-#endif
|
||||
+ name_infos.push_back({"dangerous_extension.scf", ListValueOf(".scf"), true,
|
||||
+ "dangerous_extension.download", false});
|
||||
+ // Compound extensions ending in a dangerous extension should be sanitized.
|
||||
+ name_infos.push_back({"dangerous_extension.png.local", ListValueOf(".local"),
|
||||
+ true, "dangerous_extension.png.download", false});
|
||||
+ name_infos.push_back({"dangerous_extension.png.lnk", ListValueOf(".lnk"),
|
||||
+ true, "dangerous_extension.png.download", false});
|
||||
+ name_infos.push_back({"dangerous_extension.png.scf", ListValueOf(".scf"),
|
||||
+ true, "dangerous_extension.png.download", false});
|
||||
+ // Compound extensions not ending in a dangerous extension should not be
|
||||
+ // sanitized.
|
||||
+ name_infos.push_back({"dangerous_extension.local.png", ListValueOf(".png"),
|
||||
+ true, "dangerous_extension.local.png", true});
|
||||
+ name_infos.push_back({"dangerous_extension.lnk.png", ListValueOf(".png"),
|
||||
+ true, "dangerous_extension.lnk.png", true});
|
||||
+ name_infos.push_back({"dangerous_extension.scf.png", ListValueOf(".png"),
|
||||
+ true, "dangerous_extension.scf.png", true});
|
||||
// Invalid characters should be sanitized.
|
||||
name_infos.push_back({R"(inv*l:d\\ch%rבאמת!a<ters🤓.txt)",
|
||||
ListValueOf(".txt"), true,
|
||||
diff --git a/content/browser/file_system_access/file_system_chooser_unittest.cc b/content/browser/file_system_access/file_system_chooser_unittest.cc
|
||||
index 373de41cf5ddead27b5e036c1cc14448a731250a..9b27d6305bd00a19d94b5ec49f21a7ecff7ddc48 100644
|
||||
--- a/content/browser/file_system_access/file_system_chooser_unittest.cc
|
||||
+++ b/content/browser/file_system_access/file_system_chooser_unittest.cc
|
||||
@@ -189,7 +189,7 @@ TEST_F(FileSystemChooserTest, IgnoreShellIntegratedExtensions) {
|
||||
accepts.emplace_back(blink::mojom::ChooseFileSystemEntryAcceptsOption::New(
|
||||
u"", std::vector<std::string>({}),
|
||||
std::vector<std::string>(
|
||||
- {"lnk", "foo.lnk", "foo.bar.local", "text", "local"})));
|
||||
+ {"lnk", "foo.lnk", "foo.bar.local", "text", "local", "scf"})));
|
||||
SyncShowDialog(std::move(accepts), /*include_accepts_all=*/false);
|
||||
|
||||
ASSERT_TRUE(dialog_params.file_types);
|
||||
123
patches/chromium/cherry-pick-f3d01ff794dc.patch
Normal file
123
patches/chromium/cherry-pick-f3d01ff794dc.patch
Normal file
@@ -0,0 +1,123 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Alvin Ji <alvinji@chromium.org>
|
||||
Date: Thu, 28 Apr 2022 00:44:28 +0000
|
||||
Subject: Introduce isLowEnergyDevice() for safely downward static_cast from
|
||||
BluetoothDeviceMac to BluetoothLowEnergyDeviceMac
|
||||
|
||||
device/bluetooth/bluetooth_adapter_mac.mm has two non safely downward static_cast BluetoothDevice to BluetoothLowEnergyDeviceMac
|
||||
|
||||
To avoid it, we introduce isLowEnergyDevice() to BluetoothDeviceMac so we could identify LE bluetooth device from classic bluetooth device then safely cast it downward.
|
||||
|
||||
(cherry picked from commit 2582158fc555edee390a050a64c1b89994a6b349)
|
||||
|
||||
Bug: 1318610
|
||||
Change-Id: Iaf082fc2c40270237bbc0b000b80faa7f94b1026
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3601727
|
||||
Reviewed-by: Reilly Grant <reillyg@chromium.org>
|
||||
Commit-Queue: Alvin Ji <alvinji@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#995419}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3606585
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Cr-Commit-Position: refs/branch-heads/5005@{#217}
|
||||
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
|
||||
|
||||
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
|
||||
index 11d8419791f3e45d5242081422d452d4fc703833..012f9ce97d9ed6b00deb718a88f432e053cb3bd1 100644
|
||||
--- a/device/bluetooth/bluetooth_adapter_mac.mm
|
||||
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
|
||||
@@ -774,6 +774,11 @@ CBCentralManagerState GetCBManagerState(CBCentralManager* manager) {
|
||||
DVLOG(1)
|
||||
<< "Central no longer powered on. Notifying of device disconnection.";
|
||||
for (BluetoothDevice* device : GetDevices()) {
|
||||
+ // GetDevices() returns instances of BluetoothClassicDeviceMac and
|
||||
+ // BluetoothLowEnergyDeviceMac. The DidDisconnectPeripheral() method is
|
||||
+ // only available on BluetoothLowEnergyDeviceMac.
|
||||
+ if (!static_cast<BluetoothDeviceMac*>(device)->IsLowEnergyDevice())
|
||||
+ continue;
|
||||
BluetoothLowEnergyDeviceMac* device_mac =
|
||||
static_cast<BluetoothLowEnergyDeviceMac*>(device);
|
||||
if (device_mac->IsGattConnected()) {
|
||||
@@ -911,9 +916,16 @@ CBCentralManagerState GetCBManagerState(CBCentralManager* manager) {
|
||||
BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
|
||||
auto iter = devices_.find(device_address);
|
||||
if (iter == devices_.end()) {
|
||||
- return nil;
|
||||
- }
|
||||
- return static_cast<BluetoothLowEnergyDeviceMac*>(iter->second.get());
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+ // device_mac can be BluetoothClassicDeviceMac* or
|
||||
+ // BluetoothLowEnergyDeviceMac* To return valid BluetoothLowEnergyDeviceMac*
|
||||
+ // we need to first check with IsLowEnergyDevice()
|
||||
+ BluetoothDeviceMac* device_mac =
|
||||
+ static_cast<BluetoothDeviceMac*>(iter->second.get());
|
||||
+ return device_mac->IsLowEnergyDevice()
|
||||
+ ? static_cast<BluetoothLowEnergyDeviceMac*>(device_mac)
|
||||
+ : nullptr;
|
||||
}
|
||||
|
||||
bool BluetoothAdapterMac::DoesCollideWithKnownDevice(
|
||||
diff --git a/device/bluetooth/bluetooth_classic_device_mac.h b/device/bluetooth/bluetooth_classic_device_mac.h
|
||||
index b11dbbdec75ca276c6e9df50a00e4e3a97d5be8c..a7deb96690e2b64ed82ede5ade5591935ec0f308 100644
|
||||
--- a/device/bluetooth/bluetooth_classic_device_mac.h
|
||||
+++ b/device/bluetooth/bluetooth_classic_device_mac.h
|
||||
@@ -82,6 +82,7 @@ class BluetoothClassicDeviceMac : public BluetoothDeviceMac {
|
||||
// Returns the Bluetooth address for the |device|. The returned address has a
|
||||
// normalized format (see below).
|
||||
static std::string GetDeviceAddress(IOBluetoothDevice* device);
|
||||
+ bool IsLowEnergyDevice() override;
|
||||
|
||||
protected:
|
||||
// BluetoothDevice override
|
||||
diff --git a/device/bluetooth/bluetooth_classic_device_mac.mm b/device/bluetooth/bluetooth_classic_device_mac.mm
|
||||
index 210802eec420807c36f5cd95408bb00d9052ea1b..ceeeab4f102ceb1078e9a2b463170a04773404cf 100644
|
||||
--- a/device/bluetooth/bluetooth_classic_device_mac.mm
|
||||
+++ b/device/bluetooth/bluetooth_classic_device_mac.mm
|
||||
@@ -305,4 +305,8 @@ BluetoothUUID ExtractUuid(IOBluetoothSDPDataElement* service_class_data) {
|
||||
base::SysNSStringToUTF8([device addressString]));
|
||||
}
|
||||
|
||||
+bool BluetoothClassicDeviceMac::IsLowEnergyDevice() {
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
} // namespace device
|
||||
diff --git a/device/bluetooth/bluetooth_device_mac.h b/device/bluetooth/bluetooth_device_mac.h
|
||||
index 10245db6516b08a8e50e6637011a3a960440625c..0213e20aee170aae0a4b73e93f47c418110ef289 100644
|
||||
--- a/device/bluetooth/bluetooth_device_mac.h
|
||||
+++ b/device/bluetooth/bluetooth_device_mac.h
|
||||
@@ -30,6 +30,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceMac : public BluetoothDevice {
|
||||
BluetoothGattService::GattErrorCode error_code);
|
||||
static BluetoothGattService::GattErrorCode GetGattErrorCodeFromNSError(
|
||||
NSError* error);
|
||||
+ virtual bool IsLowEnergyDevice() = 0;
|
||||
|
||||
protected:
|
||||
BluetoothDeviceMac(BluetoothAdapterMac* adapter);
|
||||
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.h b/device/bluetooth/bluetooth_low_energy_device_mac.h
|
||||
index 6db4f57489d65f3363b716c49037c4fac1389f0c..1e5ce4777b37c6a65a64ba5f7d5bb630b2ebe687 100644
|
||||
--- a/device/bluetooth/bluetooth_low_energy_device_mac.h
|
||||
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.h
|
||||
@@ -83,6 +83,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac
|
||||
const device::BluetoothUUID& uuid,
|
||||
ConnectToServiceCallback callback,
|
||||
ConnectToServiceErrorCallback error_callback) override;
|
||||
+ bool IsLowEnergyDevice() override;
|
||||
|
||||
protected:
|
||||
// BluetoothDevice override.
|
||||
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.mm b/device/bluetooth/bluetooth_low_energy_device_mac.mm
|
||||
index 9a89728b0fc47cd7ca6e20d2ffbe7483010cbe39..73b1bee8e64664bdc98e45869accdf0ade2250b0 100644
|
||||
--- a/device/bluetooth/bluetooth_low_energy_device_mac.mm
|
||||
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.mm
|
||||
@@ -207,6 +207,10 @@ @interface CBPeripheral (HighSierraSDK)
|
||||
NOTIMPLEMENTED();
|
||||
}
|
||||
|
||||
+bool BluetoothLowEnergyDeviceMac::IsLowEnergyDevice() {
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
void BluetoothLowEnergyDeviceMac::CreateGattConnectionImpl(
|
||||
absl::optional<BluetoothUUID> serivce_uuid) {
|
||||
if (!IsGattConnected()) {
|
||||
338
patches/chromium/cherry-pick-f427936d32db.patch
Normal file
338
patches/chromium/cherry-pick-f427936d32db.patch
Normal file
@@ -0,0 +1,338 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Cheng <dcheng@chromium.org>
|
||||
Date: Tue, 14 Jun 2022 19:11:17 +0000
|
||||
Subject: Ensure raw_ptr<T> and T* are treated identically in //base callback.
|
||||
|
||||
There are safety checks associated with raw pointers (e.g. ensuring
|
||||
receiver pointers are not raw pointers). Make sure these checks are
|
||||
applied whether the input type is T* or raw_ptr<T>.
|
||||
|
||||
- Implement base::IsPointer<T> and base::RemovePointer<T>, which are
|
||||
similar to std::is_pointer<T> and std::remove_pointer<T>, except they
|
||||
also consider raw_ptr<T> a raw pointer type.
|
||||
- Fix failures from the strengthened asserts: WebAppInstallFinalizer
|
||||
does not need a callback at all, while the privacy sandbox dialog
|
||||
tests can safely use base::Unretained().
|
||||
- Add test cases to cover this in the //base callback nocompile test
|
||||
suite.
|
||||
- Fix the existing nocompile tests, which did not escape `||` and
|
||||
inadvertently matched any error text.
|
||||
|
||||
(cherry picked from commit 00c072a2c7f24921af3bbf8441abb34ecb0551a6)
|
||||
|
||||
Bug: 1335458
|
||||
Change-Id: I470e3d5bc35ed52bf125136db738a868ef90b7e7
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3700700
|
||||
Reviewed-by: Lei Zhang <thestig@chromium.org>
|
||||
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1013266}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3703779
|
||||
Cr-Commit-Position: refs/branch-heads/5005@{#1173}
|
||||
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
|
||||
|
||||
diff --git a/base/bind_internal.h b/base/bind_internal.h
|
||||
index ea2ab97fae8adb9d171f9bf4dd367a12498112ec..a14ba7865c3581ee467fec39bd94bd58b6d07f3f 100644
|
||||
--- a/base/bind_internal.h
|
||||
+++ b/base/bind_internal.h
|
||||
@@ -859,8 +859,8 @@ bool QueryCancellationTraits(const BindStateBase* base,
|
||||
template <typename Functor, typename Receiver, typename... Unused>
|
||||
std::enable_if_t<
|
||||
!(MakeFunctorTraits<Functor>::is_method &&
|
||||
- std::is_pointer_v<std::decay_t<Receiver>> &&
|
||||
- IsRefCountedType<std::remove_pointer_t<std::decay_t<Receiver>>>::value)>
|
||||
+ IsPointerV<std::decay_t<Receiver>> &&
|
||||
+ IsRefCountedType<RemovePointerT<std::decay_t<Receiver>>>::value)>
|
||||
BanUnconstructedRefCountedReceiver(const Receiver& receiver, Unused&&...) {}
|
||||
|
||||
template <typename Functor>
|
||||
@@ -870,8 +870,8 @@ void BanUnconstructedRefCountedReceiver() {}
|
||||
template <typename Functor, typename Receiver, typename... Unused>
|
||||
std::enable_if_t<
|
||||
MakeFunctorTraits<Functor>::is_method &&
|
||||
- std::is_pointer_v<std::decay_t<Receiver>> &&
|
||||
- IsRefCountedType<std::remove_pointer_t<std::decay_t<Receiver>>>::value>
|
||||
+ IsPointerV<std::decay_t<Receiver>> &&
|
||||
+ IsRefCountedType<RemovePointerT<std::decay_t<Receiver>>>::value>
|
||||
BanUnconstructedRefCountedReceiver(const Receiver& receiver, Unused&&...) {
|
||||
DCHECK(receiver);
|
||||
|
||||
@@ -1006,19 +1006,20 @@ struct MakeBindStateTypeImpl<true, Functor, Receiver, BoundArgs...> {
|
||||
static_assert(!std::is_array_v<std::remove_reference_t<Receiver>>,
|
||||
"First bound argument to a method cannot be an array.");
|
||||
static_assert(
|
||||
- !std::is_pointer_v<DecayedReceiver> ||
|
||||
- IsRefCountedType<std::remove_pointer_t<DecayedReceiver>>::value,
|
||||
+ !IsPointerV<DecayedReceiver> ||
|
||||
+ IsRefCountedType<RemovePointerT<DecayedReceiver>>::value,
|
||||
"Receivers may not be raw pointers. If using a raw pointer here is safe"
|
||||
" and has no lifetime concerns, use base::Unretained() and document why"
|
||||
" it's safe.");
|
||||
+
|
||||
static_assert(!HasRefCountedTypeAsRawPtr<std::decay_t<BoundArgs>...>::value,
|
||||
"A parameter is a refcounted type and needs scoped_refptr.");
|
||||
|
||||
public:
|
||||
using Type = BindState<
|
||||
std::decay_t<Functor>,
|
||||
- std::conditional_t<std::is_pointer_v<DecayedReceiver>,
|
||||
- scoped_refptr<std::remove_pointer_t<DecayedReceiver>>,
|
||||
+ std::conditional_t<IsPointerV<DecayedReceiver>,
|
||||
+ scoped_refptr<RemovePointerT<DecayedReceiver>>,
|
||||
DecayedReceiver>,
|
||||
MakeStorageType<BoundArgs>...>;
|
||||
};
|
||||
diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc
|
||||
index a5f681fe53b97a5a98f5918ac5cbc9e6cbbf790b..6844b6796d9f19e98baf13adae772d01eedf6846 100644
|
||||
--- a/base/bind_unittest.cc
|
||||
+++ b/base/bind_unittest.cc
|
||||
@@ -1169,6 +1169,28 @@ TYPED_TEST(BindVariantsTest, UniquePtrReceiver) {
|
||||
TypeParam::Bind(&NoRef::VoidMethod0, std::move(no_ref)).Run();
|
||||
}
|
||||
|
||||
+TYPED_TEST(BindVariantsTest, ImplicitRefPtrReceiver) {
|
||||
+ StrictMock<HasRef> has_ref;
|
||||
+ EXPECT_CALL(has_ref, AddRef()).Times(1);
|
||||
+ EXPECT_CALL(has_ref, Release()).Times(1);
|
||||
+ EXPECT_CALL(has_ref, HasAtLeastOneRef()).WillRepeatedly(Return(true));
|
||||
+
|
||||
+ HasRef* ptr = &has_ref;
|
||||
+ auto ptr_cb = TypeParam::Bind(&HasRef::HasAtLeastOneRef, ptr);
|
||||
+ EXPECT_EQ(1, std::move(ptr_cb).Run());
|
||||
+}
|
||||
+
|
||||
+TYPED_TEST(BindVariantsTest, RawPtrReceiver) {
|
||||
+ StrictMock<HasRef> has_ref;
|
||||
+ EXPECT_CALL(has_ref, AddRef()).Times(1);
|
||||
+ EXPECT_CALL(has_ref, Release()).Times(1);
|
||||
+ EXPECT_CALL(has_ref, HasAtLeastOneRef()).WillRepeatedly(Return(true));
|
||||
+
|
||||
+ raw_ptr<HasRef> rawptr(&has_ref);
|
||||
+ auto rawptr_cb = TypeParam::Bind(&HasRef::HasAtLeastOneRef, rawptr);
|
||||
+ EXPECT_EQ(1, std::move(rawptr_cb).Run());
|
||||
+}
|
||||
+
|
||||
// Tests for Passed() wrapper support:
|
||||
// - Passed() can be constructed from a pointer to scoper.
|
||||
// - Passed() can be constructed from a scoper rvalue.
|
||||
@@ -1751,6 +1773,12 @@ TEST(BindDeathTest, BanFirstOwnerOfRefCountedType) {
|
||||
EXPECT_CALL(has_ref, HasAtLeastOneRef()).WillOnce(Return(false));
|
||||
base::BindOnce(&HasRef::VoidMethod0, &has_ref);
|
||||
});
|
||||
+
|
||||
+ EXPECT_DCHECK_DEATH({
|
||||
+ raw_ptr<HasRef> rawptr(&has_ref);
|
||||
+ EXPECT_CALL(has_ref, HasAtLeastOneRef()).WillOnce(Return(false));
|
||||
+ base::BindOnce(&HasRef::VoidMethod0, rawptr);
|
||||
+ });
|
||||
}
|
||||
|
||||
} // namespace
|
||||
diff --git a/base/bind_unittest.nc b/base/bind_unittest.nc
|
||||
index 20b0e0ba2ceea9576cae13bf08cdb0e979f70018..29807298ca3c13fd54c45c14d496bebdb3123816 100644
|
||||
--- a/base/bind_unittest.nc
|
||||
+++ b/base/bind_unittest.nc
|
||||
@@ -93,7 +93,7 @@ void WontCompile() {
|
||||
method_to_const_cb.Run();
|
||||
}
|
||||
|
||||
-#elif defined(NCTEST_METHOD_BIND_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!std::is_pointer_v<base::NoRef *> || IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained() and document why it's safe.\""]
|
||||
+#elif defined(NCTEST_METHOD_BIND_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!IsPointerV<base::NoRef \*> \|\| IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained\(\) and document why it's safe.\""]
|
||||
|
||||
|
||||
// Method bound to non-refcounted object.
|
||||
@@ -106,7 +106,7 @@ void WontCompile() {
|
||||
no_ref_cb.Run();
|
||||
}
|
||||
|
||||
-#elif defined(NCTEST_CONST_METHOD_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!std::is_pointer_v<base::NoRef *> || IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained() and document why it's safe.\""]
|
||||
+#elif defined(NCTEST_CONST_METHOD_BIND_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!IsPointerV<base::NoRef \*> \|\| IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained\(\) and document why it's safe.\""]
|
||||
|
||||
// Const Method bound to non-refcounted object.
|
||||
//
|
||||
@@ -118,6 +118,33 @@ void WontCompile() {
|
||||
no_ref_const_cb.Run();
|
||||
}
|
||||
|
||||
+#elif defined(NCTEST_METHOD_BIND_RAW_PTR_RECEIVER_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!IsPointerV<base::raw_ptr<base::NoRef, [^>]+>> \|\| IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained\(\) and document why it's safe.\""]
|
||||
+
|
||||
+
|
||||
+// Method bound to non-refcounted object.
|
||||
+//
|
||||
+// We require refcounts unless you have Unretained().
|
||||
+void WontCompile() {
|
||||
+ NoRef no_ref;
|
||||
+ raw_ptr<NoRef> rawptr(&no_ref);
|
||||
+ RepeatingCallback<void()> no_ref_cb =
|
||||
+ BindRepeating(&NoRef::VoidMethod0, rawptr);
|
||||
+ no_ref_cb.Run();
|
||||
+}
|
||||
+
|
||||
+#elif defined(NCTEST_CONST_METHOD_BIND_RAW_PTR_RECEIVER_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!IsPointerV<base::raw_ptr<base::NoRef, [^>]+>> \|\| IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained\(\) and document why it's safe.\""]
|
||||
+
|
||||
+// Const Method bound to non-refcounted object.
|
||||
+//
|
||||
+// We require refcounts unless you have Unretained().
|
||||
+void WontCompile() {
|
||||
+ NoRef no_ref;
|
||||
+ raw_ptr<NoRef> rawptr(&no_ref);
|
||||
+ RepeatingCallback<void()> no_ref_const_cb =
|
||||
+ BindRepeating(&NoRef::VoidConstMethod0, rawptr);
|
||||
+ no_ref_const_cb.Run();
|
||||
+}
|
||||
+
|
||||
#elif defined(NCTEST_CONST_POINTER) // [r"static_assert failed.+?BindArgument<0>::ForwardedAs<.+?>::ToParamWithType<.+?>::kCanBeForwardedToBoundFunctor.+?Type mismatch between bound argument and bound functor's parameter\."]
|
||||
// Const argument used with non-const pointer parameter of same type.
|
||||
//
|
||||
diff --git a/base/memory/raw_ptr.h b/base/memory/raw_ptr.h
|
||||
index 9172af8ffe03bd038825b885ca46b7ffe8a3f90f..045c397fafb676eacd9b1030e114bd3875f918cd 100644
|
||||
--- a/base/memory/raw_ptr.h
|
||||
+++ b/base/memory/raw_ptr.h
|
||||
@@ -882,6 +882,37 @@ RAW_PTR_FUNC_ATTRIBUTES bool operator>=(const raw_ptr<U, I>& lhs,
|
||||
return lhs.GetForComparison() >= rhs.GetForComparison();
|
||||
}
|
||||
|
||||
+// Template helpers for working with T* or raw_ptr<T>.
|
||||
+template <typename T>
|
||||
+struct IsPointer : std::false_type {};
|
||||
+
|
||||
+template <typename T>
|
||||
+struct IsPointer<T*> : std::true_type {};
|
||||
+
|
||||
+template <typename T, typename I>
|
||||
+struct IsPointer<raw_ptr<T, I>> : std::true_type {};
|
||||
+
|
||||
+template <typename T>
|
||||
+inline constexpr bool IsPointerV = IsPointer<T>::value;
|
||||
+
|
||||
+template <typename T>
|
||||
+struct RemovePointer {
|
||||
+ using type = T;
|
||||
+};
|
||||
+
|
||||
+template <typename T>
|
||||
+struct RemovePointer<T*> {
|
||||
+ using type = T;
|
||||
+};
|
||||
+
|
||||
+template <typename T, typename I>
|
||||
+struct RemovePointer<raw_ptr<T, I>> {
|
||||
+ using type = T;
|
||||
+};
|
||||
+
|
||||
+template <typename T>
|
||||
+using RemovePointerT = typename RemovePointer<T>::type;
|
||||
+
|
||||
} // namespace base
|
||||
|
||||
using base::raw_ptr;
|
||||
diff --git a/base/memory/raw_scoped_refptr_mismatch_checker.h b/base/memory/raw_scoped_refptr_mismatch_checker.h
|
||||
index 9e50458ec98bb7e9041355fa06d14c6ba703b85c..7afae066fa3e318b346fac6b60ee55873b65f909 100644
|
||||
--- a/base/memory/raw_scoped_refptr_mismatch_checker.h
|
||||
+++ b/base/memory/raw_scoped_refptr_mismatch_checker.h
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
+#include "base/memory/raw_ptr.h"
|
||||
#include "base/template_util.h"
|
||||
|
||||
// It is dangerous to post a task with a T* argument where T is a subtype of
|
||||
@@ -35,8 +36,8 @@ struct IsRefCountedType<T,
|
||||
// pointer type and are convertible to a RefCounted(Base|ThreadSafeBase) type.
|
||||
template <typename T>
|
||||
struct NeedsScopedRefptrButGetsRawPtr
|
||||
- : conjunction<std::is_pointer<T>,
|
||||
- IsRefCountedType<std::remove_pointer_t<T>>> {
|
||||
+ : conjunction<base::IsPointer<T>,
|
||||
+ IsRefCountedType<base::RemovePointerT<T>>> {
|
||||
static_assert(!std::is_reference<T>::value,
|
||||
"NeedsScopedRefptrButGetsRawPtr requires non-reference type.");
|
||||
};
|
||||
diff --git a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc
|
||||
index 47d3cc7579b8375e7e356385c0537eec59dd591a..a2808c7d198d7b2e469e2c639e028cbf9cf9b583 100644
|
||||
--- a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc
|
||||
+++ b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc
|
||||
@@ -67,9 +67,7 @@ class PrivacySandboxDialogHandlerTest : public testing::Test {
|
||||
content::TestWebUI* web_ui() { return web_ui_.get(); }
|
||||
PrivacySandboxDialogHandler* handler() { return handler_.get(); }
|
||||
TestingProfile* profile() { return &profile_; }
|
||||
- raw_ptr<MockPrivacySandboxDialogView> dialog_mock() {
|
||||
- return dialog_mock_.get();
|
||||
- }
|
||||
+ MockPrivacySandboxDialogView* dialog_mock() { return dialog_mock_.get(); }
|
||||
MockPrivacySandboxService* mock_privacy_sandbox_service() {
|
||||
return mock_privacy_sandbox_service_;
|
||||
}
|
||||
@@ -93,13 +91,16 @@ class PrivacySandboxConsentDialogHandlerTest
|
||||
: public PrivacySandboxDialogHandlerTest {
|
||||
protected:
|
||||
std::unique_ptr<PrivacySandboxDialogHandler> CreateHandler() override {
|
||||
+ // base::Unretained is safe because the created handler does not outlive the
|
||||
+ // mock.
|
||||
return std::make_unique<PrivacySandboxDialogHandler>(
|
||||
- base::BindOnce(&MockPrivacySandboxDialogView::Close, dialog_mock()),
|
||||
+ base::BindOnce(&MockPrivacySandboxDialogView::Close,
|
||||
+ base::Unretained(dialog_mock())),
|
||||
base::BindOnce(&MockPrivacySandboxDialogView::ResizeNativeView,
|
||||
- dialog_mock()),
|
||||
+ base::Unretained(dialog_mock())),
|
||||
base::BindOnce(
|
||||
&MockPrivacySandboxDialogView::OpenPrivacySandboxSettings,
|
||||
- dialog_mock()),
|
||||
+ base::Unretained(dialog_mock())),
|
||||
PrivacySandboxService::DialogType::kConsent);
|
||||
}
|
||||
};
|
||||
@@ -193,13 +194,16 @@ class PrivacySandboxNoticeDialogHandlerTest
|
||||
: public PrivacySandboxDialogHandlerTest {
|
||||
protected:
|
||||
std::unique_ptr<PrivacySandboxDialogHandler> CreateHandler() override {
|
||||
+ // base::Unretained is safe because the created handler does not outlive the
|
||||
+ // mock.
|
||||
return std::make_unique<PrivacySandboxDialogHandler>(
|
||||
- base::BindOnce(&MockPrivacySandboxDialogView::Close, dialog_mock()),
|
||||
+ base::BindOnce(&MockPrivacySandboxDialogView::Close,
|
||||
+ base::Unretained(dialog_mock())),
|
||||
base::BindOnce(&MockPrivacySandboxDialogView::ResizeNativeView,
|
||||
- dialog_mock()),
|
||||
+ base::Unretained(dialog_mock())),
|
||||
base::BindOnce(
|
||||
&MockPrivacySandboxDialogView::OpenPrivacySandboxSettings,
|
||||
- dialog_mock()),
|
||||
+ base::Unretained(dialog_mock())),
|
||||
PrivacySandboxService::DialogType::kNotice);
|
||||
}
|
||||
};
|
||||
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
|
||||
index 7f36ec57e697b871b677c09c255cbce9bcb09b3a..b4e6af317aa25d7f8eb11bfda1d81c999a93e1fb 100644
|
||||
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
|
||||
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
|
||||
@@ -568,10 +568,6 @@ void WebAppInstallFinalizer::SetWebAppManifestFieldsAndWriteData(
|
||||
web_app_info.shortcuts_menu_icon_bitmaps;
|
||||
IconsMap other_icon_bitmaps = web_app_info.other_icon_bitmaps;
|
||||
|
||||
- auto write_icons_callback = base::BindOnce(
|
||||
- &WebAppIconManager::WriteData, icon_manager_, app_id,
|
||||
- std::move(icon_bitmaps), std::move(shortcuts_menu_icon_bitmaps),
|
||||
- std::move(other_icon_bitmaps));
|
||||
auto write_translations_callback = base::BindOnce(
|
||||
&WebAppInstallFinalizer::WriteTranslations,
|
||||
weak_ptr_factory_.GetWeakPtr(), app_id, std::move(web_app_info));
|
||||
@@ -579,11 +575,12 @@ void WebAppInstallFinalizer::SetWebAppManifestFieldsAndWriteData(
|
||||
base::BindOnce(&WebAppInstallFinalizer::CommitToSyncBridge,
|
||||
weak_ptr_factory_.GetWeakPtr(), std::move(web_app));
|
||||
|
||||
- std::move(write_icons_callback)
|
||||
- .Run(base::BindOnce(
|
||||
- std::move(write_translations_callback),
|
||||
- base::BindOnce(std::move(commit_to_sync_bridge_callback),
|
||||
- std::move(commit_callback))));
|
||||
+ icon_manager_->WriteData(
|
||||
+ app_id, std::move(icon_bitmaps), std::move(shortcuts_menu_icon_bitmaps),
|
||||
+ std::move(other_icon_bitmaps),
|
||||
+ base::BindOnce(std::move(write_translations_callback),
|
||||
+ base::BindOnce(std::move(commit_to_sync_bridge_callback),
|
||||
+ std::move(commit_callback))));
|
||||
}
|
||||
|
||||
void WebAppInstallFinalizer::WriteTranslations(
|
||||
47
patches/chromium/chore_add_electron_deps_to_gitignores.patch
Normal file
47
patches/chromium/chore_add_electron_deps_to_gitignores.patch
Normal file
@@ -0,0 +1,47 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samuel Attard <sattard@salesforce.com>
|
||||
Date: Tue, 26 Jul 2022 00:05:29 -0700
|
||||
Subject: chore: add electron deps to gitignores
|
||||
|
||||
Makes things like "git status" quicker when developing electron locally
|
||||
|
||||
diff --git a/.gitignore b/.gitignore
|
||||
index 5a473b051598d15aee36550a2a393912f155f534..0f09c61758711b46838031b98e76c4cf5ca9fda8 100644
|
||||
--- a/.gitignore
|
||||
+++ b/.gitignore
|
||||
@@ -218,6 +218,7 @@ vs-chromium-project.txt
|
||||
/delegate_execute
|
||||
/device/serial/device_serial_mojo.xml
|
||||
/docs/website
|
||||
+/electron
|
||||
/google_apis/gcm/gcm.xml
|
||||
/google_apis/internal
|
||||
/googleurl
|
||||
diff --git a/third_party/.gitignore b/third_party/.gitignore
|
||||
index 9163a971d3a0ac13e8e3b8620cd9bfd196d7fd78..6feb071b816c26e8da9080393eaa52ef4c6d802a 100644
|
||||
--- a/third_party/.gitignore
|
||||
+++ b/third_party/.gitignore
|
||||
@@ -78,6 +78,7 @@
|
||||
/directxsdk
|
||||
/dom_distiller_js/dist
|
||||
/eigen3/src
|
||||
+/electron_node
|
||||
/elfutils/src
|
||||
/emoji-segmenter/src
|
||||
/emoji-metadata/src
|
||||
@@ -172,6 +173,7 @@
|
||||
/mocha
|
||||
/mockito/src
|
||||
/nacl_sdk_binaries/
|
||||
+/nan
|
||||
/nasm
|
||||
/nearby/src
|
||||
/neon_2_sse/src
|
||||
@@ -235,6 +237,7 @@
|
||||
/speex
|
||||
/sqlite/src
|
||||
/sqlite4java/lib/
|
||||
+/squirrel.mac
|
||||
/subresource-filter-ruleset/data/UnindexedRules
|
||||
/swiftshader/
|
||||
/syzygy
|
||||
@@ -0,0 +1,76 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Fri, 29 Jul 2022 00:29:35 +0900
|
||||
Subject: chore: allow chromium to handle synthetic mouse events for touch
|
||||
|
||||
With WCO, allow chromium to handle synthetic mouse events generated for touch
|
||||
actions in the non-client caption area.
|
||||
|
||||
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
|
||||
index 65b9f5e5f81e8ef8b591ef2e027e095df11c0d7b..b5d764db353e758e8feefd5fd0a045be27cf09c6 100644
|
||||
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
|
||||
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
|
||||
@@ -1169,6 +1169,10 @@ void DesktopWindowTreeHostWin::HandleWindowScaleFactorChanged(
|
||||
}
|
||||
}
|
||||
|
||||
+bool DesktopWindowTreeHostWin::HandleMouseEventForCaption(UINT message) const {
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
DesktopNativeCursorManager*
|
||||
DesktopWindowTreeHostWin::GetSingletonDesktopNativeCursorManager() {
|
||||
return new DesktopNativeCursorManagerWin();
|
||||
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
|
||||
index 444581249014a8ce301591f269dbb194f0520732..9377f26b081b717db6b50c13ce3795907cf2fcd2 100644
|
||||
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
|
||||
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
|
||||
@@ -262,6 +262,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
|
||||
void HandleWindowSizeChanging() override;
|
||||
void HandleWindowSizeUnchanged() override;
|
||||
void HandleWindowScaleFactorChanged(float window_scale_factor) override;
|
||||
+ bool HandleMouseEventForCaption(UINT message) const override;
|
||||
|
||||
Widget* GetWidget();
|
||||
const Widget* GetWidget() const;
|
||||
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
|
||||
index 86f06d2a2c9588a2210a9f78f47e73f1b7c5e329..f943179d0071ed77fe49f5e4e9cff05eeff665e0 100644
|
||||
--- a/ui/views/win/hwnd_message_handler.cc
|
||||
+++ b/ui/views/win/hwnd_message_handler.cc
|
||||
@@ -3049,15 +3049,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message,
|
||||
SetMsgHandled(FALSE);
|
||||
// We must let Windows handle the caption buttons if it's drawing them, or
|
||||
// they won't work.
|
||||
+ bool simulate_mouse_event_for_caption = false;
|
||||
if (delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN &&
|
||||
(hittest == HTCLOSE || hittest == HTMINBUTTON ||
|
||||
hittest == HTMAXBUTTON)) {
|
||||
- SetMsgHandled(FALSE);
|
||||
+ simulate_mouse_event_for_caption =
|
||||
+ delegate_->HandleMouseEventForCaption(message);
|
||||
+ if (!simulate_mouse_event_for_caption)
|
||||
+ SetMsgHandled(FALSE);
|
||||
}
|
||||
// Let resize events fall through. Ignore everything else, as we're either
|
||||
// letting Windows handle it above or we've already handled the equivalent
|
||||
// touch message.
|
||||
- if (!IsHitTestOnResizeHandle(hittest))
|
||||
+ if (!IsHitTestOnResizeHandle(hittest) && !simulate_mouse_event_for_caption)
|
||||
return 0;
|
||||
}
|
||||
|
||||
diff --git a/ui/views/win/hwnd_message_handler_delegate.h b/ui/views/win/hwnd_message_handler_delegate.h
|
||||
index 5dbb192d0840ca0ded61397c399b774a8cb05cce..098a9c3140e9e140fdc8f0dc9cf4e8ec84451221 100644
|
||||
--- a/ui/views/win/hwnd_message_handler_delegate.h
|
||||
+++ b/ui/views/win/hwnd_message_handler_delegate.h
|
||||
@@ -258,6 +258,10 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate {
|
||||
// Called when the window scale factor has changed.
|
||||
virtual void HandleWindowScaleFactorChanged(float window_scale_factor) = 0;
|
||||
|
||||
+ // Called when synthetic mouse event is generated for touch event on
|
||||
+ // caption buttons.
|
||||
+ virtual bool HandleMouseEventForCaption(UINT message) const = 0;
|
||||
+
|
||||
protected:
|
||||
virtual ~HWNDMessageHandlerDelegate() = default;
|
||||
};
|
||||
43
patches/chromium/custom_protocols_plzserviceworker.patch
Normal file
43
patches/chromium/custom_protocols_plzserviceworker.patch
Normal file
@@ -0,0 +1,43 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Fri, 20 May 2022 00:29:34 +0900
|
||||
Subject: custom_protocols_plzserviceworker.patch
|
||||
|
||||
Allow registering custom protocols to handle service worker main script fetching with PlzServiceWorker.
|
||||
|
||||
Refs https://bugs.chromium.org/p/chromium/issues/detail?id=996511
|
||||
|
||||
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
|
||||
index 4ce769314c6dddae41d915c7f05916f77f26a86e..d8f6882f2dee93fc127e7617abbcc7b71a65775d 100644
|
||||
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
|
||||
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
|
||||
@@ -1600,6 +1600,29 @@ ServiceWorkerContextWrapper::GetLoaderFactoryForBrowserInitiatedRequest(
|
||||
std::unique_ptr<network::PendingSharedURLLoaderFactory>
|
||||
loader_factory_bundle_info =
|
||||
context()->loader_factory_bundle_for_update_check()->Clone();
|
||||
+
|
||||
+ // Give the embedder a chance to register custom schemes that can
|
||||
+ // handle loading the service worker main script.
|
||||
+ // Previous registration triggered by
|
||||
+ // ServiceWorkerContextWrapper::CreateNonNetworkPendingURLLoaderFactoryBundleForUpdateCheck
|
||||
+ // happens early on browser startup before the JS in the main process
|
||||
+ // is run by the embedder.
|
||||
+ auto* factory_bundle = static_cast<blink::PendingURLLoaderFactoryBundle*>(
|
||||
+ loader_factory_bundle_info.get());
|
||||
+ ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories;
|
||||
+ GetContentClient()
|
||||
+ ->browser()
|
||||
+ ->RegisterNonNetworkServiceWorkerUpdateURLLoaderFactories(
|
||||
+ storage_partition_->browser_context(), &non_network_factories);
|
||||
+ for (auto& pair : non_network_factories) {
|
||||
+ const std::string& scheme = pair.first;
|
||||
+ mojo::PendingRemote<network::mojom::URLLoaderFactory>& factory_remote =
|
||||
+ pair.second;
|
||||
+
|
||||
+ factory_bundle->pending_scheme_specific_factories().emplace(
|
||||
+ scheme, std::move(factory_remote));
|
||||
+ }
|
||||
+
|
||||
static_cast<blink::PendingURLLoaderFactoryBundle*>(
|
||||
loader_factory_bundle_info.get())
|
||||
->pending_default_factory() = std::move(remote);
|
||||
@@ -20,14 +20,15 @@ to deal with color spaces. That is being tracked at
|
||||
https://crbug.com/634542 and https://crbug.com/711107.
|
||||
|
||||
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
|
||||
index 3824e500fd88105e9c6d459f23097b2b4f0686ed..927ede7608a5c6992e9f9fb05e4b8e43b7e10cf8 100644
|
||||
index 3824e500fd88105e9c6d459f23097b2b4f0686ed..958c592de85a194d5c2ba640a64549afedbb73c1 100644
|
||||
--- a/cc/trees/layer_tree_host_impl.cc
|
||||
+++ b/cc/trees/layer_tree_host_impl.cc
|
||||
@@ -1889,6 +1889,9 @@ void LayerTreeHostImpl::SetIsLikelyToRequireADraw(
|
||||
@@ -1889,6 +1889,10 @@ void LayerTreeHostImpl::SetIsLikelyToRequireADraw(
|
||||
TargetColorParams LayerTreeHostImpl::GetTargetColorParams(
|
||||
gfx::ContentColorUsage content_color_usage) const {
|
||||
TargetColorParams params;
|
||||
+ if (!settings_.enable_color_correct_rendering) {
|
||||
+ params.color_space = gfx::ColorSpace();
|
||||
+ return params;
|
||||
+ }
|
||||
|
||||
@@ -260,7 +261,7 @@ index 5f2451eefad211c85460eb457ad3d6e184540d59..d9d8352c6b1b8db8d86ad1ed1d4a3d30
|
||||
sandbox::policy::switches::kDisableSeccompFilterSandbox,
|
||||
sandbox::policy::switches::kNoSandbox,
|
||||
diff --git a/third_party/blink/renderer/platform/graphics/canvas_color_params.cc b/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
|
||||
index 6260d73068636f4a8d4c73c161a4ffe165b267d0..2686ef0dfb7d8b667647d11fece5aa8e1be76fe5 100644
|
||||
index 6260d73068636f4a8d4c73c161a4ffe165b267d0..2d86f6829b9ad8927a03f288cc78d6ce65d7c5c0 100644
|
||||
--- a/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
|
||||
+++ b/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
|
||||
@@ -4,6 +4,7 @@
|
||||
@@ -279,7 +280,18 @@ index 6260d73068636f4a8d4c73c161a4ffe165b267d0..2686ef0dfb7d8b667647d11fece5aa8e
|
||||
|
||||
namespace blink {
|
||||
|
||||
@@ -118,6 +120,11 @@ uint8_t CanvasColorParams::BytesPerPixel() const {
|
||||
@@ -21,6 +23,10 @@ namespace {
|
||||
// Level 4 specification.
|
||||
gfx::ColorSpace PredefinedColorSpaceToGfxColorSpace(
|
||||
PredefinedColorSpace color_space) {
|
||||
+ auto* cmd_line = base::CommandLine::ForCurrentProcess();
|
||||
+ if (cmd_line->HasSwitch(switches::kDisableColorCorrectRendering)) {
|
||||
+ return gfx::ColorSpace();
|
||||
+ }
|
||||
switch (color_space) {
|
||||
case PredefinedColorSpace::kSRGB:
|
||||
return gfx::ColorSpace::CreateSRGB();
|
||||
@@ -118,6 +124,11 @@ uint8_t CanvasColorParams::BytesPerPixel() const {
|
||||
}
|
||||
|
||||
gfx::ColorSpace CanvasColorParams::GetStorageGfxColorSpace() const {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Dominik=20Inf=C3=BChr?= <dinfuehr@chromium.org>
|
||||
Date: Tue, 22 Mar 2022 17:33:03 +0000
|
||||
Subject: Do not reduce page size from 64K to 4K on Linux/ARM64
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This fixes build on Asahi Linux which uses 16K pages.
|
||||
|
||||
Change-Id: I8cf3664849d98bcb984f339ebf9076d1cfaf5701
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3542265
|
||||
Reviewed-by: Andrew Grieve <agrieve@chromium.org>
|
||||
Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#983900}
|
||||
|
||||
diff --git a/build/config/android/BUILD.gn b/build/config/android/BUILD.gn
|
||||
index 4fc5565e27a596fe4aa1a093cfc0567fbd24b2a0..69341cea35d0c44aeb5f09b39783bd2abfcdcc43 100644
|
||||
--- a/build/config/android/BUILD.gn
|
||||
+++ b/build/config/android/BUILD.gn
|
||||
@@ -47,6 +47,13 @@ config("compiler") {
|
||||
"-Wl,--exclude-libs=libvpx_assembly_arm.a",
|
||||
]
|
||||
|
||||
+ if (current_cpu == "arm64") {
|
||||
+ # Reduce the page size from 65536 in order to reduce binary size slightly
|
||||
+ # by shrinking the alignment gap between segments. This also causes all
|
||||
+ # segments to be mapped adjacently, which breakpad relies on.
|
||||
+ ldflags += [ "-Wl,-z,max-page-size=4096" ]
|
||||
+ }
|
||||
+
|
||||
if (current_cpu == "arm64") {
|
||||
if (arm_control_flow_integrity == "standard") {
|
||||
cflags += [ "-mbranch-protection=standard" ]
|
||||
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
|
||||
index 7b9ce0172eedf2b55fe7f3de7baee2f2028f4a7b..8f932d6493ec84f89c69078f95db7fc7ad03ef05 100644
|
||||
--- a/build/config/compiler/BUILD.gn
|
||||
+++ b/build/config/compiler/BUILD.gn
|
||||
@@ -454,14 +454,7 @@ config("compiler") {
|
||||
|
||||
# Linux-specific compiler flags setup.
|
||||
# ------------------------------------
|
||||
- if ((is_posix || is_fuchsia) && !is_apple && use_lld) {
|
||||
- if (current_cpu == "arm64") {
|
||||
- # Reduce the page size from 65536 in order to reduce binary size slightly
|
||||
- # by shrinking the alignment gap between segments. This also causes all
|
||||
- # segments to be mapped adjacently, which breakpad relies on.
|
||||
- ldflags += [ "-Wl,-z,max-page-size=4096" ]
|
||||
- }
|
||||
- } else if (use_gold) {
|
||||
+ if (use_gold) {
|
||||
ldflags += [ "-fuse-ld=gold" ]
|
||||
if (!is_android) {
|
||||
# On Android, this isn't needed. gcc in the NDK knows to look next to
|
||||
@@ -0,0 +1,348 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Raymond Zhao <raymondzhao@microsoft.com>
|
||||
Date: Tue, 7 Sep 2021 14:54:25 -0700
|
||||
Subject: feat: Add data parameter to ProcessSingleton
|
||||
|
||||
This patch adds an additional_data parameter to the constructor of
|
||||
ProcessSingleton, so that the second instance can send additional
|
||||
data over to the first instance while requesting the ProcessSingleton
|
||||
lock.
|
||||
|
||||
On the Electron side, we then expose an extra parameter to the
|
||||
app.requestSingleInstanceLock API so that users can pass in a JSON
|
||||
object for the second instance to send to the first instance.
|
||||
|
||||
diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
|
||||
index 5a64220aaf1309832dc0ad543e353de67fe0a779..e75c4f0d7cf1cac2e5862eb858800359e2001eb6 100644
|
||||
--- a/chrome/browser/process_singleton.h
|
||||
+++ b/chrome/browser/process_singleton.h
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/process/process.h"
|
||||
+#include "base/containers/span.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
|
||||
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
|
||||
@@ -99,22 +100,25 @@ class ProcessSingleton {
|
||||
// handled within the current browser instance or false if the remote process
|
||||
// should handle it (i.e., because the current process is shutting down).
|
||||
using NotificationCallback =
|
||||
- base::RepeatingCallback<bool(const base::CommandLine& command_line,
|
||||
- const base::FilePath& current_directory)>;
|
||||
+ base::RepeatingCallback<bool(const base::CommandLine& command_line,
|
||||
+ const base::FilePath& current_directory,
|
||||
+ const std::vector<const uint8_t> additional_data)>;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
ProcessSingleton(const std::string& program_name,
|
||||
const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
bool is_sandboxed,
|
||||
const NotificationCallback& notification_callback);
|
||||
#else
|
||||
ProcessSingleton(const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
const NotificationCallback& notification_callback);
|
||||
+#endif
|
||||
|
||||
ProcessSingleton(const ProcessSingleton&) = delete;
|
||||
ProcessSingleton& operator=(const ProcessSingleton&) = delete;
|
||||
|
||||
-#endif
|
||||
~ProcessSingleton();
|
||||
|
||||
// Notify another process, if available. Otherwise sets ourselves as the
|
||||
@@ -177,7 +181,10 @@ class ProcessSingleton {
|
||||
#endif
|
||||
|
||||
private:
|
||||
- NotificationCallback notification_callback_; // Handler for notifications.
|
||||
+ // A callback to run when the first instance receives data from the second.
|
||||
+ NotificationCallback notification_callback_;
|
||||
+ // Custom data to pass to the other instance during notify.
|
||||
+ base::span<const uint8_t> additional_data_;
|
||||
|
||||
#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 7d3a441bdb64268ed5fbfa7bf589fb35a2fd1b75..a3e45e9baa09bfc87be5b7ff589ac76841c686d4 100644
|
||||
--- a/chrome/browser/process_singleton_posix.cc
|
||||
+++ b/chrome/browser/process_singleton_posix.cc
|
||||
@@ -614,6 +614,7 @@ class ProcessSingleton::LinuxWatcher
|
||||
// |reader| is for sending back ACK message.
|
||||
void HandleMessage(const std::string& current_dir,
|
||||
const std::vector<std::string>& argv,
|
||||
+ const std::vector<const uint8_t> additional_data,
|
||||
SocketReader* reader);
|
||||
|
||||
private:
|
||||
@@ -668,13 +669,16 @@ void ProcessSingleton::LinuxWatcher::StartListening(int socket) {
|
||||
}
|
||||
|
||||
void ProcessSingleton::LinuxWatcher::HandleMessage(
|
||||
- const std::string& current_dir, const std::vector<std::string>& argv,
|
||||
+ const std::string& current_dir,
|
||||
+ const std::vector<std::string>& argv,
|
||||
+ const std::vector<const uint8_t> additional_data,
|
||||
SocketReader* reader) {
|
||||
DCHECK(ui_task_runner_->BelongsToCurrentThread());
|
||||
DCHECK(reader);
|
||||
|
||||
if (parent_->notification_callback_.Run(base::CommandLine(argv),
|
||||
- base::FilePath(current_dir))) {
|
||||
+ base::FilePath(current_dir),
|
||||
+ std::move(additional_data))) {
|
||||
// Send back "ACK" message to prevent the client process from starting up.
|
||||
reader->FinishWithACK(kACKToken, base::size(kACKToken) - 1);
|
||||
} else {
|
||||
@@ -722,7 +726,8 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
|
||||
}
|
||||
}
|
||||
|
||||
- // Validate the message. The shortest message is kStartToken\0x\0x
|
||||
+ // Validate the message. The shortest message kStartToken\0\00
|
||||
+ // The shortest message with additional data is kStartToken\0\00\00\0.
|
||||
const size_t kMinMessageLength = base::size(kStartToken) + 4;
|
||||
if (bytes_read_ < kMinMessageLength) {
|
||||
buf_[bytes_read_] = 0;
|
||||
@@ -752,10 +757,28 @@ 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);
|
||||
+
|
||||
+ std::vector<const uint8_t> additional_data;
|
||||
+ if (tokens.size() >= 3 + num_args) {
|
||||
+ size_t additional_data_size;
|
||||
+ base::StringToSizeT(tokens[1 + num_args], &additional_data_size);
|
||||
+ std::string remaining_args = base::JoinString(
|
||||
+ base::make_span(tokens.begin() + 2 + num_args, tokens.end()),
|
||||
+ std::string(1, kTokenDelimiter));
|
||||
+ const uint8_t* additional_data_bits =
|
||||
+ reinterpret_cast<const uint8_t*>(remaining_args.c_str());
|
||||
+ additional_data = std::vector<const uint8_t>(
|
||||
+ additional_data_bits, additional_data_bits + additional_data_size);
|
||||
+ }
|
||||
+
|
||||
// Return to the UI thread to handle opening a new browser tab.
|
||||
ui_task_runner_->PostTask(
|
||||
FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::HandleMessage,
|
||||
- parent_, current_dir, tokens, this));
|
||||
+ parent_, current_dir, command_line,
|
||||
+ std::move(additional_data), this));
|
||||
fd_watch_controller_.reset();
|
||||
|
||||
// LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
|
||||
@@ -784,8 +807,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
|
||||
//
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
const NotificationCallback& notification_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
+ additional_data_(additional_data),
|
||||
current_pid_(base::GetCurrentProcId()),
|
||||
watcher_(new LinuxWatcher(this)) {
|
||||
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
|
||||
@@ -904,7 +929,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
sizeof(socket_timeout));
|
||||
|
||||
// Found another process, prepare our command line
|
||||
- // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>".
|
||||
+ // format is "START\0<current-dir>\0<n-args>\0<argv[0]>\0...\0<argv[n]>
|
||||
+ // \0<additional-data-length>\0<additional-data>".
|
||||
std::string to_send(kStartToken);
|
||||
to_send.push_back(kTokenDelimiter);
|
||||
|
||||
@@ -914,11 +940,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
to_send.append(current_dir.value());
|
||||
|
||||
const std::vector<std::string>& argv = cmd_line.argv();
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(base::NumberToString(argv.size()));
|
||||
for (auto it = argv.begin(); it != argv.end(); ++it) {
|
||||
to_send.push_back(kTokenDelimiter);
|
||||
to_send.append(*it);
|
||||
}
|
||||
|
||||
+ size_t data_to_send_size = additional_data_.size_bytes();
|
||||
+ if (data_to_send_size) {
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(base::NumberToString(data_to_send_size));
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(reinterpret_cast<const char*>(additional_data_.data()), data_to_send_size);
|
||||
+ }
|
||||
+
|
||||
// Send the message
|
||||
if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
|
||||
// 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 0ea5eb3e3cf055d981ab73486115bac53287f2d7..fe68beb4b2522d27e07dbbb3341f100f14494680 100644
|
||||
--- a/chrome/browser/process_singleton_win.cc
|
||||
+++ b/chrome/browser/process_singleton_win.cc
|
||||
@@ -80,10 +80,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
|
||||
|
||||
bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||
base::CommandLine* parsed_command_line,
|
||||
- base::FilePath* current_directory) {
|
||||
+ base::FilePath* current_directory,
|
||||
+ std::vector<const uint8_t>* parsed_additional_data) {
|
||||
// We should have enough room for the shortest command (min_message_size)
|
||||
// and also be a multiple of wchar_t bytes. The shortest command
|
||||
- // possible is L"START\0\0" (empty current directory and command line).
|
||||
+ // possible is L"START\0\0" (empty command line, current directory,
|
||||
+ // and additional data).
|
||||
static const int min_message_size = 7;
|
||||
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
|
||||
cds->cbData % sizeof(wchar_t) != 0) {
|
||||
@@ -133,6 +135,37 @@ 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);
|
||||
+
|
||||
+ const std::wstring::size_type fourth_null =
|
||||
+ msg.find_first_of(L'\0', third_null + 1);
|
||||
+ if (fourth_null == std::wstring::npos ||
|
||||
+ fourth_null == msg.length()) {
|
||||
+ // No additional data was provided.
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // Get length of the additional data.
|
||||
+ const std::wstring additional_data_length_string =
|
||||
+ msg.substr(third_null + 1, fourth_null - third_null);
|
||||
+ size_t additional_data_length;
|
||||
+ base::StringToSizeT(additional_data_length_string, &additional_data_length);
|
||||
+
|
||||
+ const std::wstring::size_type fifth_null =
|
||||
+ msg.find_first_of(L'\0', fourth_null + 1);
|
||||
+ if (fifth_null == std::wstring::npos ||
|
||||
+ fifth_null == msg.length()) {
|
||||
+ LOG(WARNING) << "Invalid format for start command, we need a string in 6 "
|
||||
+ "parts separated by NULLs";
|
||||
+ }
|
||||
+
|
||||
+ // Get the actual additional data.
|
||||
+ const std::wstring additional_data =
|
||||
+ msg.substr(fourth_null + 1, fifth_null - fourth_null);
|
||||
+ const uint8_t* additional_data_bytes =
|
||||
+ reinterpret_cast<const uint8_t*>(additional_data.c_str());
|
||||
+ *parsed_additional_data = std::vector<const uint8_t>(additional_data_bytes,
|
||||
+ additional_data_bytes + additional_data_length);
|
||||
+
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -154,13 +187,14 @@ bool ProcessLaunchNotification(
|
||||
|
||||
base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
|
||||
base::FilePath current_directory;
|
||||
- if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) {
|
||||
+ std::vector<const uint8_t> additional_data;
|
||||
+ if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory, &additional_data)) {
|
||||
*result = TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
- *result = notification_callback.Run(parsed_command_line, current_directory) ?
|
||||
- TRUE : FALSE;
|
||||
+ *result = notification_callback.Run(parsed_command_line,
|
||||
+ current_directory, std::move(additional_data)) ? TRUE : FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -254,9 +288,11 @@ bool ProcessSingleton::EscapeVirtualization(
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const std::string& program_name,
|
||||
const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
bool is_app_sandboxed,
|
||||
const NotificationCallback& notification_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
+ additional_data_(additional_data),
|
||||
program_name_(program_name),
|
||||
is_app_sandboxed_(is_app_sandboxed),
|
||||
is_virtualized_(false),
|
||||
@@ -283,7 +319,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
||||
return PROCESS_NONE;
|
||||
}
|
||||
|
||||
- switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
|
||||
+ switch (chrome::AttemptToNotifyRunningChrome(remote_window_, additional_data_)) {
|
||||
case chrome::NOTIFY_SUCCESS:
|
||||
return PROCESS_NOTIFIED;
|
||||
case chrome::NOTIFY_FAILED:
|
||||
diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
|
||||
index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..cfdb2d75532d270e3dd548eb7475a6cdbddf1016 100644
|
||||
--- a/chrome/browser/win/chrome_process_finder.cc
|
||||
+++ b/chrome/browser/win/chrome_process_finder.cc
|
||||
@@ -36,7 +36,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
|
||||
return base::win::MessageWindow::FindWindow(user_data_dir.value());
|
||||
}
|
||||
|
||||
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
+NotifyChromeResult AttemptToNotifyRunningChrome(
|
||||
+ HWND remote_window,
|
||||
+ const base::span<const uint8_t> additional_data) {
|
||||
TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome");
|
||||
|
||||
DCHECK(remote_window);
|
||||
@@ -50,7 +52,8 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
}
|
||||
|
||||
// Send the command line to the remote chrome window.
|
||||
- // Format is "START\0<<<current directory>>>\0<<<commandline>>>".
|
||||
+ // Format is
|
||||
+ // "START\0<current-directory>\0<command-line>\0<additional-data-length>\0<additional-data>".
|
||||
std::wstring to_send(L"START\0", 6); // want the NULL in the string.
|
||||
base::FilePath cur_dir;
|
||||
if (!base::GetCurrentDirectory(&cur_dir)) {
|
||||
@@ -64,6 +67,22 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
base::CommandLine::ForCurrentProcess()->GetCommandLineString());
|
||||
to_send.append(L"\0", 1); // Null separator.
|
||||
|
||||
+ size_t additional_data_size = additional_data.size_bytes();
|
||||
+ if (additional_data_size) {
|
||||
+ // Send over the size, because the reinterpret cast to wchar_t could
|
||||
+ // add padding.
|
||||
+ to_send.append(base::UTF8ToWide(base::NumberToString(additional_data_size)));
|
||||
+ to_send.append(L"\0", 1); // Null separator.
|
||||
+
|
||||
+ 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);
|
||||
+ to_send.append(L"\0", 1); // Null separator.
|
||||
+ }
|
||||
+
|
||||
// Allow the current running browser window to make itself the foreground
|
||||
// window (otherwise it will just flash in the taskbar).
|
||||
::AllowSetForegroundWindow(process_id);
|
||||
diff --git a/chrome/browser/win/chrome_process_finder.h b/chrome/browser/win/chrome_process_finder.h
|
||||
index 5516673cee019f6060077091e59498bf9038cd6e..8edea5079b46c2cba67833114eb9c21d85cfc22d 100644
|
||||
--- a/chrome/browser/win/chrome_process_finder.h
|
||||
+++ b/chrome/browser/win/chrome_process_finder.h
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
+#include "base/containers/span.h"
|
||||
#include "base/time/time.h"
|
||||
|
||||
namespace base {
|
||||
@@ -27,7 +28,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir);
|
||||
// Attempts to send the current command line to an already running instance of
|
||||
// Chrome via a WM_COPYDATA message.
|
||||
// Returns true if a running Chrome is found and successfully notified.
|
||||
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window);
|
||||
+NotifyChromeResult AttemptToNotifyRunningChrome(
|
||||
+ HWND remote_window,
|
||||
+ const base::span<const uint8_t> additional_data);
|
||||
|
||||
// Changes the notification timeout to |new_timeout|, returns the old timeout.
|
||||
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout);
|
||||
@@ -1,639 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Raymond Zhao <raymondzhao@microsoft.com>
|
||||
Date: Tue, 7 Sep 2021 14:54:25 -0700
|
||||
Subject: feat: Add data transfer mechanism to requestSingleInstanceLock flow
|
||||
|
||||
This patch adds code that allows for the second instance to send
|
||||
additional data to the first instance, and for the first instance
|
||||
to send additional data back to the second instance, during the
|
||||
app.requestSingleInstanceLock call.
|
||||
|
||||
Firstly, this patch adds an additional_data parameter
|
||||
to the constructor of ProcessSingleton, so that the second instance
|
||||
can send additional data over to the first instance
|
||||
while requesting the ProcessSingleton lock.
|
||||
|
||||
Then, we add additional processing to the second-instance event, both
|
||||
so the first instance can receive additional data from the second
|
||||
instance, but also so the second instance can send back additional
|
||||
data to the first instance if needed.
|
||||
|
||||
diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
|
||||
index 5a64220aaf1309832dc0ad543e353de67fe0a779..a568dd10d1ef8679d66f4cdc6a471c251cbcd4eb 100644
|
||||
--- a/chrome/browser/process_singleton.h
|
||||
+++ b/chrome/browser/process_singleton.h
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/process/process.h"
|
||||
+#include "base/containers/span.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
|
||||
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
|
||||
@@ -93,6 +94,9 @@ class ProcessSingleton {
|
||||
|
||||
static constexpr int kNumNotifyResults = LAST_VALUE + 1;
|
||||
|
||||
+ using NotificationAckCallback =
|
||||
+ base::RepeatingCallback<void(const base::span<const uint8_t>* ack_data)>;
|
||||
+
|
||||
// Implement this callback to handle notifications from other processes. The
|
||||
// callback will receive the command line and directory with which the other
|
||||
// Chrome process was launched. Return true if the command line will be
|
||||
@@ -100,21 +104,27 @@ class ProcessSingleton {
|
||||
// should handle it (i.e., because the current process is shutting down).
|
||||
using NotificationCallback =
|
||||
base::RepeatingCallback<bool(const base::CommandLine& command_line,
|
||||
- const base::FilePath& current_directory)>;
|
||||
+ const base::FilePath& current_directory,
|
||||
+ const std::vector<const uint8_t> additional_data,
|
||||
+ const NotificationAckCallback& ack_callback)>;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
ProcessSingleton(const std::string& program_name,
|
||||
const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
bool is_sandboxed,
|
||||
- const NotificationCallback& notification_callback);
|
||||
+ const NotificationCallback& notification_callback,
|
||||
+ const NotificationAckCallback& ack_notification_callback);
|
||||
#else
|
||||
ProcessSingleton(const base::FilePath& user_data_dir,
|
||||
- const NotificationCallback& notification_callback);
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
+ const NotificationCallback& notification_callback,
|
||||
+ const NotificationAckCallback& ack_notification_callback);
|
||||
+#endif
|
||||
|
||||
ProcessSingleton(const ProcessSingleton&) = delete;
|
||||
ProcessSingleton& operator=(const ProcessSingleton&) = delete;
|
||||
|
||||
-#endif
|
||||
~ProcessSingleton();
|
||||
|
||||
// Notify another process, if available. Otherwise sets ourselves as the
|
||||
@@ -177,7 +187,13 @@ class ProcessSingleton {
|
||||
#endif
|
||||
|
||||
private:
|
||||
- NotificationCallback notification_callback_; // Handler for notifications.
|
||||
+ // A callback to run when the first instance receives data from the second.
|
||||
+ NotificationCallback notification_callback_;
|
||||
+ // A callback to run when the second instance
|
||||
+ // receives an acknowledgement from the first.
|
||||
+ NotificationAckCallback notification_ack_callback_;
|
||||
+ // Custom data to pass to the other instance during notify.
|
||||
+ base::span<const uint8_t> additional_data_;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
bool EscapeVirtualization(const base::FilePath& user_data_dir);
|
||||
@@ -190,6 +206,7 @@ class ProcessSingleton {
|
||||
HANDLE lock_file_;
|
||||
base::FilePath user_data_dir_;
|
||||
ShouldKillRemoteProcessCallback should_kill_remote_process_callback_;
|
||||
+ HANDLE ack_pipe_;
|
||||
#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
|
||||
// Return true if the given pid is one of our child processes.
|
||||
// Assumes that the current pid is the root of all pids of the current
|
||||
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
|
||||
index 7d3a441bdb64268ed5fbfa7bf589fb35a2fd1b75..b23c16fde275fdba559abb1f30e42f65ddbfc332 100644
|
||||
--- a/chrome/browser/process_singleton_posix.cc
|
||||
+++ b/chrome/browser/process_singleton_posix.cc
|
||||
@@ -148,7 +148,7 @@ const char kACKToken[] = "ACK";
|
||||
const char kShutdownToken[] = "SHUTDOWN";
|
||||
const char kTokenDelimiter = '\0';
|
||||
const int kMaxMessageLength = 32 * 1024;
|
||||
-const int kMaxACKMessageLength = base::size(kShutdownToken) - 1;
|
||||
+const int kMaxACKMessageLength = kMaxMessageLength;
|
||||
|
||||
bool g_disable_prompt = false;
|
||||
bool g_skip_is_chrome_process_check = false;
|
||||
@@ -614,6 +614,7 @@ class ProcessSingleton::LinuxWatcher
|
||||
// |reader| is for sending back ACK message.
|
||||
void HandleMessage(const std::string& current_dir,
|
||||
const std::vector<std::string>& argv,
|
||||
+ const std::vector<const uint8_t> additional_data,
|
||||
SocketReader* reader);
|
||||
|
||||
private:
|
||||
@@ -638,6 +639,9 @@ class ProcessSingleton::LinuxWatcher
|
||||
// The ProcessSingleton that owns us.
|
||||
ProcessSingleton* const parent_;
|
||||
|
||||
+ bool ack_callback_called_ = false;
|
||||
+ void AckCallback(SocketReader* reader, const base::span<const uint8_t>* response);
|
||||
+
|
||||
std::set<std::unique_ptr<SocketReader>, base::UniquePtrComparator> readers_;
|
||||
};
|
||||
|
||||
@@ -668,16 +672,21 @@ void ProcessSingleton::LinuxWatcher::StartListening(int socket) {
|
||||
}
|
||||
|
||||
void ProcessSingleton::LinuxWatcher::HandleMessage(
|
||||
- const std::string& current_dir, const std::vector<std::string>& argv,
|
||||
+ const std::string& current_dir,
|
||||
+ const std::vector<std::string>& argv,
|
||||
+ const std::vector<const uint8_t> additional_data,
|
||||
SocketReader* reader) {
|
||||
DCHECK(ui_task_runner_->BelongsToCurrentThread());
|
||||
DCHECK(reader);
|
||||
|
||||
- if (parent_->notification_callback_.Run(base::CommandLine(argv),
|
||||
- base::FilePath(current_dir))) {
|
||||
- // Send back "ACK" message to prevent the client process from starting up.
|
||||
- reader->FinishWithACK(kACKToken, base::size(kACKToken) - 1);
|
||||
- } else {
|
||||
+ auto wrapped_ack_callback =
|
||||
+ base::BindRepeating(&ProcessSingleton::LinuxWatcher::AckCallback,
|
||||
+ base::Unretained(this), reader);
|
||||
+ ack_callback_called_ = false;
|
||||
+ if (!parent_->notification_callback_.Run(base::CommandLine(argv),
|
||||
+ base::FilePath(current_dir),
|
||||
+ std::move(additional_data),
|
||||
+ wrapped_ack_callback)) {
|
||||
LOG(WARNING) << "Not handling interprocess notification as browser"
|
||||
" is shutting down";
|
||||
// Send back "SHUTDOWN" message, so that the client process can start up
|
||||
@@ -687,6 +696,22 @@ void ProcessSingleton::LinuxWatcher::HandleMessage(
|
||||
}
|
||||
}
|
||||
|
||||
+void ProcessSingleton::LinuxWatcher::AckCallback(
|
||||
+ SocketReader* reader,
|
||||
+ const base::span<const uint8_t>* response) {
|
||||
+ // Send back "ACK" message to prevent the client process from starting up.
|
||||
+ if (!ack_callback_called_) {
|
||||
+ ack_callback_called_ = true;
|
||||
+ std::string ack_message;
|
||||
+ ack_message.append(kACKToken, base::size(kACKToken) - 1);
|
||||
+ if (response && response->size_bytes()) {
|
||||
+ ack_message.append(reinterpret_cast<const char*>(response->data()),
|
||||
+ response->size_bytes());
|
||||
+ }
|
||||
+ reader->FinishWithACK(ack_message.c_str(), ack_message.size());
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void ProcessSingleton::LinuxWatcher::RemoveSocketReader(SocketReader* reader) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
DCHECK(reader);
|
||||
@@ -722,7 +747,8 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
|
||||
}
|
||||
}
|
||||
|
||||
- // Validate the message. The shortest message is kStartToken\0x\0x
|
||||
+ // Validate the message. The shortest message kStartToken\0\00
|
||||
+ // The shortest message with additional data is kStartToken\0\00\00\0.
|
||||
const size_t kMinMessageLength = base::size(kStartToken) + 4;
|
||||
if (bytes_read_ < kMinMessageLength) {
|
||||
buf_[bytes_read_] = 0;
|
||||
@@ -752,10 +778,28 @@ 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);
|
||||
+
|
||||
+ std::vector<const uint8_t> additional_data;
|
||||
+ if (tokens.size() >= 3 + num_args) {
|
||||
+ size_t additional_data_size;
|
||||
+ base::StringToSizeT(tokens[1 + num_args], &additional_data_size);
|
||||
+ std::string remaining_args = base::JoinString(
|
||||
+ base::make_span(tokens.begin() + 2 + num_args, tokens.end()),
|
||||
+ std::string(1, kTokenDelimiter));
|
||||
+ const uint8_t* additional_data_bits =
|
||||
+ reinterpret_cast<const uint8_t*>(remaining_args.c_str());
|
||||
+ additional_data = std::vector<const uint8_t>(additional_data_bits,
|
||||
+ additional_data_bits + additional_data_size);
|
||||
+ }
|
||||
+
|
||||
// Return to the UI thread to handle opening a new browser tab.
|
||||
ui_task_runner_->PostTask(
|
||||
FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::HandleMessage,
|
||||
- parent_, current_dir, tokens, this));
|
||||
+ parent_, current_dir, command_line,
|
||||
+ std::move(additional_data), this));
|
||||
fd_watch_controller_.reset();
|
||||
|
||||
// LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
|
||||
@@ -784,8 +828,12 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
|
||||
//
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const base::FilePath& user_data_dir,
|
||||
- const NotificationCallback& notification_callback)
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
+ const NotificationCallback& notification_callback,
|
||||
+ const NotificationAckCallback& notification_ack_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
+ notification_ack_callback_(notification_ack_callback),
|
||||
+ additional_data_(additional_data),
|
||||
current_pid_(base::GetCurrentProcId()),
|
||||
watcher_(new LinuxWatcher(this)) {
|
||||
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
|
||||
@@ -904,7 +952,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
sizeof(socket_timeout));
|
||||
|
||||
// Found another process, prepare our command line
|
||||
- // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>".
|
||||
+ // format is "START\0<current-dir>\0<n-args>\0<argv[0]>\0...\0<argv[n]>
|
||||
+ // \0<additional-data-length>\0<additional-data>".
|
||||
std::string to_send(kStartToken);
|
||||
to_send.push_back(kTokenDelimiter);
|
||||
|
||||
@@ -914,11 +963,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
to_send.append(current_dir.value());
|
||||
|
||||
const std::vector<std::string>& argv = cmd_line.argv();
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(base::NumberToString(argv.size()));
|
||||
for (auto it = argv.begin(); it != argv.end(); ++it) {
|
||||
to_send.push_back(kTokenDelimiter);
|
||||
to_send.append(*it);
|
||||
}
|
||||
|
||||
+ size_t data_to_send_size = additional_data_.size_bytes();
|
||||
+ if (data_to_send_size) {
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(base::NumberToString(data_to_send_size));
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(reinterpret_cast<const char*>(additional_data_.data()), data_to_send_size);
|
||||
+ }
|
||||
+
|
||||
// Send the message
|
||||
if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
|
||||
// Try to kill the other process, because it might have been dead.
|
||||
@@ -960,6 +1019,17 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
linux_ui->NotifyWindowManagerStartupComplete();
|
||||
#endif
|
||||
|
||||
+ size_t ack_data_len = len - (base::size(kACKToken) - 1);
|
||||
+ if (ack_data_len) {
|
||||
+ const uint8_t* raw_ack_data =
|
||||
+ reinterpret_cast<const uint8_t*>(buf + base::size(kACKToken) - 1);
|
||||
+ base::span<const uint8_t> ack_data =
|
||||
+ base::make_span(raw_ack_data, raw_ack_data + ack_data_len);
|
||||
+ notification_ack_callback_.Run(&ack_data);
|
||||
+ } else {
|
||||
+ notification_ack_callback_.Run(nullptr);
|
||||
+ }
|
||||
+
|
||||
// Assume the other process is handling the request.
|
||||
return PROCESS_NOTIFIED;
|
||||
}
|
||||
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
|
||||
index 0ea5eb3e3cf055d981ab73486115bac53287f2d7..d9a47fc419b7d42e31498cc2d803560984f00810 100644
|
||||
--- a/chrome/browser/process_singleton_win.cc
|
||||
+++ b/chrome/browser/process_singleton_win.cc
|
||||
@@ -13,14 +13,17 @@
|
||||
#include "base/command_line.h"
|
||||
#include "base/debug/activity_tracker.h"
|
||||
#include "base/files/file_path.h"
|
||||
+#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
#include "base/process/process.h"
|
||||
#include "base/process/process_info.h"
|
||||
+#include "base/rand_util.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
+#include "base/timer/timer.h"
|
||||
#include "base/trace_event/base_tracing.h"
|
||||
#include "base/win/registry.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
@@ -45,6 +48,13 @@
|
||||
namespace {
|
||||
|
||||
const char kLockfile[] = "lockfile";
|
||||
+const DWORD kPipeTimeout = 10000;
|
||||
+const DWORD kMaxMessageLength = 32 * 1024;
|
||||
+
|
||||
+std::unique_ptr<std::vector<const uint8_t>> g_ack_data;
|
||||
+base::OneShotTimer g_ack_timer;
|
||||
+HANDLE g_write_ack_pipe;
|
||||
+bool g_write_ack_callback_called = false;
|
||||
|
||||
// A helper class that acquires the given |mutex| while the AutoLockMutex is in
|
||||
// scope.
|
||||
@@ -80,10 +90,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
|
||||
|
||||
bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||
base::CommandLine* parsed_command_line,
|
||||
- base::FilePath* current_directory) {
|
||||
+ base::FilePath* current_directory,
|
||||
+ std::vector<const uint8_t>* parsed_additional_data) {
|
||||
// We should have enough room for the shortest command (min_message_size)
|
||||
// and also be a multiple of wchar_t bytes. The shortest command
|
||||
- // possible is L"START\0\0" (empty current directory and command line).
|
||||
+ // possible is L"START\0\0" (empty command line, current directory,
|
||||
+ // and additional data).
|
||||
static const int min_message_size = 7;
|
||||
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
|
||||
cds->cbData % sizeof(wchar_t) != 0) {
|
||||
@@ -133,11 +145,82 @@ 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);
|
||||
+
|
||||
+ const std::wstring::size_type fourth_null =
|
||||
+ msg.find_first_of(L'\0', third_null + 1);
|
||||
+ if (fourth_null == std::wstring::npos ||
|
||||
+ fourth_null == msg.length()) {
|
||||
+ // No additional data was provided.
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // Get length of the additional data.
|
||||
+ const std::wstring additional_data_length_string =
|
||||
+ msg.substr(third_null + 1, fourth_null - third_null);
|
||||
+ size_t additional_data_length;
|
||||
+ base::StringToSizeT(additional_data_length_string, &additional_data_length);
|
||||
+
|
||||
+ const std::wstring::size_type fifth_null =
|
||||
+ msg.find_first_of(L'\0', fourth_null + 1);
|
||||
+ if (fifth_null == std::wstring::npos ||
|
||||
+ fifth_null == msg.length()) {
|
||||
+ LOG(WARNING) << "Invalid format for start command, we need a string in 6 "
|
||||
+ "parts separated by NULLs";
|
||||
+ }
|
||||
+
|
||||
+ // Get the actual additional data.
|
||||
+ const std::wstring additional_data =
|
||||
+ msg.substr(fourth_null + 1, fifth_null - fourth_null);
|
||||
+ const uint8_t* additional_data_bytes =
|
||||
+ reinterpret_cast<const uint8_t*>(additional_data.c_str());
|
||||
+ *parsed_additional_data = std::vector<const uint8_t>(additional_data_bytes,
|
||||
+ additional_data_bytes + additional_data_length);
|
||||
+
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+void StoreAck(const base::span<const uint8_t>* ack_data) {
|
||||
+ if (ack_data) {
|
||||
+ g_ack_data = std::make_unique<std::vector<const uint8_t>>(ack_data->begin(),
|
||||
+ ack_data->end());
|
||||
+ } else {
|
||||
+ g_ack_data = nullptr;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void SendBackAck() {
|
||||
+ // This is the first instance sending the ack back to the second instance.
|
||||
+ if (!g_write_ack_callback_called) {
|
||||
+ g_write_ack_callback_called = true;
|
||||
+ const uint8_t* data_buffer = nullptr;
|
||||
+ DWORD data_to_send_size = 0;
|
||||
+ if (g_ack_data) {
|
||||
+ data_buffer = g_ack_data->data();
|
||||
+ DWORD ack_data_size = g_ack_data->size() * sizeof(uint8_t);
|
||||
+ data_to_send_size = (ack_data_size < kMaxMessageLength) ? ack_data_size : kMaxMessageLength;
|
||||
+ }
|
||||
+
|
||||
+ ::ConnectNamedPipe(g_write_ack_pipe, NULL);
|
||||
+
|
||||
+ DWORD bytes_written = 0;
|
||||
+ ::WriteFile(g_write_ack_pipe,
|
||||
+ (LPCVOID)data_buffer,
|
||||
+ data_to_send_size,
|
||||
+ &bytes_written,
|
||||
+ NULL);
|
||||
+ DCHECK(bytes_written == data_to_send_size);
|
||||
+
|
||||
+ ::FlushFileBuffers(g_write_ack_pipe);
|
||||
+ ::DisconnectNamedPipe(g_write_ack_pipe);
|
||||
+
|
||||
+ if (g_ack_data) {
|
||||
+ g_ack_data.reset();
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
bool ProcessLaunchNotification(
|
||||
const ProcessSingleton::NotificationCallback& notification_callback,
|
||||
UINT message,
|
||||
@@ -151,16 +234,35 @@ bool ProcessLaunchNotification(
|
||||
|
||||
// Handle the WM_COPYDATA message from another process.
|
||||
const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
|
||||
-
|
||||
base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
|
||||
base::FilePath current_directory;
|
||||
- if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) {
|
||||
+ std::vector<const uint8_t> additional_data;
|
||||
+ if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory,
|
||||
+ &additional_data)) {
|
||||
*result = TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
- *result = notification_callback.Run(parsed_command_line, current_directory) ?
|
||||
- TRUE : FALSE;
|
||||
+ // notification_callback.Run waits for StoreAck to
|
||||
+ // run to completion before moving onwards.
|
||||
+ // Therefore, we cannot directly send the SendBackAck
|
||||
+ // callback instead, as it would hang the program
|
||||
+ // during the ConnectNamedPipe call.
|
||||
+ g_write_ack_callback_called = false;
|
||||
+ *result = notification_callback.Run(parsed_command_line, current_directory,
|
||||
+ std::move(additional_data),
|
||||
+ base::BindRepeating(&StoreAck))
|
||||
+ ? TRUE
|
||||
+ : FALSE;
|
||||
+ if (*result) {
|
||||
+ // If *result is TRUE, we return NOTIFY_SUCCESS.
|
||||
+ // Only for that case does the second process read
|
||||
+ // the acknowledgement. Therefore, only send back
|
||||
+ // the acknowledgement if *result is TRUE,
|
||||
+ // otherwise the program hangs during the ConnectNamedPipe call.
|
||||
+ g_ack_timer.Start(FROM_HERE, base::Seconds(0),
|
||||
+ base::BindOnce(&SendBackAck));
|
||||
+ }
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -254,9 +356,13 @@ bool ProcessSingleton::EscapeVirtualization(
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const std::string& program_name,
|
||||
const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
bool is_app_sandboxed,
|
||||
- const NotificationCallback& notification_callback)
|
||||
+ const NotificationCallback& notification_callback,
|
||||
+ const NotificationAckCallback& notification_ack_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
+ notification_ack_callback_(notification_ack_callback),
|
||||
+ additional_data_(additional_data),
|
||||
program_name_(program_name),
|
||||
is_app_sandboxed_(is_app_sandboxed),
|
||||
is_virtualized_(false),
|
||||
@@ -271,6 +377,47 @@ ProcessSingleton::~ProcessSingleton() {
|
||||
::CloseHandle(lock_file_);
|
||||
}
|
||||
|
||||
+void ReadAck(const ProcessSingleton::NotificationAckCallback& ack_callback,
|
||||
+ const std::string program_name,
|
||||
+ base::FilePath& user_data_dir) {
|
||||
+ // We are reading the ack from the first instance.
|
||||
+ // First, wait for the pipe.
|
||||
+ HWND remote_window = chrome::FindRunningChromeWindow(user_data_dir);
|
||||
+ DWORD process_id;
|
||||
+ DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
|
||||
+ std::string identifier = base::NumberToString(process_id) +
|
||||
+ base::NumberToString(thread_id);
|
||||
+ std::wstring pipe_name = base::UTF8ToWide("\\\\.\\pipe\\" + identifier +
|
||||
+ program_name);
|
||||
+ const LPCWSTR w_pipe_name = pipe_name.c_str();
|
||||
+ ::WaitNamedPipe(w_pipe_name, NMPWAIT_USE_DEFAULT_WAIT);
|
||||
+
|
||||
+ HANDLE read_ack_pipe = ::CreateFile(w_pipe_name,
|
||||
+ GENERIC_READ,
|
||||
+ FILE_SHARE_READ,
|
||||
+ NULL,
|
||||
+ OPEN_EXISTING,
|
||||
+ FILE_ATTRIBUTE_NORMAL,
|
||||
+ NULL);
|
||||
+ CHECK(read_ack_pipe != INVALID_HANDLE_VALUE);
|
||||
+
|
||||
+ DWORD bytes_read;
|
||||
+ uint8_t read_ack_buffer[kMaxMessageLength];
|
||||
+ ::ReadFile(read_ack_pipe,
|
||||
+ (LPVOID)read_ack_buffer,
|
||||
+ kMaxMessageLength,
|
||||
+ &bytes_read,
|
||||
+ NULL);
|
||||
+
|
||||
+ if (!bytes_read) {
|
||||
+ ack_callback.Run(nullptr);
|
||||
+ } else {
|
||||
+ base::span<const uint8_t> out_span(read_ack_buffer, read_ack_buffer + bytes_read);
|
||||
+ ack_callback.Run(&out_span);
|
||||
+ }
|
||||
+ ::CloseHandle(read_ack_pipe);
|
||||
+}
|
||||
+
|
||||
// Code roughly based on Mozilla.
|
||||
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
||||
TRACE_EVENT0("startup", "ProcessSingleton::NotifyOtherProcess");
|
||||
@@ -283,8 +430,9 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
||||
return PROCESS_NONE;
|
||||
}
|
||||
|
||||
- switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
|
||||
+ switch (chrome::AttemptToNotifyRunningChrome(remote_window_, additional_data_)) {
|
||||
case chrome::NOTIFY_SUCCESS:
|
||||
+ ReadAck(notification_ack_callback_, program_name_, user_data_dir_);
|
||||
return PROCESS_NOTIFIED;
|
||||
case chrome::NOTIFY_FAILED:
|
||||
remote_window_ = NULL;
|
||||
@@ -422,6 +570,26 @@ bool ProcessSingleton::Create() {
|
||||
<< "Lock file can not be created! Error code: " << error;
|
||||
|
||||
if (lock_file_ != INVALID_HANDLE_VALUE) {
|
||||
+ // We are the first instance. Create a pipe to send out ack data.
|
||||
+ // Create a per-process pipename using a combination of the
|
||||
+ // username, process id, thread id, and program name. Pipe names max
|
||||
+ // at 256 characters, can include any character other than a backslash
|
||||
+ std::string identifier = base::NumberToString(::GetCurrentProcessId()) +
|
||||
+ base::NumberToString(::GetCurrentThreadId());
|
||||
+ std::wstring pipe_name = base::UTF8ToWide("\\\\.\\pipe\\" + identifier +
|
||||
+ program_name_);
|
||||
+ const LPCWSTR w_pipe_name = pipe_name.c_str();
|
||||
+ ack_pipe_ = ::CreateNamedPipe(w_pipe_name,
|
||||
+ PIPE_ACCESS_OUTBOUND,
|
||||
+ PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
|
||||
+ PIPE_UNLIMITED_INSTANCES,
|
||||
+ kMaxMessageLength,
|
||||
+ 0,
|
||||
+ kPipeTimeout,
|
||||
+ NULL);
|
||||
+ CHECK(ack_pipe_ != INVALID_HANDLE_VALUE);
|
||||
+ g_write_ack_pipe = ack_pipe_;
|
||||
+
|
||||
// Set the window's title to the path of our user data directory so
|
||||
// other Chrome instances can decide if they should forward to us.
|
||||
TRACE_EVENT0("startup", "ProcessSingleton::Create:CreateWindow");
|
||||
@@ -449,6 +617,7 @@ bool ProcessSingleton::Create() {
|
||||
}
|
||||
|
||||
void ProcessSingleton::Cleanup() {
|
||||
+ ::CloseHandle(ack_pipe_);
|
||||
}
|
||||
|
||||
void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
|
||||
diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
|
||||
index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..ce851d09d501ebcc6d6c4065e746e869d5275b2b 100644
|
||||
--- a/chrome/browser/win/chrome_process_finder.cc
|
||||
+++ b/chrome/browser/win/chrome_process_finder.cc
|
||||
@@ -36,9 +36,10 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
|
||||
return base::win::MessageWindow::FindWindow(user_data_dir.value());
|
||||
}
|
||||
|
||||
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
+NotifyChromeResult AttemptToNotifyRunningChrome(
|
||||
+ HWND remote_window,
|
||||
+ const base::span<const uint8_t> additional_data) {
|
||||
TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome");
|
||||
-
|
||||
DCHECK(remote_window);
|
||||
DWORD process_id = 0;
|
||||
DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
|
||||
@@ -50,7 +51,8 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
}
|
||||
|
||||
// Send the command line to the remote chrome window.
|
||||
- // Format is "START\0<<<current directory>>>\0<<<commandline>>>".
|
||||
+ // Format is
|
||||
+ // "START\0<current-directory>\0<command-line>\0<additional-data-length>\0<additional-data>".
|
||||
std::wstring to_send(L"START\0", 6); // want the NULL in the string.
|
||||
base::FilePath cur_dir;
|
||||
if (!base::GetCurrentDirectory(&cur_dir)) {
|
||||
@@ -64,6 +66,22 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
base::CommandLine::ForCurrentProcess()->GetCommandLineString());
|
||||
to_send.append(L"\0", 1); // Null separator.
|
||||
|
||||
+ size_t additional_data_size = additional_data.size_bytes();
|
||||
+ if (additional_data_size) {
|
||||
+ // Send over the size, because the reinterpret cast to wchar_t could
|
||||
+ // add padding.
|
||||
+ to_send.append(base::UTF8ToWide(base::NumberToString(additional_data_size)));
|
||||
+ to_send.append(L"\0", 1); // Null separator.
|
||||
+
|
||||
+ 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);
|
||||
+ to_send.append(L"\0", 1); // Null separator.
|
||||
+ }
|
||||
+
|
||||
// Allow the current running browser window to make itself the foreground
|
||||
// window (otherwise it will just flash in the taskbar).
|
||||
::AllowSetForegroundWindow(process_id);
|
||||
diff --git a/chrome/browser/win/chrome_process_finder.h b/chrome/browser/win/chrome_process_finder.h
|
||||
index 5516673cee019f6060077091e59498bf9038cd6e..8edea5079b46c2cba67833114eb9c21d85cfc22d 100644
|
||||
--- a/chrome/browser/win/chrome_process_finder.h
|
||||
+++ b/chrome/browser/win/chrome_process_finder.h
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
+#include "base/containers/span.h"
|
||||
#include "base/time/time.h"
|
||||
|
||||
namespace base {
|
||||
@@ -27,7 +28,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir);
|
||||
// Attempts to send the current command line to an already running instance of
|
||||
// Chrome via a WM_COPYDATA message.
|
||||
// Returns true if a running Chrome is found and successfully notified.
|
||||
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window);
|
||||
+NotifyChromeResult AttemptToNotifyRunningChrome(
|
||||
+ HWND remote_window,
|
||||
+ const base::span<const uint8_t> additional_data);
|
||||
|
||||
// Changes the notification timeout to |new_timeout|, returns the old timeout.
|
||||
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout);
|
||||
@@ -1,20 +1,22 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Mon, 25 Oct 2021 21:45:57 +0200
|
||||
Subject: fix: patch out permissions checks in exclusive_access
|
||||
Subject: fix: adapt exclusive_access for electron needs
|
||||
|
||||
This patch is necessary in order to properly enable
|
||||
navigator.keyboard.{(un)?lock}() functionality. We don't have a concept
|
||||
of PermissionManager nor of a Profile, so this would not affect usage of
|
||||
the API.
|
||||
|
||||
We might consider potentially using our own permissions handler,
|
||||
but it's not strictly necessary for this API to work to spec.
|
||||
|
||||
Profile check has been upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/3247196
|
||||
We also need to ensure that NotifyExclusiveTabAccessLost is called
|
||||
on all platforms in FullscreenController::ExitFullscreenModeInternal()
|
||||
and not just macOS, since Electron's native window impls report state
|
||||
change fairly instantly as well, and so pressing escape won't work on
|
||||
Linux or Windows to un-fullscreen in some circumstances without this
|
||||
change.
|
||||
|
||||
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
|
||||
index 9b2c91d39324b61afa49ccea6be2eda8308473ff..1652b52c5c752809348b3ab44d3703ac343c829d 100644
|
||||
index 9b2c91d39324b61afa49ccea6be2eda8308473ff..6817a9cd22ebb8adba2118a1bd8a32cfc065e8ea 100644
|
||||
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
|
||||
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
|
||||
@@ -382,13 +382,9 @@ void FullscreenController::EnterFullscreenModeInternal(
|
||||
@@ -49,3 +51,19 @@ index 9b2c91d39324b61afa49ccea6be2eda8308473ff..1652b52c5c752809348b3ab44d3703ac
|
||||
|
||||
if (option == BROWSER)
|
||||
base::RecordAction(base::UserMetricsAction("ToggleFullscreen"));
|
||||
@@ -439,12 +437,12 @@ void FullscreenController::ExitFullscreenModeInternal() {
|
||||
|
||||
RecordExitingUMA();
|
||||
toggled_into_fullscreen_ = false;
|
||||
-#if BUILDFLAG(IS_MAC)
|
||||
- // Mac windows report a state change instantly, and so we must also clear
|
||||
+
|
||||
+ // Electron native windows report a state change instantly, and so we must also clear
|
||||
// state_prior_to_tab_fullscreen_ to match them else other logic using
|
||||
// state_prior_to_tab_fullscreen_ will be incorrect.
|
||||
NotifyTabExclusiveAccessLost();
|
||||
-#endif
|
||||
+
|
||||
exclusive_access_manager()->context()->ExitFullscreen();
|
||||
extension_caused_fullscreen_ = GURL();
|
||||
|
||||
119
patches/chromium/fix_mac_build_with_enable_plugins_false.patch
Normal file
119
patches/chromium/fix_mac_build_with_enable_plugins_false.patch
Normal file
@@ -0,0 +1,119 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Lei Zhang <thestig@chromium.org>
|
||||
Date: Thu, 23 Jun 2022 17:54:12 +0000
|
||||
Subject: Fix Mac build with enable_plugins=false.
|
||||
|
||||
Remove spurious includes of plugin headers, and add appropriate #ifs.
|
||||
|
||||
Bug: 1027360
|
||||
Change-Id: I445252f5de14dff8e89ab371429a24ad3e57ca97
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3719213
|
||||
Reviewed-by: Robert Sesek <rsesek@chromium.org>
|
||||
Reviewed-by: Thomas Lukaszewicz <tluk@chromium.org>
|
||||
Commit-Queue: Lei Zhang <thestig@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1017248}
|
||||
|
||||
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
|
||||
index 0c5bfe154c4df19d69a91b5a54ba9e3700aa1a5a..d556360a52f4a1edf0d5f5d4558b6661590a101d 100644
|
||||
--- a/chrome/browser/app_controller_mac.mm
|
||||
+++ b/chrome/browser/app_controller_mac.mm
|
||||
@@ -109,7 +109,6 @@
|
||||
#include "components/prefs/pref_service.h"
|
||||
#include "components/sessions/core/tab_restore_service.h"
|
||||
#include "content/public/browser/download_manager.h"
|
||||
-#include "content/public/browser/plugin_service.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/extension_system.h"
|
||||
#include "net/base/filename_util.h"
|
||||
diff --git a/content/browser/sandbox_mac_unittest.mm b/content/browser/sandbox_mac_unittest.mm
|
||||
index a69810d9eb36ef940fb9a234d5f6610c4709516c..e7e40981b5ab965d79708e8de358d172dbe77378 100644
|
||||
--- a/content/browser/sandbox_mac_unittest.mm
|
||||
+++ b/content/browser/sandbox_mac_unittest.mm
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "content/browser/sandbox_parameters_mac.h"
|
||||
#include "content/common/mac/font_loader.h"
|
||||
#include "crypto/openssl_util.h"
|
||||
+#include "ppapi/buildflags/buildflags.h"
|
||||
#include "sandbox/mac/seatbelt.h"
|
||||
#include "sandbox/mac/seatbelt_exec.h"
|
||||
#include "sandbox/policy/mac/sandbox_mac.h"
|
||||
@@ -91,7 +92,9 @@ void ExecuteInAllSandboxTypes(const std::string& multiprocess_main,
|
||||
sandbox::mojom::Sandbox::kCdm,
|
||||
sandbox::mojom::Sandbox::kGpu,
|
||||
sandbox::mojom::Sandbox::kNaClLoader,
|
||||
+#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
sandbox::mojom::Sandbox::kPpapi,
|
||||
+#endif
|
||||
sandbox::mojom::Sandbox::kPrintBackend,
|
||||
sandbox::mojom::Sandbox::kPrintCompositor,
|
||||
sandbox::mojom::Sandbox::kRenderer,
|
||||
diff --git a/content/browser/sandbox_parameters_mac.mm b/content/browser/sandbox_parameters_mac.mm
|
||||
index 76a22420f605994ebe0a7ffa9a7f2a0535c2440f..cf8b251180177c3ea4f8ddb2450f0483d201ba2b 100644
|
||||
--- a/content/browser/sandbox_parameters_mac.mm
|
||||
+++ b/content/browser/sandbox_parameters_mac.mm
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/system/sys_info.h"
|
||||
#include "content/public/browser/content_browser_client.h"
|
||||
-#include "content/public/browser/plugin_service.h"
|
||||
#include "content/public/common/content_client.h"
|
||||
#include "content/public/common/content_features.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
@@ -34,6 +33,7 @@
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
+#include "content/public/browser/plugin_service.h"
|
||||
#include "content/public/common/pepper_plugin_info.h"
|
||||
#endif
|
||||
|
||||
@@ -229,11 +229,11 @@ void SetupSandboxParameters(sandbox::mojom::Sandbox sandbox_type,
|
||||
case sandbox::mojom::Sandbox::kNetwork:
|
||||
SetupNetworkSandboxParameters(client);
|
||||
break;
|
||||
- case sandbox::mojom::Sandbox::kPpapi:
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
+ case sandbox::mojom::Sandbox::kPpapi:
|
||||
SetupPPAPISandboxParameters(client);
|
||||
-#endif
|
||||
break;
|
||||
+#endif
|
||||
case sandbox::mojom::Sandbox::kNoSandbox:
|
||||
CHECK(false) << "Unhandled parameters for sandbox_type "
|
||||
<< static_cast<int>(sandbox_type);
|
||||
diff --git a/sandbox/policy/mac/sandbox_mac.mm b/sandbox/policy/mac/sandbox_mac.mm
|
||||
index 34f8b003c96b9fff00b666ae184b5076e5d6fd45..11392b99ecad98f2cf39e67d545fe1947ede1593 100644
|
||||
--- a/sandbox/policy/mac/sandbox_mac.mm
|
||||
+++ b/sandbox/policy/mac/sandbox_mac.mm
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "base/files/scoped_file.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
+#include "ppapi/buildflags/buildflags.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "sandbox/policy/mac/audio.sb.h"
|
||||
#include "sandbox/policy/mac/cdm.sb.h"
|
||||
@@ -69,9 +70,11 @@
|
||||
case sandbox::mojom::Sandbox::kNetwork:
|
||||
profile += kSeatbeltPolicyString_network;
|
||||
break;
|
||||
+#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
case sandbox::mojom::Sandbox::kPpapi:
|
||||
profile += kSeatbeltPolicyString_ppapi;
|
||||
break;
|
||||
+#endif
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
case sandbox::mojom::Sandbox::kPrintBackend:
|
||||
profile += kSeatbeltPolicyString_print_backend;
|
||||
diff --git a/ui/views_content_client/views_content_client_main_parts_mac.mm b/ui/views_content_client/views_content_client_main_parts_mac.mm
|
||||
index 044600ad464fc48a805bf3e47811808983d43fef..df23f797e4f0898d9dc7742e05d286f540bfdacb 100644
|
||||
--- a/ui/views_content_client/views_content_client_main_parts_mac.mm
|
||||
+++ b/ui/views_content_client/views_content_client_main_parts_mac.mm
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
#include "base/path_service.h"
|
||||
#include "content/public/browser/context_factory.h"
|
||||
-#include "content/public/browser/plugin_service.h"
|
||||
#include "content/public/common/content_paths.h"
|
||||
#include "content/public/common/result_codes.h"
|
||||
#include "content/shell/browser/shell_application_mac.h"
|
||||
@@ -0,0 +1,108 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Lei Zhang <thestig@chromium.org>
|
||||
Date: Thu, 23 Jun 2022 18:52:27 +0000
|
||||
Subject: Fix Windows build with enable_plugins=false.
|
||||
|
||||
Add some #if checks to Windows-only code so plugins enums are only
|
||||
referenced when plugins are enabled. Also only build
|
||||
plugin_list_unittest.cc when plugins are enabled.
|
||||
|
||||
Bug: 1027360
|
||||
Change-Id: I0d265ae711e5e3401076dc89d1d49329f423ca64
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3719402
|
||||
Reviewed-by: Alex Gough <ajgo@chromium.org>
|
||||
Commit-Queue: Lei Zhang <thestig@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1017281}
|
||||
|
||||
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
|
||||
index 2d3d64f06e3301b42c78e5987b09c5216573900d..b488d9d4f57f7fd05f0283a999911ee35f3aaab6 100644
|
||||
--- a/chrome/browser/chrome_content_browser_client.cc
|
||||
+++ b/chrome/browser/chrome_content_browser_client.cc
|
||||
@@ -3928,7 +3928,9 @@ std::wstring ChromeContentBrowserClient::GetAppContainerSidForSandboxType(
|
||||
return std::wstring();
|
||||
case sandbox::mojom::Sandbox::kGpu:
|
||||
return std::wstring();
|
||||
+#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
case sandbox::mojom::Sandbox::kPpapi:
|
||||
+#endif
|
||||
case sandbox::mojom::Sandbox::kNoSandbox:
|
||||
case sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges:
|
||||
case sandbox::mojom::Sandbox::kXrCompositing:
|
||||
@@ -4000,7 +4002,9 @@ bool ChromeContentBrowserClient::PreSpawnChild(
|
||||
break;
|
||||
case sandbox::mojom::Sandbox::kUtility:
|
||||
case sandbox::mojom::Sandbox::kGpu:
|
||||
+#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
case sandbox::mojom::Sandbox::kPpapi:
|
||||
+#endif
|
||||
case sandbox::mojom::Sandbox::kNoSandbox:
|
||||
case sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges:
|
||||
case sandbox::mojom::Sandbox::kXrCompositing:
|
||||
diff --git a/chrome/child/pdf_child_init.cc b/chrome/child/pdf_child_init.cc
|
||||
index e53cfc60b41fe0d4eeb0362e9923f1e39e61a64d..8e0d72623bbb0c92c8fd8b97ab833c947806667b 100644
|
||||
--- a/chrome/child/pdf_child_init.cc
|
||||
+++ b/chrome/child/pdf_child_init.cc
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "base/win/windows_version.h"
|
||||
#include "content/public/child/child_thread.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
+#include "ppapi/buildflags/buildflags.h"
|
||||
#include "sandbox/policy/mojom/sandbox.mojom.h"
|
||||
#include "sandbox/policy/sandbox_type.h"
|
||||
#include "sandbox/policy/switches.h"
|
||||
@@ -57,7 +58,9 @@ void MaybePatchGdiGetFontData() {
|
||||
auto service_sandbox_type =
|
||||
sandbox::policy::SandboxTypeFromCommandLine(command_line);
|
||||
bool need_gdi =
|
||||
+#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
service_sandbox_type == sandbox::mojom::Sandbox::kPpapi ||
|
||||
+#endif
|
||||
service_sandbox_type == sandbox::mojom::Sandbox::kPrintCompositor ||
|
||||
service_sandbox_type == sandbox::mojom::Sandbox::kPdfConversion ||
|
||||
(service_sandbox_type == sandbox::mojom::Sandbox::kRenderer &&
|
||||
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
|
||||
index 27b5eb50bfcc042b00934021bc654cb9688b88b4..4593ba59f70fc7a65bf4bd14d1ba27953d804f3b 100644
|
||||
--- a/content/test/BUILD.gn
|
||||
+++ b/content/test/BUILD.gn
|
||||
@@ -2134,7 +2134,6 @@ test("content_unittests") {
|
||||
"../browser/payments/payment_manager_unittest.cc",
|
||||
"../browser/permissions/permission_controller_impl_unittest.cc",
|
||||
"../browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc",
|
||||
- "../browser/plugin_list_unittest.cc",
|
||||
"../browser/ppapi_plugin_sandboxed_process_launcher_delegate_unittest.cc",
|
||||
"../browser/prerender/prerender_host_registry_unittest.cc",
|
||||
"../browser/prerender/prerender_host_unittest.cc",
|
||||
@@ -2832,8 +2831,8 @@ test("content_unittests") {
|
||||
deps += [ "//ui/events:test_support" ]
|
||||
}
|
||||
|
||||
- if (!is_win && !is_mac) {
|
||||
- sources -= [ "../browser/plugin_list_unittest.cc" ]
|
||||
+ if (enable_plugins && (is_win || is_mac)) {
|
||||
+ sources += [ "../browser/plugin_list_unittest.cc" ]
|
||||
}
|
||||
|
||||
if (use_ozone) {
|
||||
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
|
||||
index 197e66eeb5b3bd91efd5817a09d2ddbb29c34c25..3ac3ae04ee1d17bcfab1ad0e4fc258e6f12f951a 100644
|
||||
--- a/sandbox/policy/win/sandbox_win.cc
|
||||
+++ b/sandbox/policy/win/sandbox_win.cc
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "base/win/sid.h"
|
||||
#include "base/win/win_util.h"
|
||||
#include "base/win/windows_version.h"
|
||||
+#include "ppapi/buildflags/buildflags.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "sandbox/policy/features.h"
|
||||
#include "sandbox/policy/mojom/sandbox.mojom.h"
|
||||
@@ -1244,8 +1245,10 @@ std::string SandboxWin::GetSandboxTypeInEnglish(Sandbox sandbox_type) {
|
||||
return "Utility";
|
||||
case Sandbox::kGpu:
|
||||
return "GPU";
|
||||
+#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
case Sandbox::kPpapi:
|
||||
return "PPAPI";
|
||||
+#endif
|
||||
case Sandbox::kNetwork:
|
||||
return "Network";
|
||||
case Sandbox::kCdm:
|
||||
203
patches/chromium/m104_background_fetch_passing.patch
Normal file
203
patches/chromium/m104_background_fetch_passing.patch
Normal file
@@ -0,0 +1,203 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Min Qin <qinmin@chromium.org>
|
||||
Date: Tue, 28 Jun 2022 16:27:43 +0000
|
||||
Subject: passing update_first_party_url_on_redirect=false for fetch
|
||||
|
||||
Background fetch doesn't work like regular download as it is not
|
||||
considered a top frame navigation. This CL let background fetch to
|
||||
pass update_first_party_url_on_redirect=false to DownloadURLParameters,
|
||||
and handle it properly w.r.t samesite cookies.
|
||||
|
||||
BUG=1268580
|
||||
|
||||
(cherry picked from commit bf1e93c6af21dad12088b615feda07a90a85c158)
|
||||
|
||||
Change-Id: I3a1cc33be8578d5d8c796dbbb21fa35a47bdda36
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3712307
|
||||
Reviewed-by: Rayan Kanso <rayankans@chromium.org>
|
||||
Commit-Queue: Min Qin <qinmin@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1016316}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3727786
|
||||
Cr-Commit-Position: refs/branch-heads/5112@{#397}
|
||||
Cr-Branched-From: b13d3fe7b3c47a56354ef54b221008afa754412e-refs/heads/main@{#1012729}
|
||||
|
||||
diff --git a/components/background_fetch/background_fetch_delegate_base.cc b/components/background_fetch/background_fetch_delegate_base.cc
|
||||
index 98d8977f1b18dd4d6fee6acbc2244063cb64fa24..1106781258375606ddc61ad9798eb90df9014c88 100644
|
||||
--- a/components/background_fetch/background_fetch_delegate_base.cc
|
||||
+++ b/components/background_fetch/background_fetch_delegate_base.cc
|
||||
@@ -102,6 +102,7 @@ void BackgroundFetchDelegateBase::DownloadUrl(
|
||||
weak_ptr_factory_.GetWeakPtr());
|
||||
params.traffic_annotation =
|
||||
net::MutableNetworkTrafficAnnotationTag(traffic_annotation);
|
||||
+ params.request_params.update_first_party_url_on_redirect = false;
|
||||
|
||||
JobDetails* job_details = GetJobDetails(job_id);
|
||||
if (job_details->job_state == JobDetails::State::kPendingWillStartPaused ||
|
||||
diff --git a/components/download/content/internal/download_driver_impl.cc b/components/download/content/internal/download_driver_impl.cc
|
||||
index d3c15b4852e80aeca68469715935af20f91ace96..ccff2b7ec99d214ddb9161059daf65d7b0000674 100644
|
||||
--- a/components/download/content/internal/download_driver_impl.cc
|
||||
+++ b/components/download/content/internal/download_driver_impl.cc
|
||||
@@ -227,6 +227,8 @@ void DownloadDriverImpl::Start(
|
||||
download_url_params->set_isolation_info(
|
||||
request_params.isolation_info.value());
|
||||
}
|
||||
+ download_url_params->set_update_first_party_url_on_redirect(
|
||||
+ request_params.update_first_party_url_on_redirect);
|
||||
|
||||
download_manager_coordinator_->DownloadUrl(std::move(download_url_params));
|
||||
}
|
||||
diff --git a/components/download/internal/common/download_utils.cc b/components/download/internal/common/download_utils.cc
|
||||
index e5eb9aa02069682cc3bdba6125f604a885faca9b..1b02ed5599398f86f94df438767b6a7b45c36d47 100644
|
||||
--- a/components/download/internal/common/download_utils.cc
|
||||
+++ b/components/download/internal/common/download_utils.cc
|
||||
@@ -349,8 +349,10 @@ std::unique_ptr<network::ResourceRequest> CreateResourceRequest(
|
||||
// cross-site URL has been visited before.
|
||||
url::Origin origin = url::Origin::Create(params->url());
|
||||
request->trusted_params->isolation_info = net::IsolationInfo::Create(
|
||||
- net::IsolationInfo::RequestType::kMainFrame, origin, origin,
|
||||
- net::SiteForCookies::FromOrigin(origin));
|
||||
+ params->update_first_party_url_on_redirect()
|
||||
+ ? net::IsolationInfo::RequestType::kMainFrame
|
||||
+ : net::IsolationInfo::RequestType::kOther,
|
||||
+ origin, origin, net::SiteForCookies::FromOrigin(origin));
|
||||
request->site_for_cookies = net::SiteForCookies::FromUrl(params->url());
|
||||
}
|
||||
|
||||
@@ -358,7 +360,8 @@ std::unique_ptr<network::ResourceRequest> CreateResourceRequest(
|
||||
request->referrer = params->referrer();
|
||||
request->referrer_policy = params->referrer_policy();
|
||||
request->is_main_frame = true;
|
||||
- request->update_first_party_url_on_redirect = true;
|
||||
+ request->update_first_party_url_on_redirect =
|
||||
+ params->update_first_party_url_on_redirect();
|
||||
|
||||
// Downloads should be treated as navigations from Fetch spec perspective.
|
||||
// See also:
|
||||
diff --git a/components/download/public/background_service/download_params.h b/components/download/public/background_service/download_params.h
|
||||
index e567fcc8aea307d73ec64fea47e0ac3733256340..ce6070e67bbfcf8164f64bd79e14fd4b1a6425b5 100644
|
||||
--- a/components/download/public/background_service/download_params.h
|
||||
+++ b/components/download/public/background_service/download_params.h
|
||||
@@ -120,6 +120,12 @@ struct RequestParams {
|
||||
// The isolation info of the request, this won't be persisted to db and will
|
||||
// be invalidate during download resumption in new browser session.
|
||||
absl::optional<net::IsolationInfo> isolation_info;
|
||||
+
|
||||
+ // First-party URL redirect policy: During server redirects, whether the
|
||||
+ // first-party URL for cookies will need to be changed. Download is normally
|
||||
+ // considered a main frame navigation. However, this is not true for
|
||||
+ // background fetch.
|
||||
+ bool update_first_party_url_on_redirect = true;
|
||||
};
|
||||
|
||||
// The parameters that describe a download request made to the DownloadService.
|
||||
diff --git a/components/download/public/common/download_url_parameters.cc b/components/download/public/common/download_url_parameters.cc
|
||||
index 3dec7148d86cd3892670df8b95dcb78d49616e89..25ea6f13e0df9f23364b75c48b870d8ea6a53e92 100644
|
||||
--- a/components/download/public/common/download_url_parameters.cc
|
||||
+++ b/components/download/public/common/download_url_parameters.cc
|
||||
@@ -34,7 +34,8 @@ DownloadUrlParameters::DownloadUrlParameters(
|
||||
traffic_annotation_(traffic_annotation),
|
||||
download_source_(DownloadSource::UNKNOWN),
|
||||
require_safety_checks_(true),
|
||||
- has_user_gesture_(false) {}
|
||||
+ has_user_gesture_(false),
|
||||
+ update_first_party_url_on_redirect_(true) {}
|
||||
|
||||
DownloadUrlParameters::~DownloadUrlParameters() = default;
|
||||
|
||||
diff --git a/components/download/public/common/download_url_parameters.h b/components/download/public/common/download_url_parameters.h
|
||||
index ba0a03cb035a79651967ac2882f89f577bd0c012..61eb9af8a00c3e4adf700178994a9e6e864bcf0c 100644
|
||||
--- a/components/download/public/common/download_url_parameters.h
|
||||
+++ b/components/download/public/common/download_url_parameters.h
|
||||
@@ -279,6 +279,11 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadUrlParameters {
|
||||
has_user_gesture_ = has_user_gesture;
|
||||
}
|
||||
|
||||
+ void set_update_first_party_url_on_redirect(
|
||||
+ bool update_first_party_url_on_redirect) {
|
||||
+ update_first_party_url_on_redirect_ = update_first_party_url_on_redirect;
|
||||
+ }
|
||||
+
|
||||
OnStartedCallback& callback() { return callback_; }
|
||||
bool content_initiated() const { return content_initiated_; }
|
||||
const std::string& last_modified() const { return last_modified_; }
|
||||
@@ -335,6 +340,9 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadUrlParameters {
|
||||
return isolation_info_;
|
||||
}
|
||||
bool has_user_gesture() const { return has_user_gesture_; }
|
||||
+ bool update_first_party_url_on_redirect() const {
|
||||
+ return update_first_party_url_on_redirect_;
|
||||
+ }
|
||||
|
||||
// STATE CHANGING: All save_info_ sub-objects will be in an indeterminate
|
||||
// state following this call.
|
||||
@@ -383,6 +391,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadUrlParameters {
|
||||
bool require_safety_checks_;
|
||||
absl::optional<net::IsolationInfo> isolation_info_;
|
||||
bool has_user_gesture_;
|
||||
+ bool update_first_party_url_on_redirect_;
|
||||
};
|
||||
|
||||
} // namespace download
|
||||
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
|
||||
index d6adfeb5aff596089ae3823248ff50c84ba94ad2..73cf82580555d620cfb7a3ba3aad3379d38e4608 100644
|
||||
--- a/content/browser/download/download_browsertest.cc
|
||||
+++ b/content/browser/download/download_browsertest.cc
|
||||
@@ -3751,6 +3751,58 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, UpdateSiteForCookies) {
|
||||
site_a.GetURL("a.test", "/")));
|
||||
}
|
||||
|
||||
+// Tests that if `update_first_party_url_on_redirect` is set to false, download
|
||||
+// will not behave like a top-level frame navigation and SameSite=Strict cookies
|
||||
+// will not be set on a redirection.
|
||||
+IN_PROC_BROWSER_TEST_F(
|
||||
+ DownloadContentTest,
|
||||
+ SiteForCookies_DownloadUrl_NotUpdateFirstPartyUrlOnRedirect) {
|
||||
+ net::EmbeddedTestServer site_a;
|
||||
+ net::EmbeddedTestServer site_b;
|
||||
+
|
||||
+ base::StringPairs cookie_headers;
|
||||
+ cookie_headers.push_back(std::make_pair(
|
||||
+ std::string("Set-Cookie"), std::string("A=strict; SameSite=Strict")));
|
||||
+ cookie_headers.push_back(std::make_pair(std::string("Set-Cookie"),
|
||||
+ std::string("B=lax; SameSite=Lax")));
|
||||
+
|
||||
+ // This will request a URL on b.test, which redirects to a url that sets the
|
||||
+ // cookies on a.test.
|
||||
+ site_a.RegisterRequestHandler(CreateBasicResponseHandler(
|
||||
+ "/sets-samesite-cookies", net::HTTP_OK, cookie_headers,
|
||||
+ "application/octet-stream", "abcd"));
|
||||
+ ASSERT_TRUE(site_a.Start());
|
||||
+ site_b.RegisterRequestHandler(
|
||||
+ CreateRedirectHandler("/redirected-download",
|
||||
+ site_a.GetURL("a.test", "/sets-samesite-cookies")));
|
||||
+ ASSERT_TRUE(site_b.Start());
|
||||
+
|
||||
+ // Download the file.
|
||||
+ SetupEnsureNoPendingDownloads();
|
||||
+ std::unique_ptr<download::DownloadUrlParameters> download_parameters(
|
||||
+ DownloadRequestUtils::CreateDownloadForWebContentsMainFrame(
|
||||
+ shell()->web_contents(),
|
||||
+ site_b.GetURL("b.test", "/redirected-download"),
|
||||
+ TRAFFIC_ANNOTATION_FOR_TESTS));
|
||||
+ download_parameters->set_update_first_party_url_on_redirect(false);
|
||||
+ std::unique_ptr<DownloadTestObserver> observer(CreateWaiter(shell(), 1));
|
||||
+ DownloadManagerForShell(shell())->DownloadUrl(std::move(download_parameters));
|
||||
+ observer->WaitForFinished();
|
||||
+
|
||||
+ // Get the important info from other threads and check it.
|
||||
+ EXPECT_TRUE(EnsureNoPendingDownloads());
|
||||
+
|
||||
+ std::vector<download::DownloadItem*> downloads;
|
||||
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
|
||||
+ ASSERT_EQ(1u, downloads.size());
|
||||
+ ASSERT_EQ(download::DownloadItem::COMPLETE, downloads[0]->GetState());
|
||||
+
|
||||
+ // Check that the cookies were not set on a.test.
|
||||
+ EXPECT_EQ("",
|
||||
+ content::GetCookies(shell()->web_contents()->GetBrowserContext(),
|
||||
+ site_a.GetURL("a.test", "/")));
|
||||
+}
|
||||
+
|
||||
// Verifies that isolation info set in DownloadUrlParameters can be populated.
|
||||
IN_PROC_BROWSER_TEST_F(DownloadContentTest,
|
||||
SiteForCookies_DownloadUrl_IsolationInfoPopulated) {
|
||||
@@ -1,16 +1,28 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Thu, 7 Apr 2022 20:30:16 +0900
|
||||
Subject: Make gtk::GetLibGtk public
|
||||
Subject: Make gtk::GetLibGtk and gtk::GetLibGdkPixbuf public
|
||||
|
||||
Allows embedders to get a handle to the gtk library
|
||||
already loaded in the process.
|
||||
Allows embedders to get a handle to the gtk and
|
||||
gdk_pixbuf libraries already loaded in the process.
|
||||
|
||||
diff --git a/ui/gtk/gtk_compat.cc b/ui/gtk/gtk_compat.cc
|
||||
index 7104a0d5f4489c687f3cb9e63bc7cbef59d2fa62..f0b9ed834a3f7310da09377f0b2105cf635ffeb7 100644
|
||||
index 7104a0d5f4489c687f3cb9e63bc7cbef59d2fa62..7449c0b7bdb0cac7340cb73292e70e6aa0336657 100644
|
||||
--- a/ui/gtk/gtk_compat.cc
|
||||
+++ b/ui/gtk/gtk_compat.cc
|
||||
@@ -86,12 +86,6 @@ void* GetLibGtk4(bool check = true) {
|
||||
@@ -66,11 +66,6 @@ void* GetLibGio() {
|
||||
return libgio;
|
||||
}
|
||||
|
||||
-void* GetLibGdkPixbuf() {
|
||||
- static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
|
||||
- return libgdk_pixbuf;
|
||||
-}
|
||||
-
|
||||
void* GetLibGdk3() {
|
||||
static void* libgdk3 = DlOpen("libgdk-3.so.0");
|
||||
return libgdk3;
|
||||
@@ -86,12 +81,6 @@ void* GetLibGtk4(bool check = true) {
|
||||
return libgtk4;
|
||||
}
|
||||
|
||||
@@ -23,10 +35,15 @@ index 7104a0d5f4489c687f3cb9e63bc7cbef59d2fa62..f0b9ed834a3f7310da09377f0b2105cf
|
||||
bool LoadGtk3() {
|
||||
if (!GetLibGtk3(false))
|
||||
return false;
|
||||
@@ -133,6 +127,12 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
@@ -133,6 +122,17 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
|
||||
} // namespace
|
||||
|
||||
+void* GetLibGdkPixbuf() {
|
||||
+ static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
|
||||
+ return libgdk_pixbuf;
|
||||
+}
|
||||
+
|
||||
+void* GetLibGtk() {
|
||||
+ if (GtkCheckVersion(4))
|
||||
+ return GetLibGtk4();
|
||||
@@ -37,13 +54,16 @@ index 7104a0d5f4489c687f3cb9e63bc7cbef59d2fa62..f0b9ed834a3f7310da09377f0b2105cf
|
||||
static bool loaded = LoadGtkImpl();
|
||||
return loaded;
|
||||
diff --git a/ui/gtk/gtk_compat.h b/ui/gtk/gtk_compat.h
|
||||
index 72981270fe26579211afcaf3c596a412f69f5fac..b5dbfde5b011d57d26960d245e0dc61cac9341e4 100644
|
||||
index 72981270fe26579211afcaf3c596a412f69f5fac..2ead29619e9f69f50699970546bf048686ca643f 100644
|
||||
--- a/ui/gtk/gtk_compat.h
|
||||
+++ b/ui/gtk/gtk_compat.h
|
||||
@@ -37,6 +37,9 @@ using SkColor = uint32_t;
|
||||
@@ -37,6 +37,12 @@ using SkColor = uint32_t;
|
||||
|
||||
namespace gtk {
|
||||
|
||||
+// Get handle to the currently loaded gdk_pixbuf library in the process.
|
||||
+void* GetLibGdkPixbuf();
|
||||
+
|
||||
+// Get handle to the currently loaded gtk library in the process.
|
||||
+void* GetLibGtk();
|
||||
+
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aaron Leventhal <aleventhal@google.com>
|
||||
Date: Tue, 26 Jul 2022 17:36:04 +0000
|
||||
Subject: Merge 104: Speculative fix for IsValidCodePointInIndex() range crash
|
||||
|
||||
(cherry picked from commit b4d1ce62d7783a9b31a0a2bec1b886a9a6519a40)
|
||||
|
||||
Bug: 1333970
|
||||
Change-Id: I5a4c78e708357074fdec1f7a18fa928e39f9c51a
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3757922
|
||||
Auto-Submit: Aaron Leventhal <aleventhal@chromium.org>
|
||||
Reviewed-by: Nektarios Paisios <nektar@chromium.org>
|
||||
Commit-Queue: Aaron Leventhal <aleventhal@chromium.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#1025405}
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3788215
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Cr-Commit-Position: refs/branch-heads/5112@{#1206}
|
||||
Cr-Branched-From: b13d3fe7b3c47a56354ef54b221008afa754412e-refs/heads/main@{#1012729}
|
||||
|
||||
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
|
||||
index 29ed811d67ad6eaf9fc6b7c22521bebc74038258..4d39bc31167d266aa5866b44b420fe743fddca8d 100644
|
||||
--- a/content/browser/accessibility/browser_accessibility_manager.cc
|
||||
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
|
||||
@@ -394,6 +394,10 @@ const ui::AXTreeData& BrowserAccessibilityManager::GetTreeData() const {
|
||||
return ax_tree()->data();
|
||||
}
|
||||
|
||||
+std::string BrowserAccessibilityManager::ToString() const {
|
||||
+ return GetTreeData().ToString();
|
||||
+}
|
||||
+
|
||||
void BrowserAccessibilityManager::OnWindowFocused() {
|
||||
if (IsRootTree())
|
||||
FireFocusEventsIfNeeded();
|
||||
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
|
||||
index 92d62b5832d4fc87830b1c2bad7c7b972f1de74e..ca696fd855dcfe45e80e6c070546b19b19d42f69 100644
|
||||
--- a/content/browser/accessibility/browser_accessibility_manager.h
|
||||
+++ b/content/browser/accessibility/browser_accessibility_manager.h
|
||||
@@ -225,6 +225,8 @@ class CONTENT_EXPORT BrowserAccessibilityManager
|
||||
// Get the AXTreeData for this frame.
|
||||
const ui::AXTreeData& GetTreeData() const;
|
||||
|
||||
+ std::string ToString() const override;
|
||||
+
|
||||
// Called to notify the accessibility manager that its associated native
|
||||
// view got focused.
|
||||
virtual void OnWindowFocused();
|
||||
diff --git a/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc b/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc
|
||||
index 30000a5a65b1d344fda047f6fd592cdcb6ee0e72..e8b1e2f08db121069bf7042fd8ad704c5d4b7a0d 100644
|
||||
--- a/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc
|
||||
+++ b/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc
|
||||
@@ -2,6 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
+#include "automation_ax_tree_wrapper.h"
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/containers/cxx20_erase.h"
|
||||
#include "base/no_destructor.h"
|
||||
@@ -545,4 +546,8 @@ ui::AXNode* AutomationAXTreeWrapper::GetParentNodeFromParentTreeAsAXNode()
|
||||
return owner_->GetParent(tree_.root(), &wrapper);
|
||||
}
|
||||
|
||||
+std::string AutomationAXTreeWrapper::ToString() const {
|
||||
+ return "<AutomationAXTreeWrapper>";
|
||||
+}
|
||||
+
|
||||
} // namespace extensions
|
||||
diff --git a/extensions/renderer/api/automation/automation_ax_tree_wrapper.h b/extensions/renderer/api/automation/automation_ax_tree_wrapper.h
|
||||
index bd3fae05ab1f70fb26888d0ec9a8ab29a17f3f3f..d370dcee0eb277925ccfb48049d5c0197acc225b 100644
|
||||
--- a/extensions/renderer/api/automation/automation_ax_tree_wrapper.h
|
||||
+++ b/extensions/renderer/api/automation/automation_ax_tree_wrapper.h
|
||||
@@ -116,6 +116,7 @@ class AutomationAXTreeWrapper : public ui::AXTreeObserver,
|
||||
ui::AXTreeID GetParentTreeID() const override;
|
||||
ui::AXNode* GetRootAsAXNode() const override;
|
||||
ui::AXNode* GetParentNodeFromParentTreeAsAXNode() const override;
|
||||
+ std::string ToString() const override;
|
||||
|
||||
private:
|
||||
// AXTreeObserver overrides.
|
||||
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
|
||||
index b0f06a0912447e5fa9bad9e62b2523817490ea02..3081258496dacc4f26976dfd99ea005a70bcd210 100644
|
||||
--- a/ui/accessibility/ax_node_position_unittest.cc
|
||||
+++ b/ui/accessibility/ax_node_position_unittest.cc
|
||||
@@ -9105,8 +9105,12 @@ TEST_F(AXPositionTest,
|
||||
EXPECT_TRUE(test_position->IsNullPosition());
|
||||
}
|
||||
|
||||
+// TODO(crbug.com/1333970) It is not legal to call
|
||||
+// AsLeafTextPositionBeforeCharacter or AsLeafTextPositionAfterCharacter with
|
||||
+// a text position using out-of-range offsets. It's necessary to call
|
||||
+// AsValidPosition() first. Therefore, this test currently triggers a DCHECK.
|
||||
TEST_F(AXPositionTest,
|
||||
- AsLeafTextPositionBeforeAndAfterCharacterWithInvalidPosition) {
|
||||
+ DISABLED_AsLeafTextPositionBeforeAndAfterCharacterWithInvalidPosition) {
|
||||
AXNodeData root_data;
|
||||
root_data.id = 1;
|
||||
root_data.role = ax::mojom::Role::kRootWebArea;
|
||||
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
|
||||
index 07fd92a174f08acad1db1853ed4bae57c0505990..280774a1844fd78407a4025b92adb6f7fae9fd21 100644
|
||||
--- a/ui/accessibility/ax_position.h
|
||||
+++ b/ui/accessibility/ax_position.h
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
+#include "ui/accessibility/ax_common.h"
|
||||
#include "ui/accessibility/ax_enum_util.h"
|
||||
#include "ui/accessibility/ax_enums.mojom.h"
|
||||
#include "ui/accessibility/ax_node.h"
|
||||
@@ -382,16 +383,33 @@ class AXPosition {
|
||||
return str + " annotated_text=" + base::UTF16ToUTF8(annotated_text);
|
||||
}
|
||||
|
||||
+ // Helper for logging the position, the AXTreeManager and the anchor node.
|
||||
+ std::string ToDebugString() const {
|
||||
+ if (IsNullPosition()) {
|
||||
+ return "* Position: null";
|
||||
+ }
|
||||
+ DCHECK(GetAnchor());
|
||||
+ DCHECK(GetManager());
|
||||
+ std::ostringstream str;
|
||||
+ str << "* Position: " << ToString()
|
||||
+ << "\n* Manager: " << GetManager()->ToString()
|
||||
+ << "\n* Anchor node: " << *GetAnchor();
|
||||
+ return str.str();
|
||||
+ }
|
||||
+
|
||||
AXPositionKind kind() const { return kind_; }
|
||||
AXTreeID tree_id() const { return tree_id_; }
|
||||
AXNodeID anchor_id() const { return anchor_id_; }
|
||||
|
||||
+ AXTreeManager* GetManager() const {
|
||||
+ return AXTreeManagerMap::GetInstance().GetManager(tree_id());
|
||||
+ }
|
||||
+
|
||||
AXNode* GetAnchor() const {
|
||||
if (tree_id_ == AXTreeIDUnknown() || anchor_id_ == kInvalidAXNodeID)
|
||||
return nullptr;
|
||||
|
||||
- const AXTreeManager* manager =
|
||||
- AXTreeManagerMap::GetInstance().GetManager(tree_id());
|
||||
+ const AXTreeManager* manager = GetManager();
|
||||
if (manager)
|
||||
return manager->GetNodeFromTree(anchor_id());
|
||||
|
||||
@@ -2568,13 +2586,6 @@ class AXPosition {
|
||||
return Clone();
|
||||
|
||||
AXPositionInstance text_position = AsTextPosition();
|
||||
- // The following situation should not be possible but there are existing
|
||||
- // crashes in the field.
|
||||
- //
|
||||
- // TODO(nektar): Remove this workaround as soon as the source of the bug
|
||||
- // is identified.
|
||||
- if (text_position->text_offset_ > text_position->MaxTextOffset())
|
||||
- return CreateNullPosition();
|
||||
|
||||
// In case the input affinity is upstream, reset it to downstream.
|
||||
//
|
||||
@@ -2592,6 +2603,16 @@ class AXPosition {
|
||||
if (!text_position->IsIgnored() && !text_position->AtEndOfAnchor()) {
|
||||
std::unique_ptr<base::i18n::BreakIterator> grapheme_iterator =
|
||||
text_position->GetGraphemeIterator();
|
||||
+ // The following situation should not be possible but there are existing
|
||||
+ // crashes in the field.
|
||||
+ //
|
||||
+ // TODO(nektar): Remove this workaround as soon as the source of the bug
|
||||
+ // is identified.
|
||||
+ if (text_position->text_offset_ < 0 ||
|
||||
+ text_position->text_offset_ > text_position->MaxTextOffset()) {
|
||||
+ SANITIZER_NOTREACHED() << "Offset range error:\n" << ToDebugString();
|
||||
+ return CreateNullPosition();
|
||||
+ }
|
||||
DCHECK_GE(text_position->text_offset_, 0);
|
||||
DCHECK_LE(text_position->text_offset_, text_position->MaxTextOffset());
|
||||
while (!text_position->AtStartOfAnchor() &&
|
||||
@@ -2644,9 +2665,11 @@ class AXPosition {
|
||||
//
|
||||
// TODO(nektar): Remove this workaround as soon as the source of the bug
|
||||
// is identified.
|
||||
- if (text_position->text_offset_ > text_position->MaxTextOffset())
|
||||
+ if (text_position->text_offset_ < 0 ||
|
||||
+ text_position->text_offset_ > text_position->MaxTextOffset()) {
|
||||
+ SANITIZER_NOTREACHED() << "Offset range error:\n" << ToDebugString();
|
||||
return CreateNullPosition();
|
||||
-
|
||||
+ }
|
||||
DCHECK_GE(text_position->text_offset_, 0);
|
||||
DCHECK_LE(text_position->text_offset_, text_position->MaxTextOffset());
|
||||
while (!text_position->AtEndOfAnchor() &&
|
||||
diff --git a/ui/accessibility/ax_tree_manager.h b/ui/accessibility/ax_tree_manager.h
|
||||
index 1081ed8fc3388ae7b6f535f2cd7437ac2e3db184..52f892c1acf3a50a24716bb361dd1ca231bd57a1 100644
|
||||
--- a/ui/accessibility/ax_tree_manager.h
|
||||
+++ b/ui/accessibility/ax_tree_manager.h
|
||||
@@ -51,6 +51,11 @@ class AX_EXPORT AXTreeManager {
|
||||
// Called when the tree manager is about to be removed from the tree map,
|
||||
// `AXTreeManagerMap`.
|
||||
virtual void WillBeRemovedFromMap() {}
|
||||
+
|
||||
+ // For debugging.
|
||||
+ // TODO(benjamin.beaudry) Instead of this, implement GetTreeData() on all
|
||||
+ // AXTreeManager subclasses, and have callers use GetTreeData().ToString();
|
||||
+ virtual std::string ToString() const = 0;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
diff --git a/ui/accessibility/test_ax_tree_manager.cc b/ui/accessibility/test_ax_tree_manager.cc
|
||||
index 15627a472481b46c66c3d40efa3895ab14cb89ef..5ee1750a265db3e9291c5f5a9beb1a4228278af2 100644
|
||||
--- a/ui/accessibility/test_ax_tree_manager.cc
|
||||
+++ b/ui/accessibility/test_ax_tree_manager.cc
|
||||
@@ -116,4 +116,8 @@ AXNode* TestAXTreeManager::GetParentNodeFromParentTreeAsAXNode() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+std::string TestAXTreeManager::ToString() const {
|
||||
+ return "<TestAXTreeManager>";
|
||||
+}
|
||||
+
|
||||
} // namespace ui
|
||||
diff --git a/ui/accessibility/test_ax_tree_manager.h b/ui/accessibility/test_ax_tree_manager.h
|
||||
index c155aeb4f164129a3b484386f0e4802a361df80a..dabc5650132d01f121f8a9763afc499800bfbd9c 100644
|
||||
--- a/ui/accessibility/test_ax_tree_manager.h
|
||||
+++ b/ui/accessibility/test_ax_tree_manager.h
|
||||
@@ -53,6 +53,7 @@ class AX_EXPORT TestAXTreeManager : public AXTreeManager {
|
||||
AXTreeID GetParentTreeID() const override;
|
||||
AXNode* GetRootAsAXNode() const override;
|
||||
AXNode* GetParentNodeFromParentTreeAsAXNode() const override;
|
||||
+ std::string ToString() const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<AXTree> tree_;
|
||||
diff --git a/ui/views/accessibility/views_ax_tree_manager.cc b/ui/views/accessibility/views_ax_tree_manager.cc
|
||||
index 53810d5b04bd1b0e61224a56229a8b83bbd60fba..fb1befba94c9cde34518bbddb7b5f309d892f369 100644
|
||||
--- a/ui/views/accessibility/views_ax_tree_manager.cc
|
||||
+++ b/ui/views/accessibility/views_ax_tree_manager.cc
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#include "ui/views/accessibility/views_ax_tree_manager.h"
|
||||
|
||||
-#include <string>
|
||||
-
|
||||
#include "base/bind.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/check.h"
|
||||
@@ -99,6 +97,10 @@ ui::AXNode* ViewsAXTreeManager::GetParentNodeFromParentTreeAsAXNode() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+std::string ViewsAXTreeManager::ToString() const {
|
||||
+ return "<ViewsAXTreeManager>";
|
||||
+}
|
||||
+
|
||||
void ViewsAXTreeManager::OnViewEvent(View* view, ax::mojom::Event event) {
|
||||
DCHECK(view);
|
||||
AXAuraObjWrapper* wrapper = cache_.GetOrCreate(view);
|
||||
diff --git a/ui/views/accessibility/views_ax_tree_manager.h b/ui/views/accessibility/views_ax_tree_manager.h
|
||||
index c46b23a8819cba1a4fd1950d6db66a55e1a08eec..7678983b31a9e852a05d4feed48f369ab7730c27 100644
|
||||
--- a/ui/views/accessibility/views_ax_tree_manager.h
|
||||
+++ b/ui/views/accessibility/views_ax_tree_manager.h
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
+#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback_forward.h"
|
||||
@@ -89,6 +90,7 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager,
|
||||
ui::AXTreeID GetParentTreeID() const override;
|
||||
ui::AXNode* GetRootAsAXNode() const override;
|
||||
ui::AXNode* GetParentNodeFromParentTreeAsAXNode() const override;
|
||||
+ std::string ToString() const override;
|
||||
|
||||
// AXActionHandlerBase implementation.
|
||||
void PerformAction(const ui::AXActionData& data) override;
|
||||
250
patches/chromium/pa_support_16kb_pagesize_on_linux_arm64.patch
Normal file
250
patches/chromium/pa_support_16kb_pagesize_on_linux_arm64.patch
Normal file
@@ -0,0 +1,250 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jorrit Jongma <jorrit@jongma.org>
|
||||
Date: Tue, 12 Apr 2022 17:09:34 +0000
|
||||
Subject: Support 16kb pagesize on Linux+ARM64
|
||||
|
||||
This makes the system pagesize a run-time property.
|
||||
|
||||
ARM64 supports 4kb, 16kb, and 64kb page sizes. Previously, only 4kb
|
||||
was supported by Chromium. This patch adds 16kb support, as is used
|
||||
for example by Asahi Linux on M1 Macs. The rare 64kb case is still
|
||||
not supported due to further changes needed to SlotSpanMetadata.
|
||||
|
||||
The implementation follows the changes made to support run-time page
|
||||
size on macOS. On macOS, the required constants are conveniently
|
||||
injected before any code runs, while on Linux a function call is
|
||||
needed, complicating initialization.
|
||||
|
||||
The new PageCharacteristics structure holds the page size and shift
|
||||
as std::atomic<int> which are initialized on first use.
|
||||
|
||||
Bug: 1301788
|
||||
Change-Id: I8ceead40de53ba7a2ec248bd6ef46f2a521dd29c
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3545665
|
||||
Reviewed-by: Benoit Lize <lizeb@chromium.org>
|
||||
Reviewed-by: Mark Mentovai <mark@chromium.org>
|
||||
Commit-Queue: Mark Mentovai <mark@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#991588}
|
||||
|
||||
diff --git a/AUTHORS b/AUTHORS
|
||||
index 364df55a2888c221523023facd8873fa010d40d6..f2b8d8c0d2a4474ded579212327f00489da00623 100644
|
||||
--- a/AUTHORS
|
||||
+++ b/AUTHORS
|
||||
@@ -572,6 +572,7 @@ Jongsoo Lee <leejongsoo@gmail.com>
|
||||
Joone Hur <joone.hur@intel.com>
|
||||
Joonghun Park <pjh0718@gmail.com>
|
||||
Jorge Villatoro <jorge@tomatocannon.com>
|
||||
+Jorrit Jongma <jorrit@jongma.org>
|
||||
Joseph Gentle <josephg@gmail.com>
|
||||
Joseph Lolak <joseph.lolak@samsung.com>
|
||||
Josh Triplett <josh.triplett@intel.com>
|
||||
diff --git a/base/allocator/partition_allocator/address_space_randomization.h b/base/allocator/partition_allocator/address_space_randomization.h
|
||||
index 43033d728050a8d8b03f5e4a69fa1593190d30f1..e77757c3ad512f03cd21127ebd780e7a19f12672 100644
|
||||
--- a/base/allocator/partition_allocator/address_space_randomization.h
|
||||
+++ b/base/allocator/partition_allocator/address_space_randomization.h
|
||||
@@ -121,6 +121,21 @@ AslrMask(uintptr_t bits) {
|
||||
return AslrAddress(0x20000000ULL);
|
||||
}
|
||||
|
||||
+ #elif BUILDFLAG(IS_LINUX)
|
||||
+
|
||||
+ // Linux on arm64 can use 39, 42, 48, or 52-bit user space, depending on
|
||||
+ // page size and number of levels of translation pages used. We use
|
||||
+ // 39-bit as base as all setups should support this, lowered to 38-bit
|
||||
+ // as ASLROffset() could cause a carry.
|
||||
+ PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
|
||||
+ ASLRMask() {
|
||||
+ return AslrMask(38);
|
||||
+ }
|
||||
+ PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
|
||||
+ ASLROffset() {
|
||||
+ return AslrAddress(0x1000000000ULL);
|
||||
+ }
|
||||
+
|
||||
#else
|
||||
|
||||
// ARM64 on Linux has 39-bit user space. Use 38 bits since ASLROffset()
|
||||
diff --git a/base/allocator/partition_allocator/page_allocator_constants.h b/base/allocator/partition_allocator/page_allocator_constants.h
|
||||
index 12515b9a02865eb8a4b2a7c15b0fcce06a8ec7f9..0a996cfdcc4a42005ffad8922f3cc55547c47d20 100644
|
||||
--- a/base/allocator/partition_allocator/page_allocator_constants.h
|
||||
+++ b/base/allocator/partition_allocator/page_allocator_constants.h
|
||||
@@ -24,6 +24,31 @@
|
||||
// elimination.
|
||||
#define PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR __attribute__((const))
|
||||
|
||||
+#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
|
||||
+// This should work for all POSIX (if needed), but currently all other
|
||||
+// supported OS/architecture combinations use either hard-coded values
|
||||
+// (such as x86) or have means to determine these values without needing
|
||||
+// atomics (such as macOS on arm64).
|
||||
+
|
||||
+// Page allocator constants are run-time constant
|
||||
+#define PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR __attribute__((const))
|
||||
+
|
||||
+#include <unistd.h>
|
||||
+#include <atomic>
|
||||
+
|
||||
+namespace partition_alloc::internal {
|
||||
+
|
||||
+// Holds the current page size and shift, where size = 1 << shift
|
||||
+// Use PageAllocationGranularity(), PageAllocationGranularityShift()
|
||||
+// to initialize and retrieve these values safely.
|
||||
+struct PageCharacteristics {
|
||||
+ std::atomic<int> size;
|
||||
+ std::atomic<int> shift;
|
||||
+};
|
||||
+extern PageCharacteristics page_characteristics;
|
||||
+
|
||||
+} // namespace partition_alloc::internal
|
||||
+
|
||||
#else
|
||||
|
||||
// When defined, page size constants are fixed at compile time. When not
|
||||
@@ -38,6 +63,10 @@
|
||||
|
||||
namespace partition_alloc::internal {
|
||||
|
||||
+// Forward declaration, implementation below
|
||||
+PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
|
||||
+PageAllocationGranularity();
|
||||
+
|
||||
PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
|
||||
PageAllocationGranularityShift() {
|
||||
#if BUILDFLAG(IS_WIN) || defined(ARCH_CPU_PPC64)
|
||||
@@ -50,6 +79,15 @@ PageAllocationGranularityShift() {
|
||||
return 14; // 16kB
|
||||
#elif BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)
|
||||
return vm_page_shift;
|
||||
+#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
|
||||
+ // arm64 supports 4kb (shift = 12), 16kb (shift = 14), and 64kb (shift = 16)
|
||||
+ // page sizes. Retrieve from or initialize cache.
|
||||
+ int shift = page_characteristics.shift.load(std::memory_order_relaxed);
|
||||
+ if (UNLIKELY(shift == 0)) {
|
||||
+ shift = __builtin_ctz((int)PageAllocationGranularity());
|
||||
+ page_characteristics.shift.store(shift, std::memory_order_relaxed);
|
||||
+ }
|
||||
+ return shift;
|
||||
#else
|
||||
return 12; // 4kB
|
||||
#endif
|
||||
@@ -59,8 +97,17 @@ PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
|
||||
PageAllocationGranularity() {
|
||||
#if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)
|
||||
// This is literally equivalent to |1 << PageAllocationGranularityShift()|
|
||||
- // below, but was separated out for OS_APPLE to avoid << on a non-constexpr.
|
||||
+ // below, but was separated out for IS_APPLE to avoid << on a non-constexpr.
|
||||
return vm_page_size;
|
||||
+#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
|
||||
+ // arm64 supports 4kb, 16kb, and 64kb page sizes. Retrieve from or
|
||||
+ // initialize cache.
|
||||
+ int size = page_characteristics.size.load(std::memory_order_relaxed);
|
||||
+ if (UNLIKELY(size == 0)) {
|
||||
+ size = getpagesize();
|
||||
+ page_characteristics.size.store(size, std::memory_order_relaxed);
|
||||
+ }
|
||||
+ return size;
|
||||
#else
|
||||
return 1 << PageAllocationGranularityShift();
|
||||
#endif
|
||||
@@ -90,9 +137,11 @@ SystemPageShift() {
|
||||
|
||||
PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
|
||||
SystemPageSize() {
|
||||
-#if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)
|
||||
+#if (BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)) || \
|
||||
+ (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64))
|
||||
// This is literally equivalent to |1 << SystemPageShift()| below, but was
|
||||
- // separated out for 64-bit OS_APPLE to avoid << on a non-constexpr.
|
||||
+ // separated out for 64-bit IS_APPLE and arm64 on Linux to avoid << on a
|
||||
+ // non-constexpr.
|
||||
return PageAllocationGranularity();
|
||||
#else
|
||||
return 1 << SystemPageShift();
|
||||
diff --git a/base/allocator/partition_allocator/partition_address_space.cc b/base/allocator/partition_allocator/partition_address_space.cc
|
||||
index 71075090cc64bfac2d443a3ee4e210d7aca8a3e5..1e9dc5d312e154ef7a1c66aaef4de3c45a69c912 100644
|
||||
--- a/base/allocator/partition_allocator/partition_address_space.cc
|
||||
+++ b/base/allocator/partition_allocator/partition_address_space.cc
|
||||
@@ -167,6 +167,12 @@ void PartitionAddressSpace::UninitConfigurablePoolForTesting() {
|
||||
setup_.configurable_pool_ = 0;
|
||||
}
|
||||
|
||||
+#if BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
|
||||
+
|
||||
+PageCharacteristics page_characteristics;
|
||||
+
|
||||
+#endif // BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
|
||||
+
|
||||
#endif // defined(PA_HAS_64_BITS_POINTERS)
|
||||
|
||||
} // namespace partition_alloc::internal
|
||||
diff --git a/base/allocator/partition_allocator/partition_alloc_constants.h b/base/allocator/partition_allocator/partition_alloc_constants.h
|
||||
index e54a9a4a3bfafc1a174ed3f3b3cfa98e9d0794f3..1a1c110dc7b49f59e1b9cb7fa0297b90f70880ec 100644
|
||||
--- a/base/allocator/partition_allocator/partition_alloc_constants.h
|
||||
+++ b/base/allocator/partition_allocator/partition_alloc_constants.h
|
||||
@@ -59,10 +59,11 @@ PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
|
||||
PartitionPageShift() {
|
||||
return 18; // 256 KiB
|
||||
}
|
||||
-#elif BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)
|
||||
+#elif (BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)) || \
|
||||
+ (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64))
|
||||
PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
|
||||
PartitionPageShift() {
|
||||
- return vm_page_shift + 2;
|
||||
+ return PageAllocationGranularityShift() + 2;
|
||||
}
|
||||
#else
|
||||
PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
|
||||
diff --git a/base/allocator/partition_allocator/partition_page.h b/base/allocator/partition_allocator/partition_page.h
|
||||
index 5ee5e2c3843a6fa7d8717a619b5d8ccec2edd8f3..efaf910f2b4e9db812402db6566bb7b6aeb354f2 100644
|
||||
--- a/base/allocator/partition_allocator/partition_page.h
|
||||
+++ b/base/allocator/partition_allocator/partition_page.h
|
||||
@@ -134,6 +134,12 @@ struct __attribute__((packed)) SlotSpanMetadata {
|
||||
// PartitionPageSize() is 4 times the OS page size.
|
||||
static constexpr size_t kMaxSlotsPerSlotSpan =
|
||||
4 * (1 << 14) / kSmallestBucket;
|
||||
+#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
|
||||
+ // System page size can be 4, 16, or 64 kiB on Linux on arm64. 64 kiB is
|
||||
+ // currently (kMaxSlotsPerSlotSpanBits == 13) not supported by the code,
|
||||
+ // so we use the 16 kiB maximum (64 kiB will crash).
|
||||
+ static constexpr size_t kMaxSlotsPerSlotSpan =
|
||||
+ 4 * (1 << 14) / kSmallestBucket;
|
||||
#else
|
||||
// A slot span can "span" multiple PartitionPages, but then its slot size is
|
||||
// larger, so it doesn't have as many slots.
|
||||
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc
|
||||
index 98e3940c44cb7460b91305aab0fcc24ef739b979..31f6519227c0c6c5d6235cff8567e46ebe010d24 100644
|
||||
--- a/base/allocator/partition_allocator/partition_root.cc
|
||||
+++ b/base/allocator/partition_allocator/partition_root.cc
|
||||
@@ -309,11 +309,12 @@ static size_t PartitionPurgeSlotSpan(
|
||||
constexpr size_t kMaxSlotCount =
|
||||
(PartitionPageSize() * kMaxPartitionPagesPerRegularSlotSpan) /
|
||||
SystemPageSize();
|
||||
-#elif BUILDFLAG(IS_APPLE)
|
||||
+#elif BUILDFLAG(IS_APPLE) || (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64))
|
||||
// It's better for slot_usage to be stack-allocated and fixed-size, which
|
||||
- // demands that its size be constexpr. On OS_APPLE, PartitionPageSize() is
|
||||
- // always SystemPageSize() << 2, so regardless of what the run time page size
|
||||
- // is, kMaxSlotCount can always be simplified to this expression.
|
||||
+ // demands that its size be constexpr. On OS_APPLE and Linux on arm64,
|
||||
+ // PartitionPageSize() is always SystemPageSize() << 2, so regardless of
|
||||
+ // what the run time page size is, kMaxSlotCount can always be simplified
|
||||
+ // to this expression.
|
||||
constexpr size_t kMaxSlotCount = 4 * kMaxPartitionPagesPerRegularSlotSpan;
|
||||
PA_CHECK(kMaxSlotCount ==
|
||||
(PartitionPageSize() * kMaxPartitionPagesPerRegularSlotSpan) /
|
||||
@@ -633,6 +634,14 @@ void PartitionRoot<thread_safe>::Init(PartitionOptions opts) {
|
||||
// apple OSes.
|
||||
PA_CHECK((SystemPageSize() == (size_t{1} << 12)) ||
|
||||
(SystemPageSize() == (size_t{1} << 14)));
|
||||
+#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
|
||||
+ // Check runtime pagesize. Though the code is currently the same, it is
|
||||
+ // not merged with the IS_APPLE case above as a 1 << 16 case needs to be
|
||||
+ // added here in the future, to allow 64 kiB pagesize. That is only
|
||||
+ // supported on Linux on arm64, not on IS_APPLE, but not yet present here
|
||||
+ // as the rest of the partition allocator does not currently support it.
|
||||
+ PA_CHECK((SystemPageSize() == (size_t{1} << 12)) ||
|
||||
+ (SystemPageSize() == (size_t{1} << 14)));
|
||||
#endif
|
||||
|
||||
::partition_alloc::internal::ScopedGuard guard{lock_};
|
||||
@@ -0,0 +1,641 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Darshan Sen <raisinten@gmail.com>
|
||||
Date: Fri, 17 Jun 2022 13:19:32 +0530
|
||||
Subject: posix: Replace DoubleForkAndExec() with ForkAndSpawn()
|
||||
|
||||
The DoubleForkAndExec() function was taking over 622 milliseconds to run
|
||||
on macOS 11 (BigSur) on Intel i5-1038NG7. I did some debugging by adding
|
||||
some custom traces and found that the fork() syscall is the bottleneck
|
||||
here, i.e., the first fork() takes around 359 milliseconds and the
|
||||
nested fork() takes around 263 milliseconds. Replacing the nested fork()
|
||||
and exec() with posix_spawn() reduces the time consumption to 257
|
||||
milliseconds!
|
||||
|
||||
See https://github.com/libuv/libuv/pull/3064 to know why fork() is so
|
||||
slow on macOS and why posix_spawn() is a better replacement.
|
||||
|
||||
Another point to note is that even base::LaunchProcess() from Chromium
|
||||
calls posix_spawnp() on macOS -
|
||||
https://source.chromium.org/chromium/chromium/src/+/8f8d82dea0fa8f11f57c74dbb65126f8daba58f7:base/process/launch_mac.cc;l=295-296
|
||||
|
||||
Change-Id: I25c6ee9629a1ae5d0c32b361b56a1ce0b4b0fd26
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3641386
|
||||
Reviewed-by: Mark Mentovai <mark@chromium.org>
|
||||
Commit-Queue: Mark Mentovai <mark@chromium.org>
|
||||
|
||||
diff --git a/third_party/crashpad/crashpad/AUTHORS b/third_party/crashpad/crashpad/AUTHORS
|
||||
index 8dcac3238870920d374b86033d05d77ebde351e9..02103924332eddbd158c04f8a395bb4a247e8bd9 100644
|
||||
--- a/third_party/crashpad/crashpad/AUTHORS
|
||||
+++ b/third_party/crashpad/crashpad/AUTHORS
|
||||
@@ -12,3 +12,4 @@ Opera Software ASA
|
||||
Vewd Software AS
|
||||
LG Electronics, Inc.
|
||||
MIPS Technologies, Inc.
|
||||
+Darshan Sen <raisinten@gmail.com>
|
||||
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
|
||||
index 35742be01f2c5c9f834bb7d2f03b261193bc69f2..2769936403ca6d4a1bb6c1f61ac6db776feba6d6 100644
|
||||
--- a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
|
||||
+++ b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
|
||||
@@ -45,7 +45,7 @@
|
||||
#include "util/linux/socket.h"
|
||||
#include "util/misc/address_sanitizer.h"
|
||||
#include "util/misc/from_pointer_cast.h"
|
||||
-#include "util/posix/double_fork_and_exec.h"
|
||||
+#include "util/posix/fork_and_spawn.h"
|
||||
#include "util/posix/scoped_mmap.h"
|
||||
#include "util/posix/signals.h"
|
||||
|
||||
@@ -445,7 +445,7 @@ bool CrashpadClient::StartHandler(
|
||||
|
||||
argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get()));
|
||||
argv.push_back("--shared-client-connection");
|
||||
- if (!DoubleForkAndExec(argv, nullptr, handler_sock.get(), false, nullptr)) {
|
||||
+ if (!ForkAndSpawn(argv, nullptr, handler_sock.get(), false, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -600,7 +600,7 @@ bool CrashpadClient::StartJavaHandlerForClient(
|
||||
int socket) {
|
||||
std::vector<std::string> argv = BuildAppProcessArgs(
|
||||
class_name, database, metrics_dir, url, annotations, arguments, socket);
|
||||
- return DoubleForkAndExec(argv, env, socket, false, nullptr);
|
||||
+ return ForkAndSpawn(argv, env, socket, false, nullptr);
|
||||
}
|
||||
|
||||
bool CrashpadClient::StartHandlerWithLinkerAtCrash(
|
||||
@@ -649,7 +649,7 @@ bool CrashpadClient::StartHandlerWithLinkerForClient(
|
||||
annotations,
|
||||
arguments,
|
||||
socket);
|
||||
- return DoubleForkAndExec(argv, env, socket, false, nullptr);
|
||||
+ return ForkAndSpawn(argv, env, socket, false, nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -683,7 +683,7 @@ bool CrashpadClient::StartHandlerForClient(
|
||||
|
||||
argv.push_back(FormatArgumentInt("initial-client-fd", socket));
|
||||
|
||||
- return DoubleForkAndExec(argv, nullptr, socket, true, nullptr);
|
||||
+ return ForkAndSpawn(argv, nullptr, socket, true, nullptr);
|
||||
}
|
||||
|
||||
// static
|
||||
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_mac.cc b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
|
||||
index d25bfb71638a4837d40950df770b461d8beac351..585bac910a4b6d3fef10ab23b86a04c2985173b8 100644
|
||||
--- a/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
|
||||
+++ b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
|
||||
@@ -36,7 +36,7 @@
|
||||
#include "util/mach/notify_server.h"
|
||||
#include "util/misc/clock.h"
|
||||
#include "util/misc/implicit_cast.h"
|
||||
-#include "util/posix/double_fork_and_exec.h"
|
||||
+#include "util/posix/fork_and_spawn.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@@ -343,7 +343,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface {
|
||||
// this parent process, which was probably using the exception server now
|
||||
// being restarted. The handler can’t monitor itself for its own crashes via
|
||||
// this interface.
|
||||
- if (!DoubleForkAndExec(
|
||||
+ if (!ForkAndSpawn(
|
||||
argv,
|
||||
nullptr,
|
||||
server_write_fd.get(),
|
||||
diff --git a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
|
||||
index 9e58d94aa499fdb7271a78ea21a1dcc1b12e3a52..3caa3b987b35be575558a312026cf6f19485c418 100644
|
||||
--- a/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
|
||||
+++ b/third_party/crashpad/crashpad/handler/linux/cros_crash_report_exception_handler.cc
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "util/linux/ptrace_client.h"
|
||||
#include "util/misc/metrics.h"
|
||||
#include "util/misc/uuid.h"
|
||||
-#include "util/posix/double_fork_and_exec.h"
|
||||
+#include "util/posix/fork_and_spawn.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@@ -266,12 +266,11 @@ bool CrosCrashReportExceptionHandler::HandleExceptionWithConnection(
|
||||
argv.push_back("--always_allow_feedback");
|
||||
}
|
||||
|
||||
- if (!DoubleForkAndExec(argv,
|
||||
- nullptr /* envp */,
|
||||
- file_writer.fd() /* preserve_fd */,
|
||||
- false /* use_path */,
|
||||
- nullptr /* child_function */)) {
|
||||
- LOG(ERROR) << "DoubleForkAndExec failed";
|
||||
+ if (!ForkAndSpawn(argv,
|
||||
+ nullptr /* envp */,
|
||||
+ file_writer.fd() /* preserve_fd */,
|
||||
+ false /* use_path */,
|
||||
+ nullptr /* child_function */)) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
|
||||
return false;
|
||||
diff --git a/third_party/crashpad/crashpad/util/BUILD.gn b/third_party/crashpad/crashpad/util/BUILD.gn
|
||||
index 8251312c78e1c1bfc4aff8fa8b7ade1f5c191e33..81c2484461843268bd8a6c21de3d7fd2ea504ac4 100644
|
||||
--- a/third_party/crashpad/crashpad/util/BUILD.gn
|
||||
+++ b/third_party/crashpad/crashpad/util/BUILD.gn
|
||||
@@ -296,10 +296,10 @@ crashpad_static_library("util") {
|
||||
sources += [
|
||||
"posix/close_multiple.cc",
|
||||
"posix/close_multiple.h",
|
||||
- "posix/double_fork_and_exec.cc",
|
||||
- "posix/double_fork_and_exec.h",
|
||||
"posix/drop_privileges.cc",
|
||||
"posix/drop_privileges.h",
|
||||
+ "posix/fork_and_spawn.cc",
|
||||
+ "posix/fork_and_spawn.h",
|
||||
"posix/process_info.h",
|
||||
|
||||
# These map signals to and from strings. While Fuchsia defines some of
|
||||
diff --git a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc b/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc
|
||||
deleted file mode 100644
|
||||
index 1960430954d3f6459dce688493db5c42047567b0..0000000000000000000000000000000000000000
|
||||
--- a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc
|
||||
+++ /dev/null
|
||||
@@ -1,166 +0,0 @@
|
||||
-// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
-//
|
||||
-// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-// you may not use this file except in compliance with the License.
|
||||
-// You may obtain a copy of the License at
|
||||
-//
|
||||
-// http://www.apache.org/licenses/LICENSE-2.0
|
||||
-//
|
||||
-// Unless required by applicable law or agreed to in writing, software
|
||||
-// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-// See the License for the specific language governing permissions and
|
||||
-// limitations under the License.
|
||||
-
|
||||
-#include "util/posix/double_fork_and_exec.h"
|
||||
-
|
||||
-#include <stdlib.h>
|
||||
-#include <string.h>
|
||||
-#include <sys/wait.h>
|
||||
-#include <unistd.h>
|
||||
-
|
||||
-#include "base/check_op.h"
|
||||
-#include "base/logging.h"
|
||||
-#include "base/posix/eintr_wrapper.h"
|
||||
-#include "base/strings/stringprintf.h"
|
||||
-#include "util/posix/close_multiple.h"
|
||||
-
|
||||
-namespace crashpad {
|
||||
-
|
||||
-bool DoubleForkAndExec(const std::vector<std::string>& argv,
|
||||
- const std::vector<std::string>* envp,
|
||||
- int preserve_fd,
|
||||
- bool use_path,
|
||||
- void (*child_function)()) {
|
||||
- DCHECK(!envp || !use_path);
|
||||
-
|
||||
- // argv_c contains const char* pointers and is terminated by nullptr. This is
|
||||
- // suitable for passing to execv(). Although argv_c is not used in the parent
|
||||
- // process, it must be built in the parent process because it’s unsafe to do
|
||||
- // so in the child or grandchild process.
|
||||
- std::vector<const char*> argv_c;
|
||||
- argv_c.reserve(argv.size() + 1);
|
||||
- for (const std::string& argument : argv) {
|
||||
- argv_c.push_back(argument.c_str());
|
||||
- }
|
||||
- argv_c.push_back(nullptr);
|
||||
-
|
||||
- std::vector<const char*> envp_c;
|
||||
- if (envp) {
|
||||
- envp_c.reserve(envp->size() + 1);
|
||||
- for (const std::string& variable : *envp) {
|
||||
- envp_c.push_back(variable.c_str());
|
||||
- }
|
||||
- envp_c.push_back(nullptr);
|
||||
- }
|
||||
-
|
||||
- // Double-fork(). The three processes involved are parent, child, and
|
||||
- // grandchild. The grandchild will call execv(). The child exits immediately
|
||||
- // after spawning the grandchild, so the grandchild becomes an orphan and its
|
||||
- // parent process ID becomes 1. This relieves the parent and child of the
|
||||
- // responsibility to reap the grandchild with waitpid() or similar. The
|
||||
- // grandchild is expected to outlive the parent process, so the parent
|
||||
- // shouldn’t be concerned with reaping it. This approach means that accidental
|
||||
- // early termination of the handler process will not result in a zombie
|
||||
- // process.
|
||||
- pid_t pid = fork();
|
||||
- if (pid < 0) {
|
||||
- PLOG(ERROR) << "fork";
|
||||
- return false;
|
||||
- }
|
||||
-
|
||||
- if (pid == 0) {
|
||||
- // Child process.
|
||||
-
|
||||
- if (child_function) {
|
||||
- child_function();
|
||||
- }
|
||||
-
|
||||
- // Call setsid(), creating a new process group and a new session, both led
|
||||
- // by this process. The new process group has no controlling terminal. This
|
||||
- // disconnects it from signals generated by the parent process’ terminal.
|
||||
- //
|
||||
- // setsid() is done in the child instead of the grandchild so that the
|
||||
- // grandchild will not be a session leader. If it were a session leader, an
|
||||
- // accidental open() of a terminal device without O_NOCTTY would make that
|
||||
- // terminal the controlling terminal.
|
||||
- //
|
||||
- // It’s not desirable for the grandchild to have a controlling terminal. The
|
||||
- // grandchild manages its own lifetime, such as by monitoring clients on its
|
||||
- // own and exiting when it loses all clients and when it deems it
|
||||
- // appropraite to do so. It may serve clients in different process groups or
|
||||
- // sessions than its original client, and receiving signals intended for its
|
||||
- // original client’s process group could be harmful in that case.
|
||||
- PCHECK(setsid() != -1) << "setsid";
|
||||
-
|
||||
- pid = fork();
|
||||
- if (pid < 0) {
|
||||
- PLOG(FATAL) << "fork";
|
||||
- }
|
||||
-
|
||||
- if (pid > 0) {
|
||||
- // Child process.
|
||||
-
|
||||
- // _exit() instead of exit(), because fork() was called.
|
||||
- _exit(EXIT_SUCCESS);
|
||||
- }
|
||||
-
|
||||
- // Grandchild process.
|
||||
-
|
||||
- CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd);
|
||||
-
|
||||
- // &argv_c[0] is a pointer to a pointer to const char data, but because of
|
||||
- // how C (not C++) works, execvp() wants a pointer to a const pointer to
|
||||
- // char data. It modifies neither the data nor the pointers, so the
|
||||
- // const_cast is safe.
|
||||
- char* const* argv_for_execv = const_cast<char* const*>(&argv_c[0]);
|
||||
-
|
||||
- if (envp) {
|
||||
- // This cast is safe for the same reason that the argv_for_execv cast is.
|
||||
- char* const* envp_for_execv = const_cast<char* const*>(&envp_c[0]);
|
||||
- execve(argv_for_execv[0], argv_for_execv, envp_for_execv);
|
||||
- PLOG(FATAL) << "execve " << argv_for_execv[0];
|
||||
- }
|
||||
-
|
||||
- if (use_path) {
|
||||
- execvp(argv_for_execv[0], argv_for_execv);
|
||||
- PLOG(FATAL) << "execvp " << argv_for_execv[0];
|
||||
- }
|
||||
-
|
||||
- execv(argv_for_execv[0], argv_for_execv);
|
||||
- PLOG(FATAL) << "execv " << argv_for_execv[0];
|
||||
- }
|
||||
-
|
||||
- // waitpid() for the child, so that it does not become a zombie process. The
|
||||
- // child normally exits quickly.
|
||||
- //
|
||||
- // Failures from this point on may result in the accumulation of a zombie, but
|
||||
- // should not be considered fatal. Log only warnings, but don’t treat these
|
||||
- // failures as a failure of the function overall.
|
||||
- int status;
|
||||
- pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
|
||||
- if (wait_pid == -1) {
|
||||
- PLOG(WARNING) << "waitpid";
|
||||
- return true;
|
||||
- }
|
||||
- DCHECK_EQ(wait_pid, pid);
|
||||
-
|
||||
- if (WIFSIGNALED(status)) {
|
||||
- int sig = WTERMSIG(status);
|
||||
- LOG(WARNING) << base::StringPrintf(
|
||||
- "intermediate process terminated by signal %d (%s)%s",
|
||||
- sig,
|
||||
- strsignal(sig),
|
||||
- WCOREDUMP(status) ? " (core dumped)" : "");
|
||||
- } else if (!WIFEXITED(status)) {
|
||||
- LOG(WARNING) << base::StringPrintf(
|
||||
- "intermediate process: unknown termination 0x%x", status);
|
||||
- } else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
|
||||
- LOG(WARNING) << "intermediate process exited with code "
|
||||
- << WEXITSTATUS(status);
|
||||
- }
|
||||
-
|
||||
- return true;
|
||||
-}
|
||||
-
|
||||
-} // namespace crashpad
|
||||
diff --git a/third_party/crashpad/crashpad/util/posix/fork_and_spawn.cc b/third_party/crashpad/crashpad/util/posix/fork_and_spawn.cc
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..c6a95bbfdcba45995b0034789c8bdb4423a25642
|
||||
--- /dev/null
|
||||
+++ b/third_party/crashpad/crashpad/util/posix/fork_and_spawn.cc
|
||||
@@ -0,0 +1,235 @@
|
||||
+// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
+//
|
||||
+// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
+// you may not use this file except in compliance with the License.
|
||||
+// You may obtain a copy of the License at
|
||||
+//
|
||||
+// http://www.apache.org/licenses/LICENSE-2.0
|
||||
+//
|
||||
+// Unless required by applicable law or agreed to in writing, software
|
||||
+// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
+// See the License for the specific language governing permissions and
|
||||
+// limitations under the License.
|
||||
+
|
||||
+#include "util/posix/fork_and_spawn.h"
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include <spawn.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <string.h>
|
||||
+#include <sys/wait.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+#include "base/check.h"
|
||||
+#include "base/check_op.h"
|
||||
+#include "base/logging.h"
|
||||
+#include "base/posix/eintr_wrapper.h"
|
||||
+#include "base/strings/stringprintf.h"
|
||||
+#include "build/build_config.h"
|
||||
+#include "util/posix/close_multiple.h"
|
||||
+
|
||||
+extern char** environ;
|
||||
+
|
||||
+namespace crashpad {
|
||||
+
|
||||
+namespace {
|
||||
+
|
||||
+#if BUILDFLAG(IS_APPLE)
|
||||
+
|
||||
+class PosixSpawnAttr {
|
||||
+ public:
|
||||
+ PosixSpawnAttr() {
|
||||
+ PCHECK((errno = posix_spawnattr_init(&attr_)) == 0)
|
||||
+ << "posix_spawnattr_init";
|
||||
+ }
|
||||
+
|
||||
+ PosixSpawnAttr(const PosixSpawnAttr&) = delete;
|
||||
+ PosixSpawnAttr& operator=(const PosixSpawnAttr&) = delete;
|
||||
+
|
||||
+ ~PosixSpawnAttr() {
|
||||
+ PCHECK((errno = posix_spawnattr_destroy(&attr_)) == 0)
|
||||
+ << "posix_spawnattr_destroy";
|
||||
+ }
|
||||
+
|
||||
+ void SetFlags(short flags) {
|
||||
+ PCHECK((errno = posix_spawnattr_setflags(&attr_, flags)) == 0)
|
||||
+ << "posix_spawnattr_setflags";
|
||||
+ }
|
||||
+
|
||||
+ const posix_spawnattr_t* Get() const { return &attr_; }
|
||||
+
|
||||
+ private:
|
||||
+ posix_spawnattr_t attr_;
|
||||
+};
|
||||
+
|
||||
+class PosixSpawnFileActions {
|
||||
+ public:
|
||||
+ PosixSpawnFileActions() {
|
||||
+ PCHECK((errno = posix_spawn_file_actions_init(&file_actions_)) == 0)
|
||||
+ << "posix_spawn_file_actions_init";
|
||||
+ }
|
||||
+
|
||||
+ PosixSpawnFileActions(const PosixSpawnFileActions&) = delete;
|
||||
+ PosixSpawnFileActions& operator=(const PosixSpawnFileActions&) = delete;
|
||||
+
|
||||
+ ~PosixSpawnFileActions() {
|
||||
+ PCHECK((errno = posix_spawn_file_actions_destroy(&file_actions_)) == 0)
|
||||
+ << "posix_spawn_file_actions_destroy";
|
||||
+ }
|
||||
+
|
||||
+ void AddInheritedFileDescriptor(int fd) {
|
||||
+ PCHECK((errno = posix_spawn_file_actions_addinherit_np(&file_actions_,
|
||||
+ fd)) == 0)
|
||||
+ << "posix_spawn_file_actions_addinherit_np";
|
||||
+ }
|
||||
+
|
||||
+ const posix_spawn_file_actions_t* Get() const { return &file_actions_; }
|
||||
+
|
||||
+ private:
|
||||
+ posix_spawn_file_actions_t file_actions_;
|
||||
+};
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
+bool ForkAndSpawn(const std::vector<std::string>& argv,
|
||||
+ const std::vector<std::string>* envp,
|
||||
+ int preserve_fd,
|
||||
+ bool use_path,
|
||||
+ void (*child_function)()) {
|
||||
+ // argv_c contains const char* pointers and is terminated by nullptr. This is
|
||||
+ // suitable for passing to posix_spawn() or posix_spawnp(). Although argv_c is
|
||||
+ // not used in the parent process, it must be built in the parent process
|
||||
+ // because it’s unsafe to do so in the child.
|
||||
+ std::vector<const char*> argv_c;
|
||||
+ argv_c.reserve(argv.size() + 1);
|
||||
+ for (const std::string& argument : argv) {
|
||||
+ argv_c.push_back(argument.c_str());
|
||||
+ }
|
||||
+ argv_c.push_back(nullptr);
|
||||
+
|
||||
+ std::vector<const char*> envp_c;
|
||||
+ if (envp) {
|
||||
+ envp_c.reserve(envp->size() + 1);
|
||||
+ for (const std::string& variable : *envp) {
|
||||
+ envp_c.push_back(variable.c_str());
|
||||
+ }
|
||||
+ envp_c.push_back(nullptr);
|
||||
+ }
|
||||
+
|
||||
+ // The three processes involved are parent, child, and grandchild. The child
|
||||
+ // exits immediately after spawning the grandchild, so the grandchild becomes
|
||||
+ // an orphan and its parent process ID becomes 1. This relieves the parent and
|
||||
+ // child of the responsibility to reap the grandchild with waitpid() or
|
||||
+ // similar. The grandchild is expected to outlive the parent process, so the
|
||||
+ // parent shouldn’t be concerned with reaping it. This approach means that
|
||||
+ // accidental early termination of the handler process will not result in a
|
||||
+ // zombie process.
|
||||
+ pid_t pid = fork();
|
||||
+ if (pid < 0) {
|
||||
+ PLOG(ERROR) << "fork";
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if (pid == 0) {
|
||||
+ // Child process.
|
||||
+
|
||||
+ if (child_function) {
|
||||
+ child_function();
|
||||
+ }
|
||||
+
|
||||
+ // Call setsid(), creating a new process group and a new session, both led
|
||||
+ // by this process. The new process group has no controlling terminal. This
|
||||
+ // disconnects it from signals generated by the parent process’ terminal.
|
||||
+ //
|
||||
+ // setsid() is done in the child instead of the grandchild so that the
|
||||
+ // grandchild will not be a session leader. If it were a session leader, an
|
||||
+ // accidental open() of a terminal device without O_NOCTTY would make that
|
||||
+ // terminal the controlling terminal.
|
||||
+ //
|
||||
+ // It’s not desirable for the grandchild to have a controlling terminal. The
|
||||
+ // grandchild manages its own lifetime, such as by monitoring clients on its
|
||||
+ // own and exiting when it loses all clients and when it deems it
|
||||
+ // appropraite to do so. It may serve clients in different process groups or
|
||||
+ // sessions than its original client, and receiving signals intended for its
|
||||
+ // original client’s process group could be harmful in that case.
|
||||
+ PCHECK(setsid() != -1) << "setsid";
|
||||
+
|
||||
+ // &argv_c[0] is a pointer to a pointer to const char data, but because of
|
||||
+ // how C (not C++) works, posix_spawn() and posix_spawnp() want a pointer to
|
||||
+ // a const pointer to char data. They modifies neither the data nor the
|
||||
+ // pointers, so the const_cast is safe.
|
||||
+ char* const* argv_for_spawn = const_cast<char* const*>(argv_c.data());
|
||||
+
|
||||
+ // This cast is safe for the same reason that the argv_for_spawn cast is.
|
||||
+ char* const* envp_for_spawn =
|
||||
+ envp ? const_cast<char* const*>(envp_c.data()) : environ;
|
||||
+
|
||||
+#if BUILDFLAG(IS_APPLE)
|
||||
+ PosixSpawnAttr attr;
|
||||
+ attr.SetFlags(POSIX_SPAWN_CLOEXEC_DEFAULT);
|
||||
+
|
||||
+ PosixSpawnFileActions file_actions;
|
||||
+ for (int fd = 0; fd <= STDERR_FILENO; ++fd) {
|
||||
+ file_actions.AddInheritedFileDescriptor(fd);
|
||||
+ }
|
||||
+ file_actions.AddInheritedFileDescriptor(preserve_fd);
|
||||
+
|
||||
+ const posix_spawnattr_t* attr_p = attr.Get();
|
||||
+ const posix_spawn_file_actions_t* file_actions_p = file_actions.Get();
|
||||
+#else
|
||||
+ CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd);
|
||||
+
|
||||
+ const posix_spawnattr_t* attr_p = nullptr;
|
||||
+ const posix_spawn_file_actions_t* file_actions_p = nullptr;
|
||||
+#endif
|
||||
+
|
||||
+ auto posix_spawn_fp = use_path ? posix_spawnp : posix_spawn;
|
||||
+ if ((errno = posix_spawn_fp(&pid,
|
||||
+ argv_for_spawn[0],
|
||||
+ file_actions_p,
|
||||
+ attr_p,
|
||||
+ argv_for_spawn,
|
||||
+ envp_for_spawn)) != 0) {
|
||||
+ PLOG(FATAL) << (use_path ? "posix_spawnp" : "posix_spawn");
|
||||
+ }
|
||||
+
|
||||
+ // _exit() instead of exit(), because fork() was called.
|
||||
+ _exit(EXIT_SUCCESS);
|
||||
+ }
|
||||
+
|
||||
+ // waitpid() for the child, so that it does not become a zombie process. The
|
||||
+ // child normally exits quickly.
|
||||
+ //
|
||||
+ // Failures from this point on may result in the accumulation of a zombie, but
|
||||
+ // should not be considered fatal. Log only warnings, but don’t treat these
|
||||
+ // failures as a failure of the function overall.
|
||||
+ int status;
|
||||
+ pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));
|
||||
+ if (wait_pid == -1) {
|
||||
+ PLOG(WARNING) << "waitpid";
|
||||
+ return true;
|
||||
+ }
|
||||
+ DCHECK_EQ(wait_pid, pid);
|
||||
+
|
||||
+ if (WIFSIGNALED(status)) {
|
||||
+ int sig = WTERMSIG(status);
|
||||
+ LOG(WARNING) << base::StringPrintf(
|
||||
+ "intermediate process terminated by signal %d (%s)%s",
|
||||
+ sig,
|
||||
+ strsignal(sig),
|
||||
+ WCOREDUMP(status) ? " (core dumped)" : "");
|
||||
+ } else if (!WIFEXITED(status)) {
|
||||
+ LOG(WARNING) << base::StringPrintf(
|
||||
+ "intermediate process: unknown termination 0x%x", status);
|
||||
+ } else if (WEXITSTATUS(status) != EXIT_SUCCESS) {
|
||||
+ LOG(WARNING) << "intermediate process exited with code "
|
||||
+ << WEXITSTATUS(status);
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+} // namespace crashpad
|
||||
diff --git a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.h b/third_party/crashpad/crashpad/util/posix/fork_and_spawn.h
|
||||
similarity index 76%
|
||||
rename from third_party/crashpad/crashpad/util/posix/double_fork_and_exec.h
|
||||
rename to third_party/crashpad/crashpad/util/posix/fork_and_spawn.h
|
||||
index 02fc0f28f196b447132a2dcfaebdaaa5a916a38a..fc55aa3a37652e4ba18c66db90124abd9cad2e51 100644
|
||||
--- a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.h
|
||||
+++ b/third_party/crashpad/crashpad/util/posix/fork_and_spawn.h
|
||||
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
-#ifndef CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_
|
||||
-#define CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_
|
||||
+#ifndef CRASHPAD_UTIL_POSIX_FORK_AND_SPAWN_H_
|
||||
+#define CRASHPAD_UTIL_POSIX_FORK_AND_SPAWN_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -23,7 +23,7 @@ namespace crashpad {
|
||||
//! \brief Executes a (grand-)child process.
|
||||
//!
|
||||
//! The grandchild process will be started through the
|
||||
-//! double-`fork()`-and-`execv()` pattern. This allows the grandchild to fully
|
||||
+//! `fork()`-and-`posix_spawn()` pattern. This allows the grandchild to fully
|
||||
//! disassociate from the parent. The grandchild will not be a member of the
|
||||
//! parent’s process group or session and will not have a controlling terminal,
|
||||
//! providing isolation from signals not intended for it. The grandchild’s
|
||||
@@ -37,7 +37,7 @@ namespace crashpad {
|
||||
//! \param[in] argv The argument vector to start the grandchild process with.
|
||||
//! `argv[0]` is used as the path to the executable.
|
||||
//! \param[in] envp A vector of environment variables of the form `var=value` to
|
||||
-//! be passed to `execve()`. If this value is `nullptr`, the current
|
||||
+//! be passed to `posix_spawn()`. If this value is `nullptr`, the current
|
||||
//! environment is used.
|
||||
//! \param[in] preserve_fd A file descriptor to be inherited by the grandchild
|
||||
//! process. This file descriptor is inherited in addition to the three file
|
||||
@@ -45,16 +45,13 @@ namespace crashpad {
|
||||
//! if no additional file descriptors are to be inherited.
|
||||
//! \param[in] use_path Whether to consult the `PATH` environment variable when
|
||||
//! requested to start an executable at a non-absolute path. If `false`,
|
||||
-//! `execv()`, which does not consult `PATH`, will be used. If `true`,
|
||||
-//! `execvp()`, which does consult `PATH`, will be used.
|
||||
+//! `posix_spawn()`, which does not consult `PATH`, will be used. If `true`,
|
||||
+//! `posix_spawnp()`, which does consult `PATH`, will be used.
|
||||
//! \param[in] child_function If not `nullptr`, this function will be called in
|
||||
-//! the intermediate child process, prior to the second `fork()`. Take note
|
||||
+//! the intermediate child process, prior to the `posix_spawn()`. Take note
|
||||
//! that this function will run in the context of a forked process, and must
|
||||
//! be safe for that purpose.
|
||||
//!
|
||||
-//! Setting both \a envp to a value other than `nullptr` and \a use_path to
|
||||
-//! `true` is not currently supported.
|
||||
-//!
|
||||
//! \return `true` on success, and `false` on failure with a message logged.
|
||||
//! Only failures that occur in the parent process that indicate a definite
|
||||
//! failure to start the the grandchild are reported in the return value.
|
||||
@@ -63,12 +60,12 @@ namespace crashpad {
|
||||
//! terminating. The caller assumes the responsibility for detecting such
|
||||
//! failures, for example, by observing a failure to perform a successful
|
||||
//! handshake with the grandchild process.
|
||||
-bool DoubleForkAndExec(const std::vector<std::string>& argv,
|
||||
- const std::vector<std::string>* envp,
|
||||
- int preserve_fd,
|
||||
- bool use_path,
|
||||
- void (*child_function)());
|
||||
+bool ForkAndSpawn(const std::vector<std::string>& argv,
|
||||
+ const std::vector<std::string>* envp,
|
||||
+ int preserve_fd,
|
||||
+ bool use_path,
|
||||
+ void (*child_function)());
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
-#endif // CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_
|
||||
+#endif // CRASHPAD_UTIL_POSIX_FORK_AND_SPAWN_H_
|
||||
57
patches/chromium/remove_default_window_title.patch
Normal file
57
patches/chromium/remove_default_window_title.patch
Normal file
@@ -0,0 +1,57 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Anderson <thomasanderson@chromium.org>
|
||||
Date: Fri, 24 Jun 2022 22:27:07 +0000
|
||||
Subject: Remove default window title
|
||||
|
||||
This is to prevent the enterprise startup dialog from having "Ozone X11"
|
||||
in the titlebar. The intention is for the window to have no title [1]
|
||||
|
||||
[1] https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc;drc=855b63f8fe9115f8b38d4736a259f9a96f83fcc6;l=209
|
||||
|
||||
R=sky
|
||||
|
||||
Change-Id: Iecc200941ad36b6d9feb91e88791b3612e941ea6
|
||||
Fixed: 1336343
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3722478
|
||||
Reviewed-by: Scott Violet <sky@chromium.org>
|
||||
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1017858}
|
||||
|
||||
diff --git a/ui/ozone/platform/x11/ozone_platform_x11.cc b/ui/ozone/platform/x11/ozone_platform_x11.cc
|
||||
index e312287e4aca61b51a69c8413088f56f9f704b5e..06bb59fe5e855d0a339e738cf12c566afcf376ac 100644
|
||||
--- a/ui/ozone/platform/x11/ozone_platform_x11.cc
|
||||
+++ b/ui/ozone/platform/x11/ozone_platform_x11.cc
|
||||
@@ -102,7 +102,6 @@ class OzonePlatformX11 : public OzonePlatform,
|
||||
PlatformWindowInitProperties properties) override {
|
||||
auto window = std::make_unique<X11Window>(delegate);
|
||||
window->Initialize(std::move(properties));
|
||||
- window->SetTitle(u"Ozone X11");
|
||||
return std::move(window);
|
||||
}
|
||||
|
||||
diff --git a/ui/ozone/platform/x11/x11_window.cc b/ui/ozone/platform/x11/x11_window.cc
|
||||
index 15b5f6ff21d3fca9e06e95bae08d1b2c0d2e3b1c..0b002c3950c9d450f42212f5dc9c5ce1e7cff010 100644
|
||||
--- a/ui/ozone/platform/x11/x11_window.cc
|
||||
+++ b/ui/ozone/platform/x11/x11_window.cc
|
||||
@@ -362,6 +362,8 @@ void X11Window::Initialize(PlatformWindowInitProperties properties) {
|
||||
if (wm_role_name)
|
||||
SetWindowRole(xwindow_, std::string(wm_role_name));
|
||||
|
||||
+ SetTitle(u"");
|
||||
+
|
||||
if (properties.remove_standard_frame) {
|
||||
// Setting _GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED tells gnome-shell to not force
|
||||
// fullscreen on the window when it matches the desktop size.
|
||||
diff --git a/ui/ozone/platform/x11/x11_window.h b/ui/ozone/platform/x11/x11_window.h
|
||||
index 0b4ccfed94123120d491d08a4c95a02624d9d0ef..147304843f2acc90cf534b8da7710541605fe880 100644
|
||||
--- a/ui/ozone/platform/x11/x11_window.h
|
||||
+++ b/ui/ozone/platform/x11/x11_window.h
|
||||
@@ -363,7 +363,7 @@ class X11Window : public PlatformWindow,
|
||||
// Was this window initialized with the override_redirect window attribute?
|
||||
bool override_redirect_ = false;
|
||||
|
||||
- std::u16string window_title_;
|
||||
+ absl::optional<std::u16string> window_title_;
|
||||
|
||||
// Whether the window is visible with respect to Aura.
|
||||
bool window_mapped_in_client_ = false;
|
||||
@@ -13,11 +13,17 @@
|
||||
|
||||
"src/electron/patches/nan": "src/third_party/nan",
|
||||
|
||||
"src/electron/patches/ffmpeg": "src/third_party/ffmpeg",
|
||||
|
||||
"src/electron/patches/perfetto": "src/third_party/perfetto",
|
||||
|
||||
"src/electron/patches/squirrel.mac": "src/third_party/squirrel.mac",
|
||||
|
||||
"src/electron/patches/Mantle": "src/third_party/squirrel.mac/vendor/Mantle",
|
||||
|
||||
"src/electron/patches/ReactiveObjC": "src/third_party/squirrel.mac/vendor/ReactiveObjC"
|
||||
"src/electron/patches/ReactiveObjC": "src/third_party/squirrel.mac/vendor/ReactiveObjC",
|
||||
|
||||
"src/electron/patches/angle": "src/third_party/angle",
|
||||
|
||||
"src/electron/patches/pdfium": "src/third_party/pdfium"
|
||||
}
|
||||
|
||||
1
patches/ffmpeg/.patches
Normal file
1
patches/ffmpeg/.patches
Normal file
@@ -0,0 +1 @@
|
||||
cherry-pick-e481fc655a62.patch
|
||||
27
patches/ffmpeg/cherry-pick-e481fc655a62.patch
Normal file
27
patches/ffmpeg/cherry-pick-e481fc655a62.patch
Normal file
@@ -0,0 +1,27 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Dan Sanders <sandersd@chromium.org>
|
||||
Date: Tue, 3 May 2022 14:39:37 -0700
|
||||
Subject: Do not parse late SEI messages during decoding.
|
||||
|
||||
Bug: 1306751
|
||||
Change-Id: I2088b9ff89bd8eee8ab82675258af302d9bfccf9
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/third_party/ffmpeg/+/3625832
|
||||
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
|
||||
|
||||
diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
|
||||
index 73b17ba43c3eda5bed4b4abc70d22bbebf0c2eac..a6413bc20db5d08a5c8f1ee4b07edbd4870e168c 100644
|
||||
--- a/libavcodec/h264dec.c
|
||||
+++ b/libavcodec/h264dec.c
|
||||
@@ -678,6 +678,12 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
|
||||
avpriv_request_sample(avctx, "data partitioning");
|
||||
break;
|
||||
case H264_NAL_SEI:
|
||||
+ // If setup is finished, threads can contend over the contents of
|
||||
+ // the active SEI.
|
||||
+ if (h->setup_finished) {
|
||||
+ av_log(h->avctx, AV_LOG_DEBUG, "Late SEI\n");
|
||||
+ break;
|
||||
+ }
|
||||
ret = ff_h264_sei_decode(&h->sei, &nal->gb, &h->ps, avctx);
|
||||
h->has_recovery_point = h->has_recovery_point || h->sei.recovery_point.recovery_frame_cnt != -1;
|
||||
if (avctx->debug & FF_DEBUG_GREEN_MD)
|
||||
1
patches/pdfium/.patches
Normal file
1
patches/pdfium/.patches
Normal file
@@ -0,0 +1 @@
|
||||
cherry-pick-3466cc056b05.patch
|
||||
283
patches/pdfium/cherry-pick-3466cc056b05.patch
Normal file
283
patches/pdfium/cherry-pick-3466cc056b05.patch
Normal file
@@ -0,0 +1,283 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Sepez <tsepez@chromium.org>
|
||||
Date: Tue, 12 Jul 2022 18:13:14 +0000
|
||||
Subject: M102: Retain nodes when manipulating their dictionaries in
|
||||
CPDF_NameTree.
|
||||
|
||||
-- Pass retain ptrs consistently in a few other places.
|
||||
|
||||
Bug: chromium:1335861
|
||||
Change-Id: If23cf6b6ec39ef02384beaa6745e1c7256160cba
|
||||
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/94430
|
||||
Reviewed-by: Lei Zhang <thestig@chromium.org>
|
||||
Commit-Queue: Tom Sepez <tsepez@chromium.org>
|
||||
(cherry picked from commit ebebb757c1f210ccc16e0cb2b425860a141a4e9f)
|
||||
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/95271
|
||||
Reviewed-by: Nigi <nigi@chromium.org>
|
||||
|
||||
diff --git a/core/fpdfapi/parser/cpdf_array.cpp b/core/fpdfapi/parser/cpdf_array.cpp
|
||||
index 708e6778a8d5383c2fd15518e091bb439212a1c7..6aa6f3089f191799231d49949de7f60adf367b5a 100644
|
||||
--- a/core/fpdfapi/parser/cpdf_array.cpp
|
||||
+++ b/core/fpdfapi/parser/cpdf_array.cpp
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "core/fpdfapi/parser/cpdf_boolean.h"
|
||||
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
|
||||
#include "core/fpdfapi/parser/cpdf_name.h"
|
||||
#include "core/fpdfapi/parser/cpdf_number.h"
|
||||
#include "core/fpdfapi/parser/cpdf_reference.h"
|
||||
@@ -153,6 +154,10 @@ float CPDF_Array::GetNumberAt(size_t index) const {
|
||||
return m_Objects[index]->GetNumber();
|
||||
}
|
||||
|
||||
+RetainPtr<CPDF_Dictionary> CPDF_Array::GetMutableDictAt(size_t index) {
|
||||
+ return pdfium::WrapRetain(GetDictAt(index));
|
||||
+}
|
||||
+
|
||||
CPDF_Dictionary* CPDF_Array::GetDictAt(size_t index) {
|
||||
CPDF_Object* p = GetDirectObjectAt(index);
|
||||
if (!p)
|
||||
diff --git a/core/fpdfapi/parser/cpdf_array.h b/core/fpdfapi/parser/cpdf_array.h
|
||||
index 2270c7d9c8f22bbac3d67c8c5b6570bc128a72b4..223cd59ab7cb6cc2c08071118d1fbb3241904c6b 100644
|
||||
--- a/core/fpdfapi/parser/cpdf_array.h
|
||||
+++ b/core/fpdfapi/parser/cpdf_array.h
|
||||
@@ -62,7 +62,8 @@ class CPDF_Array final : public CPDF_Object {
|
||||
bool GetBooleanAt(size_t index, bool bDefault) const;
|
||||
int GetIntegerAt(size_t index) const;
|
||||
float GetNumberAt(size_t index) const;
|
||||
- CPDF_Dictionary* GetDictAt(size_t index);
|
||||
+ RetainPtr<CPDF_Dictionary> GetMutableDictAt(size_t index);
|
||||
+ CPDF_Dictionary* GetDictAt(size_t index); // prefer previous form.
|
||||
const CPDF_Dictionary* GetDictAt(size_t index) const;
|
||||
CPDF_Stream* GetStreamAt(size_t index);
|
||||
const CPDF_Stream* GetStreamAt(size_t index) const;
|
||||
diff --git a/core/fpdfapi/parser/cpdf_dictionary.cpp b/core/fpdfapi/parser/cpdf_dictionary.cpp
|
||||
index fc671e76bfb6f17af93a19cca57aa0532c4111e4..047b25e2c0b539a00814de6cd80d8f224413586b 100644
|
||||
--- a/core/fpdfapi/parser/cpdf_dictionary.cpp
|
||||
+++ b/core/fpdfapi/parser/cpdf_dictionary.cpp
|
||||
@@ -148,6 +148,11 @@ float CPDF_Dictionary::GetNumberFor(const ByteString& key) const {
|
||||
return p ? p->GetNumber() : 0;
|
||||
}
|
||||
|
||||
+RetainPtr<CPDF_Dictionary> CPDF_Dictionary::GetMutableDictFor(
|
||||
+ const ByteString& key) {
|
||||
+ return pdfium::WrapRetain(GetDictFor(key));
|
||||
+}
|
||||
+
|
||||
const CPDF_Dictionary* CPDF_Dictionary::GetDictFor(
|
||||
const ByteString& key) const {
|
||||
const CPDF_Object* p = GetDirectObjectFor(key);
|
||||
@@ -165,6 +170,11 @@ CPDF_Dictionary* CPDF_Dictionary::GetDictFor(const ByteString& key) {
|
||||
static_cast<const CPDF_Dictionary*>(this)->GetDictFor(key));
|
||||
}
|
||||
|
||||
+RetainPtr<CPDF_Array> CPDF_Dictionary::GetMutableArrayFor(
|
||||
+ const ByteString& key) {
|
||||
+ return pdfium::WrapRetain(GetArrayFor(key));
|
||||
+}
|
||||
+
|
||||
const CPDF_Array* CPDF_Dictionary::GetArrayFor(const ByteString& key) const {
|
||||
return ToArray(GetDirectObjectFor(key));
|
||||
}
|
||||
diff --git a/core/fpdfapi/parser/cpdf_dictionary.h b/core/fpdfapi/parser/cpdf_dictionary.h
|
||||
index d2f814327e7b880d68119aa938e73af1260668d5..fe990efaa9844fc6a7aa2c6fd671d74a2446383a 100644
|
||||
--- a/core/fpdfapi/parser/cpdf_dictionary.h
|
||||
+++ b/core/fpdfapi/parser/cpdf_dictionary.h
|
||||
@@ -67,9 +67,11 @@ class CPDF_Dictionary final : public CPDF_Object {
|
||||
int GetIntegerFor(const ByteString& key, int default_int) const;
|
||||
float GetNumberFor(const ByteString& key) const;
|
||||
const CPDF_Dictionary* GetDictFor(const ByteString& key) const;
|
||||
- CPDF_Dictionary* GetDictFor(const ByteString& key);
|
||||
+ CPDF_Dictionary* GetDictFor(const ByteString& key); // Prefer next form.
|
||||
+ RetainPtr<CPDF_Dictionary> GetMutableDictFor(const ByteString& key);
|
||||
const CPDF_Array* GetArrayFor(const ByteString& key) const;
|
||||
- CPDF_Array* GetArrayFor(const ByteString& key);
|
||||
+ CPDF_Array* GetArrayFor(const ByteString& key); // Prefer next form.
|
||||
+ RetainPtr<CPDF_Array> GetMutableArrayFor(const ByteString& key);
|
||||
const CPDF_Stream* GetStreamFor(const ByteString& key) const;
|
||||
CPDF_Stream* GetStreamFor(const ByteString& key);
|
||||
CFX_FloatRect GetRectFor(const ByteString& key) const;
|
||||
diff --git a/core/fpdfdoc/cpdf_nametree.cpp b/core/fpdfdoc/cpdf_nametree.cpp
|
||||
index dfd4e619c055229135b67bb056cb546992fe1d21..20b68b5874ff14b5625c6fc028211ce44b53a119 100644
|
||||
--- a/core/fpdfdoc/cpdf_nametree.cpp
|
||||
+++ b/core/fpdfdoc/cpdf_nametree.cpp
|
||||
@@ -42,7 +42,7 @@ std::pair<WideString, WideString> GetNodeLimitsAndSanitize(
|
||||
// Get the limit arrays that leaf array |pFind| is under in the tree with root
|
||||
// |pNode|. |pLimits| will hold all the limit arrays from the leaf up to before
|
||||
// the root. Return true if successful.
|
||||
-bool GetNodeAncestorsLimitsInternal(CPDF_Dictionary* pNode,
|
||||
+bool GetNodeAncestorsLimitsInternal(const RetainPtr<CPDF_Dictionary>& pNode,
|
||||
const CPDF_Array* pFind,
|
||||
int nLevel,
|
||||
std::vector<CPDF_Array*>* pLimits) {
|
||||
@@ -59,7 +59,7 @@ bool GetNodeAncestorsLimitsInternal(CPDF_Dictionary* pNode,
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < pKids->size(); ++i) {
|
||||
- CPDF_Dictionary* pKid = pKids->GetDictAt(i);
|
||||
+ RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
|
||||
if (!pKid)
|
||||
continue;
|
||||
|
||||
@@ -73,8 +73,9 @@ bool GetNodeAncestorsLimitsInternal(CPDF_Dictionary* pNode,
|
||||
|
||||
// Wrapper for GetNodeAncestorsLimitsInternal() so callers do not need to know
|
||||
// about the details.
|
||||
-std::vector<CPDF_Array*> GetNodeAncestorsLimits(CPDF_Dictionary* pNode,
|
||||
- const CPDF_Array* pFind) {
|
||||
+std::vector<CPDF_Array*> GetNodeAncestorsLimits(
|
||||
+ const RetainPtr<CPDF_Dictionary>& pNode,
|
||||
+ const CPDF_Array* pFind) {
|
||||
std::vector<CPDF_Array*> results;
|
||||
GetNodeAncestorsLimitsInternal(pNode, pFind, 0, &results);
|
||||
return results;
|
||||
@@ -168,21 +169,22 @@ bool UpdateNodesAndLimitsUponDeletion(CPDF_Dictionary* pNode,
|
||||
// will be the index of |csName| in |ppFind|. If |csName| is not found, |ppFind|
|
||||
// will be the leaf array that |csName| should be added to, and |pFindIndex|
|
||||
// will be the index that it should be added at.
|
||||
-CPDF_Object* SearchNameNodeByNameInternal(CPDF_Dictionary* pNode,
|
||||
- const WideString& csName,
|
||||
- int nLevel,
|
||||
- size_t* nIndex,
|
||||
- CPDF_Array** ppFind,
|
||||
- int* pFindIndex) {
|
||||
+CPDF_Object* SearchNameNodeByNameInternal(
|
||||
+ const RetainPtr<CPDF_Dictionary>& pNode,
|
||||
+ const WideString& csName,
|
||||
+ int nLevel,
|
||||
+ size_t* nIndex,
|
||||
+ RetainPtr<CPDF_Array>* ppFind,
|
||||
+ int* pFindIndex) {
|
||||
if (nLevel > kNameTreeMaxRecursion)
|
||||
return nullptr;
|
||||
|
||||
- CPDF_Array* pLimits = pNode->GetArrayFor("Limits");
|
||||
- CPDF_Array* pNames = pNode->GetArrayFor("Names");
|
||||
+ RetainPtr<CPDF_Array> pLimits = pNode->GetMutableArrayFor("Limits");
|
||||
+ RetainPtr<CPDF_Array> pNames = pNode->GetMutableArrayFor("Names");
|
||||
if (pLimits) {
|
||||
WideString csLeft;
|
||||
WideString csRight;
|
||||
- std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits);
|
||||
+ std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits.Get());
|
||||
// Skip this node if the name to look for is smaller than its lower limit.
|
||||
if (csName.Compare(csLeft) < 0)
|
||||
return nullptr;
|
||||
@@ -221,12 +223,12 @@ CPDF_Object* SearchNameNodeByNameInternal(CPDF_Dictionary* pNode,
|
||||
}
|
||||
|
||||
// Search through the node's children.
|
||||
- CPDF_Array* pKids = pNode->GetArrayFor("Kids");
|
||||
+ RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor("Kids");
|
||||
if (!pKids)
|
||||
return nullptr;
|
||||
|
||||
for (size_t i = 0; i < pKids->size(); i++) {
|
||||
- CPDF_Dictionary* pKid = pKids->GetDictAt(i);
|
||||
+ RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
|
||||
if (!pKid)
|
||||
continue;
|
||||
|
||||
@@ -240,9 +242,9 @@ CPDF_Object* SearchNameNodeByNameInternal(CPDF_Dictionary* pNode,
|
||||
|
||||
// Wrapper for SearchNameNodeByNameInternal() so callers do not need to know
|
||||
// about the details.
|
||||
-CPDF_Object* SearchNameNodeByName(CPDF_Dictionary* pNode,
|
||||
+CPDF_Object* SearchNameNodeByName(const RetainPtr<CPDF_Dictionary>& pNode,
|
||||
const WideString& csName,
|
||||
- CPDF_Array** ppFind,
|
||||
+ RetainPtr<CPDF_Array>* ppFind,
|
||||
int* pFindIndex) {
|
||||
size_t nIndex = 0;
|
||||
return SearchNameNodeByNameInternal(pNode, csName, 0, &nIndex, ppFind,
|
||||
@@ -438,17 +440,17 @@ size_t CPDF_NameTree::GetCount() const {
|
||||
|
||||
bool CPDF_NameTree::AddValueAndName(RetainPtr<CPDF_Object> pObj,
|
||||
const WideString& name) {
|
||||
- CPDF_Array* pFind = nullptr;
|
||||
+ RetainPtr<CPDF_Array> pFind;
|
||||
int nFindIndex = -1;
|
||||
// Handle the corner case where the root node is empty. i.e. No kids and no
|
||||
// names. In which case, just insert into it and skip all the searches.
|
||||
- CPDF_Array* pNames = m_pRoot->GetArrayFor("Names");
|
||||
+ RetainPtr<CPDF_Array> pNames = m_pRoot->GetMutableArrayFor("Names");
|
||||
if (pNames && pNames->IsEmpty() && !m_pRoot->GetArrayFor("Kids"))
|
||||
pFind = pNames;
|
||||
|
||||
if (!pFind) {
|
||||
// Fail if the tree already contains this name or if the tree is too deep.
|
||||
- if (SearchNameNodeByName(m_pRoot.Get(), name, &pFind, &nFindIndex))
|
||||
+ if (SearchNameNodeByName(m_pRoot, name, &pFind, &nFindIndex))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -478,7 +480,7 @@ bool CPDF_NameTree::AddValueAndName(RetainPtr<CPDF_Object> pObj,
|
||||
// Expand the limits that the newly added name is under, if the name falls
|
||||
// outside of the limits of its leaf array or any arrays above it.
|
||||
std::vector<CPDF_Array*> all_limits =
|
||||
- GetNodeAncestorsLimits(m_pRoot.Get(), pFind);
|
||||
+ GetNodeAncestorsLimits(m_pRoot, pFind.Get());
|
||||
for (auto* pLimits : all_limits) {
|
||||
if (!pLimits)
|
||||
continue;
|
||||
@@ -524,7 +526,7 @@ CPDF_Object* CPDF_NameTree::LookupValueAndName(size_t nIndex,
|
||||
}
|
||||
|
||||
CPDF_Object* CPDF_NameTree::LookupValue(const WideString& csName) const {
|
||||
- return SearchNameNodeByName(m_pRoot.Get(), csName, nullptr, nullptr);
|
||||
+ return SearchNameNodeByName(m_pRoot, csName, nullptr, nullptr);
|
||||
}
|
||||
|
||||
CPDF_Array* CPDF_NameTree::LookupNewStyleNamedDest(const ByteString& sName) {
|
||||
diff --git a/testing/resources/javascript/bug_1335681.in b/testing/resources/javascript/bug_1335681.in
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..254e5964518c40029fcde1e0f31f3e2703d9b85f
|
||||
--- /dev/null
|
||||
+++ b/testing/resources/javascript/bug_1335681.in
|
||||
@@ -0,0 +1,38 @@
|
||||
+{{header}}
|
||||
+{{object 1 0}} <<
|
||||
+ /Pages 1 0 R
|
||||
+ /OpenAction 2 0 R
|
||||
+ /Names <<
|
||||
+ /Dests 3 0 R
|
||||
+ >>
|
||||
+>>
|
||||
+endobj
|
||||
+{{object 2 0}} <<
|
||||
+ /Type /Action
|
||||
+ /S /JavaScript
|
||||
+ /JS (
|
||||
+ app.alert\("Starting"\);
|
||||
+ this.gotoNamedDest\(""\);
|
||||
+ )
|
||||
+>>
|
||||
+endobj
|
||||
+{{object 3 0}} <<
|
||||
+ /Kids 4 0 R
|
||||
+>>
|
||||
+endobj
|
||||
+{{object 4 0}} [
|
||||
+ << >>
|
||||
+ << >>
|
||||
+ <<
|
||||
+ /Kids [
|
||||
+ <<
|
||||
+ /Limits 4 0 R
|
||||
+ >>
|
||||
+ ]
|
||||
+ >>
|
||||
+]
|
||||
+endobj
|
||||
+{{xref}}
|
||||
+{{trailer}}
|
||||
+{{startxref}}
|
||||
+%%EOF
|
||||
diff --git a/testing/resources/javascript/bug_1335681_expected.txt b/testing/resources/javascript/bug_1335681_expected.txt
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..80a6951c49c8c8ef9a4e0036f7d4875c38373b89
|
||||
--- /dev/null
|
||||
+++ b/testing/resources/javascript/bug_1335681_expected.txt
|
||||
@@ -0,0 +1 @@
|
||||
+Alert: Starting
|
||||
@@ -8,3 +8,7 @@ fix_build_deprecated_attribute_for_older_msvc_versions.patch
|
||||
fix_disable_implies_dcheck_for_node_stream_array_buffers.patch
|
||||
revert_fix_cppgc_removed_deleted_cstors_in_cppheapcreateparams.patch
|
||||
cherry-pick-723ed8a9cfff.patch
|
||||
cherry-pick-44c4e56fea2c.patch
|
||||
version_10_2_154_10_cherry-pick.patch
|
||||
cherry-pick-13ffdf63a471.patch
|
||||
cherry-pick-8ea66a7833e2.patch
|
||||
|
||||
169
patches/v8/cherry-pick-13ffdf63a471.patch
Normal file
169
patches/v8/cherry-pick-13ffdf63a471.patch
Normal file
@@ -0,0 +1,169 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Tebbi <tebbi@chromium.org>
|
||||
Date: Fri, 17 Jun 2022 10:14:44 +0000
|
||||
Subject: Merged: [compiler] make CanCover() transitive
|
||||
|
||||
In addition to checking that a node is owned, CanCover() also needs to
|
||||
check if there are any side-effects in between the current node and
|
||||
the merged node. When merging inputs of inputs, this check was done
|
||||
with the wrong side-effect level of the in-between node.
|
||||
We partially fixed this before with `CanCoverTransitively`.
|
||||
This CL addresses the issue by always comparing to the side-effect
|
||||
level of the node from which we started, making `CanCoverTransitively`
|
||||
superfluous.
|
||||
|
||||
Bug: chromium:1336869
|
||||
(cherry picked from commit 6048f754931e0971cab58fb0de785482d175175b)
|
||||
|
||||
Change-Id: I63cf6bfdd40c2c55921db4a2ac737612bb7da4e4
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3722095
|
||||
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/10.2@{#19}
|
||||
Cr-Branched-From: 374091f382e88095694c1283cbdc2acddc1b1417-refs/heads/10.2.154@{#1}
|
||||
Cr-Branched-From: f0c353f6315eeb2212ba52478983a3b3af07b5b1-refs/heads/main@{#79976}
|
||||
|
||||
diff --git a/src/compiler/backend/instruction-selector.cc b/src/compiler/backend/instruction-selector.cc
|
||||
index 528aa543688041b816087efa02dec81ad8c9e3b1..b0e589738a0a1d7fb9d7f8eeb7677a5d7d0df022 100644
|
||||
--- a/src/compiler/backend/instruction-selector.cc
|
||||
+++ b/src/compiler/backend/instruction-selector.cc
|
||||
@@ -283,7 +283,7 @@ Instruction* InstructionSelector::Emit(Instruction* instr) {
|
||||
|
||||
bool InstructionSelector::CanCover(Node* user, Node* node) const {
|
||||
// 1. Both {user} and {node} must be in the same basic block.
|
||||
- if (schedule()->block(node) != schedule()->block(user)) {
|
||||
+ if (schedule()->block(node) != current_block_) {
|
||||
return false;
|
||||
}
|
||||
// 2. Pure {node}s must be owned by the {user}.
|
||||
@@ -291,7 +291,7 @@ bool InstructionSelector::CanCover(Node* user, Node* node) const {
|
||||
return node->OwnedBy(user);
|
||||
}
|
||||
// 3. Impure {node}s must match the effect level of {user}.
|
||||
- if (GetEffectLevel(node) != GetEffectLevel(user)) {
|
||||
+ if (GetEffectLevel(node) != current_effect_level_) {
|
||||
return false;
|
||||
}
|
||||
// 4. Only {node} must have value edges pointing to {user}.
|
||||
@@ -303,21 +303,6 @@ bool InstructionSelector::CanCover(Node* user, Node* node) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
-bool InstructionSelector::CanCoverTransitively(Node* user, Node* node,
|
||||
- Node* node_input) const {
|
||||
- if (CanCover(user, node) && CanCover(node, node_input)) {
|
||||
- // If {node} is pure, transitivity might not hold.
|
||||
- if (node->op()->HasProperty(Operator::kPure)) {
|
||||
- // If {node_input} is pure, the effect levels do not matter.
|
||||
- if (node_input->op()->HasProperty(Operator::kPure)) return true;
|
||||
- // Otherwise, {user} and {node_input} must have the same effect level.
|
||||
- return GetEffectLevel(user) == GetEffectLevel(node_input);
|
||||
- }
|
||||
- return true;
|
||||
- }
|
||||
- return false;
|
||||
-}
|
||||
-
|
||||
bool InstructionSelector::IsOnlyUserOfNodeInSameBlock(Node* user,
|
||||
Node* node) const {
|
||||
BasicBlock* bb_user = schedule()->block(user);
|
||||
@@ -1206,6 +1191,7 @@ void InstructionSelector::VisitBlock(BasicBlock* block) {
|
||||
int effect_level = 0;
|
||||
for (Node* const node : *block) {
|
||||
SetEffectLevel(node, effect_level);
|
||||
+ current_effect_level_ = effect_level;
|
||||
if (node->opcode() == IrOpcode::kStore ||
|
||||
node->opcode() == IrOpcode::kUnalignedStore ||
|
||||
node->opcode() == IrOpcode::kCall ||
|
||||
@@ -1223,6 +1209,7 @@ void InstructionSelector::VisitBlock(BasicBlock* block) {
|
||||
// control input should be on the same effect level as the last node.
|
||||
if (block->control_input() != nullptr) {
|
||||
SetEffectLevel(block->control_input(), effect_level);
|
||||
+ current_effect_level_ = effect_level;
|
||||
}
|
||||
|
||||
auto FinishEmittedInstructions = [&](Node* node, int instruction_start) {
|
||||
diff --git a/src/compiler/backend/instruction-selector.h b/src/compiler/backend/instruction-selector.h
|
||||
index b33de8e8569ebce59eebfec6eef12ed1f1874ddc..167cdc1f0c4962d55f3f9b40b7865737fc652bb0 100644
|
||||
--- a/src/compiler/backend/instruction-selector.h
|
||||
+++ b/src/compiler/backend/instruction-selector.h
|
||||
@@ -423,15 +423,12 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
|
||||
// Used in pattern matching during code generation.
|
||||
// Check if {node} can be covered while generating code for the current
|
||||
// instruction. A node can be covered if the {user} of the node has the only
|
||||
- // edge and the two are in the same basic block.
|
||||
- // Before fusing two instructions a and b, it is useful to check that
|
||||
- // CanCover(a, b) holds. If this is not the case, code for b must still be
|
||||
- // generated for other users, and fusing is unlikely to improve performance.
|
||||
+ // edge, the two are in the same basic block, and there are no side-effects
|
||||
+ // in-between. The last check is crucial for soundness.
|
||||
+ // For pure nodes, CanCover(a,b) is checked to avoid duplicated execution:
|
||||
+ // If this is not the case, code for b must still be generated for other
|
||||
+ // users, and fusing is unlikely to improve performance.
|
||||
bool CanCover(Node* user, Node* node) const;
|
||||
- // CanCover is not transitive. The counter example are Nodes A,B,C such that
|
||||
- // CanCover(A, B) and CanCover(B,C) and B is pure: The the effect level of A
|
||||
- // and B might differ. CanCoverTransitively does the additional checks.
|
||||
- bool CanCoverTransitively(Node* user, Node* node, Node* node_input) const;
|
||||
|
||||
// Used in pattern matching during code generation.
|
||||
// This function checks that {node} and {user} are in the same basic block,
|
||||
@@ -756,6 +753,7 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
|
||||
BoolVector defined_;
|
||||
BoolVector used_;
|
||||
IntVector effect_level_;
|
||||
+ int current_effect_level_;
|
||||
IntVector virtual_registers_;
|
||||
IntVector virtual_register_rename_;
|
||||
InstructionScheduler* scheduler_;
|
||||
diff --git a/src/compiler/backend/loong64/instruction-selector-loong64.cc b/src/compiler/backend/loong64/instruction-selector-loong64.cc
|
||||
index 4f03f99acd5b859b6fc095a34dea66ec058bcd4b..6b2d25f5dc1d912c1183e1b349bd29615024fa48 100644
|
||||
--- a/src/compiler/backend/loong64/instruction-selector-loong64.cc
|
||||
+++ b/src/compiler/backend/loong64/instruction-selector-loong64.cc
|
||||
@@ -1446,7 +1446,7 @@ void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
|
||||
if (CanCover(node, value)) {
|
||||
switch (value->opcode()) {
|
||||
case IrOpcode::kWord64Sar: {
|
||||
- if (CanCoverTransitively(node, value, value->InputAt(0)) &&
|
||||
+ if (CanCover(value, value->InputAt(0)) &&
|
||||
TryEmitExtendingLoad(this, value, node)) {
|
||||
return;
|
||||
} else {
|
||||
diff --git a/src/compiler/backend/mips64/instruction-selector-mips64.cc b/src/compiler/backend/mips64/instruction-selector-mips64.cc
|
||||
index 4f5738ddaddba03aaa0ebbc351fe18d766e04d00..1e29e0ed730be49f4a09484d6c4db3ffca92355e 100644
|
||||
--- a/src/compiler/backend/mips64/instruction-selector-mips64.cc
|
||||
+++ b/src/compiler/backend/mips64/instruction-selector-mips64.cc
|
||||
@@ -1532,7 +1532,7 @@ void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
|
||||
if (CanCover(node, value)) {
|
||||
switch (value->opcode()) {
|
||||
case IrOpcode::kWord64Sar: {
|
||||
- if (CanCoverTransitively(node, value, value->InputAt(0)) &&
|
||||
+ if (CanCover(value, value->InputAt(0)) &&
|
||||
TryEmitExtendingLoad(this, value, node)) {
|
||||
return;
|
||||
} else {
|
||||
diff --git a/src/compiler/backend/riscv64/instruction-selector-riscv64.cc b/src/compiler/backend/riscv64/instruction-selector-riscv64.cc
|
||||
index b6ced36f796b922df19e8bbb70db1bb6c33a2149..3b744795e5cd83ba4a8eff23cf4e607ffa0ca1c5 100644
|
||||
--- a/src/compiler/backend/riscv64/instruction-selector-riscv64.cc
|
||||
+++ b/src/compiler/backend/riscv64/instruction-selector-riscv64.cc
|
||||
@@ -1428,7 +1428,7 @@ void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
|
||||
if (CanCover(node, value)) {
|
||||
switch (value->opcode()) {
|
||||
case IrOpcode::kWord64Sar: {
|
||||
- if (CanCoverTransitively(node, value, value->InputAt(0)) &&
|
||||
+ if (CanCover(value, value->InputAt(0)) &&
|
||||
TryEmitExtendingLoad(this, value, node)) {
|
||||
return;
|
||||
} else {
|
||||
diff --git a/src/compiler/backend/x64/instruction-selector-x64.cc b/src/compiler/backend/x64/instruction-selector-x64.cc
|
||||
index 3aec65221fb03e0d4143d08f5156b8a3f192e82c..5b8064010419d2749cddc0302ba63dc20f6e58df 100644
|
||||
--- a/src/compiler/backend/x64/instruction-selector-x64.cc
|
||||
+++ b/src/compiler/backend/x64/instruction-selector-x64.cc
|
||||
@@ -1802,7 +1802,7 @@ void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
|
||||
case IrOpcode::kWord64Shr: {
|
||||
Int64BinopMatcher m(value);
|
||||
if (m.right().Is(32)) {
|
||||
- if (CanCoverTransitively(node, value, value->InputAt(0)) &&
|
||||
+ if (CanCover(value, value->InputAt(0)) &&
|
||||
TryMatchLoadWord64AndShiftRight(this, value, kX64Movl)) {
|
||||
return EmitIdentity(node);
|
||||
}
|
||||
185
patches/v8/cherry-pick-44c4e56fea2c.patch
Normal file
185
patches/v8/cherry-pick-44c4e56fea2c.patch
Normal file
@@ -0,0 +1,185 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shu-yu Guo <syg@chromium.org>
|
||||
Date: Tue, 3 May 2022 13:26:32 -0700
|
||||
Subject: Merged: [weakrefs] Set unregister_token to undefined when
|
||||
unregistering
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
(cherry picked from commit dd3289d7945dac855d1287cf4ea248883e908d54)
|
||||
|
||||
Bug: chromium:1321078
|
||||
Change-Id: Ic7537cc5101b35018911c27a81e9b0e0a7da154b
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3695262
|
||||
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
|
||||
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/10.2@{#16}
|
||||
Cr-Branched-From: 374091f382e88095694c1283cbdc2acddc1b1417-refs/heads/10.2.154@{#1}
|
||||
Cr-Branched-From: f0c353f6315eeb2212ba52478983a3b3af07b5b1-refs/heads/main@{#79976}
|
||||
|
||||
diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc
|
||||
index f1d6755161be21a76d084f2ccb8f73d6976d70f7..6999d52f2d38b1eb937e5fb46d26a90d4bfe6c0f 100644
|
||||
--- a/src/heap/mark-compact.cc
|
||||
+++ b/src/heap/mark-compact.cc
|
||||
@@ -3003,14 +3003,11 @@ void MarkCompactCollector::ClearJSWeakRefs() {
|
||||
// unregister_token field set to undefined when processing the first
|
||||
// WeakCell. Like above, we're modifying pointers during GC, so record the
|
||||
// slots.
|
||||
- HeapObject undefined = ReadOnlyRoots(isolate()).undefined_value();
|
||||
JSFinalizationRegistry finalization_registry =
|
||||
JSFinalizationRegistry::cast(weak_cell.finalization_registry());
|
||||
finalization_registry.RemoveUnregisterToken(
|
||||
JSReceiver::cast(unregister_token), isolate(),
|
||||
- [undefined](WeakCell matched_cell) {
|
||||
- matched_cell.set_unregister_token(undefined);
|
||||
- },
|
||||
+ JSFinalizationRegistry::kKeepMatchedCellsInRegistry,
|
||||
gc_notify_updated_slot);
|
||||
} else {
|
||||
// The unregister_token is alive.
|
||||
diff --git a/src/objects/js-weak-refs-inl.h b/src/objects/js-weak-refs-inl.h
|
||||
index acce7b72b9430d810b538d6d082e9b57b2a04b75..76e6e075e5ded6bbbf84605c8672b741757eb816 100644
|
||||
--- a/src/objects/js-weak-refs-inl.h
|
||||
+++ b/src/objects/js-weak-refs-inl.h
|
||||
@@ -60,16 +60,14 @@ bool JSFinalizationRegistry::Unregister(
|
||||
// key. Each WeakCell will be in the "active_cells" or "cleared_cells" list of
|
||||
// its FinalizationRegistry; remove it from there.
|
||||
return finalization_registry->RemoveUnregisterToken(
|
||||
- *unregister_token, isolate,
|
||||
- [isolate](WeakCell matched_cell) {
|
||||
- matched_cell.RemoveFromFinalizationRegistryCells(isolate);
|
||||
- },
|
||||
+ *unregister_token, isolate, kRemoveMatchedCellsFromRegistry,
|
||||
[](HeapObject, ObjectSlot, Object) {});
|
||||
}
|
||||
|
||||
-template <typename MatchCallback, typename GCNotifyUpdatedSlotCallback>
|
||||
+template <typename GCNotifyUpdatedSlotCallback>
|
||||
bool JSFinalizationRegistry::RemoveUnregisterToken(
|
||||
- JSReceiver unregister_token, Isolate* isolate, MatchCallback match_callback,
|
||||
+ JSReceiver unregister_token, Isolate* isolate,
|
||||
+ RemoveUnregisterTokenMode removal_mode,
|
||||
GCNotifyUpdatedSlotCallback gc_notify_updated_slot) {
|
||||
// This method is called from both FinalizationRegistry#unregister and for
|
||||
// removing weakly-held dead unregister tokens. The latter is during GC so
|
||||
@@ -107,7 +105,16 @@ bool JSFinalizationRegistry::RemoveUnregisterToken(
|
||||
value = weak_cell.key_list_next();
|
||||
if (weak_cell.unregister_token() == unregister_token) {
|
||||
// weak_cell has the same unregister token; remove it from the key list.
|
||||
- match_callback(weak_cell);
|
||||
+ switch (removal_mode) {
|
||||
+ case kRemoveMatchedCellsFromRegistry:
|
||||
+ weak_cell.RemoveFromFinalizationRegistryCells(isolate);
|
||||
+ break;
|
||||
+ case kKeepMatchedCellsInRegistry:
|
||||
+ // Do nothing.
|
||||
+ break;
|
||||
+ }
|
||||
+ // Clear unregister token-related fields.
|
||||
+ weak_cell.set_unregister_token(undefined);
|
||||
weak_cell.set_key_list_prev(undefined);
|
||||
weak_cell.set_key_list_next(undefined);
|
||||
was_present = true;
|
||||
diff --git a/src/objects/js-weak-refs.h b/src/objects/js-weak-refs.h
|
||||
index 57f765b282e653122fd83e827bdd52cb9fe818cf..f678234ff81afc7a32c93bb275edd39fbe0b3891 100644
|
||||
--- a/src/objects/js-weak-refs.h
|
||||
+++ b/src/objects/js-weak-refs.h
|
||||
@@ -43,10 +43,14 @@ class JSFinalizationRegistry
|
||||
// it modifies slots in key_map and WeakCells and the normal write barrier is
|
||||
// disabled during GC, we need to tell the GC about the modified slots via the
|
||||
// gc_notify_updated_slot function.
|
||||
- template <typename MatchCallback, typename GCNotifyUpdatedSlotCallback>
|
||||
+ enum RemoveUnregisterTokenMode {
|
||||
+ kRemoveMatchedCellsFromRegistry,
|
||||
+ kKeepMatchedCellsInRegistry
|
||||
+ };
|
||||
+ template <typename GCNotifyUpdatedSlotCallback>
|
||||
inline bool RemoveUnregisterToken(
|
||||
JSReceiver unregister_token, Isolate* isolate,
|
||||
- MatchCallback match_callback,
|
||||
+ RemoveUnregisterTokenMode removal_mode,
|
||||
GCNotifyUpdatedSlotCallback gc_notify_updated_slot);
|
||||
|
||||
// Returns true if the cleared_cells list is non-empty.
|
||||
diff --git a/src/objects/objects.cc b/src/objects/objects.cc
|
||||
index c2c9fe36c57603425dddbf36823489c59bcc4868..0908eb76b3610e6bdb5e7e816773a53d180f3ecf 100644
|
||||
--- a/src/objects/objects.cc
|
||||
+++ b/src/objects/objects.cc
|
||||
@@ -6936,7 +6936,7 @@ void JSFinalizationRegistry::RemoveCellFromUnregisterTokenMap(
|
||||
}
|
||||
|
||||
// weak_cell is now removed from the unregister token map, so clear its
|
||||
- // unregister token-related fields for heap verification.
|
||||
+ // unregister token-related fields.
|
||||
weak_cell.set_unregister_token(undefined);
|
||||
weak_cell.set_key_list_prev(undefined);
|
||||
weak_cell.set_key_list_next(undefined);
|
||||
diff --git a/test/cctest/test-js-weak-refs.cc b/test/cctest/test-js-weak-refs.cc
|
||||
index 8974bdf6dba65be319afbfdfc66641d27ed14a07..eb6ca2d73685b437978fac642190c8d653f4c73a 100644
|
||||
--- a/test/cctest/test-js-weak-refs.cc
|
||||
+++ b/test/cctest/test-js-weak-refs.cc
|
||||
@@ -853,9 +853,7 @@ TEST(TestRemoveUnregisterToken) {
|
||||
|
||||
finalization_registry->RemoveUnregisterToken(
|
||||
JSReceiver::cast(*token2), isolate,
|
||||
- [undefined](WeakCell matched_cell) {
|
||||
- matched_cell.set_unregister_token(*undefined);
|
||||
- },
|
||||
+ JSFinalizationRegistry::kKeepMatchedCellsInRegistry,
|
||||
[](HeapObject, ObjectSlot, Object) {});
|
||||
|
||||
// Both weak_cell2a and weak_cell2b remain on the weak cell chains.
|
||||
@@ -1024,5 +1022,52 @@ TEST(UnregisterTokenHeapVerifier) {
|
||||
EmptyMessageQueues(isolate);
|
||||
}
|
||||
|
||||
+TEST(UnregisteredAndUnclearedCellHeapVerifier) {
|
||||
+ if (!FLAG_incremental_marking) return;
|
||||
+ ManualGCScope manual_gc_scope;
|
||||
+#ifdef VERIFY_HEAP
|
||||
+ FLAG_verify_heap = true;
|
||||
+#endif
|
||||
+
|
||||
+ CcTest::InitializeVM();
|
||||
+ v8::Isolate* isolate = CcTest::isolate();
|
||||
+ Heap* heap = CcTest::heap();
|
||||
+ v8::HandleScope outer_scope(isolate);
|
||||
+
|
||||
+ {
|
||||
+ // Make a new FinalizationRegistry and register an object with a token.
|
||||
+ v8::HandleScope scope(isolate);
|
||||
+ CompileRun(
|
||||
+ "var token = {}; "
|
||||
+ "var registry = new FinalizationRegistry(function () {}); "
|
||||
+ "registry.register({}, undefined, token);");
|
||||
+ }
|
||||
+
|
||||
+ // Start incremental marking to activate the marking barrier.
|
||||
+ heap::SimulateIncrementalMarking(heap, false);
|
||||
+
|
||||
+ {
|
||||
+ // Make a WeakCell list with length >1, then unregister with the token to
|
||||
+ // the WeakCell from the registry. The linked list manipulation keeps the
|
||||
+ // unregistered WeakCell alive (i.e. not put into cleared_cells) due to the
|
||||
+ // marking barrier from incremental marking. Then make the original token
|
||||
+ // collectible.
|
||||
+ v8::HandleScope scope(isolate);
|
||||
+ CompileRun(
|
||||
+ "registry.register({}); "
|
||||
+ "registry.unregister(token); "
|
||||
+ "token = 0;");
|
||||
+ }
|
||||
+
|
||||
+ // Trigger GC.
|
||||
+ CcTest::CollectAllGarbage();
|
||||
+ CcTest::CollectAllGarbage();
|
||||
+
|
||||
+ // Pump message loop to run the finalizer task, then the incremental marking
|
||||
+ // task. The verifier will verify that live WeakCells don't point to dead
|
||||
+ // unregister tokens.
|
||||
+ EmptyMessageQueues(isolate);
|
||||
+}
|
||||
+
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
43
patches/v8/cherry-pick-8ea66a7833e2.patch
Normal file
43
patches/v8/cherry-pick-8ea66a7833e2.patch
Normal file
@@ -0,0 +1,43 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Tebbi <tebbi@chromium.org>
|
||||
Date: Wed, 29 Jun 2022 10:18:59 +0000
|
||||
Subject: Merged: [compiler] fix FrameState revisit bug in escape analysis
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
(cherry picked from commit 17da9e70833014e0a2646db5c11588f0aee02de7)
|
||||
|
||||
Bug: chromium:1340335, chromium:1315901
|
||||
Change-Id: I81cdc6bc3d6c7441ebc333d33801329c05fbd5d4
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3755103
|
||||
Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
|
||||
Commit-Queue: Samuel Groß <saelo@chromium.org>
|
||||
Cr-Commit-Position: refs/branch-heads/10.2@{#25}
|
||||
Cr-Branched-From: 374091f382e88095694c1283cbdc2acddc1b1417-refs/heads/10.2.154@{#1}
|
||||
Cr-Branched-From: f0c353f6315eeb2212ba52478983a3b3af07b5b1-refs/heads/main@{#79976}
|
||||
|
||||
diff --git a/src/compiler/escape-analysis.cc b/src/compiler/escape-analysis.cc
|
||||
index 1ee9100ecd78757e8873866dd9da05231b97439b..42c5819fa1fa9ab00a0b9573d6c13cbcf66e251b 100644
|
||||
--- a/src/compiler/escape-analysis.cc
|
||||
+++ b/src/compiler/escape-analysis.cc
|
||||
@@ -78,6 +78,8 @@ class ReduceScope {
|
||||
explicit ReduceScope(Node* node, Reduction* reduction)
|
||||
: current_node_(node), reduction_(reduction) {}
|
||||
|
||||
+ void SetValueChanged() { reduction()->set_value_changed(); }
|
||||
+
|
||||
protected:
|
||||
Node* current_node() const { return current_node_; }
|
||||
Reduction* reduction() { return reduction_; }
|
||||
@@ -806,7 +808,9 @@ void ReduceNode(const Operator* op, EscapeAnalysisTracker::Scope* current,
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kStateValues:
|
||||
- // These uses are always safe.
|
||||
+ // We visit StateValue nodes through their correpsonding FrameState node,
|
||||
+ // so we need to make sure we revisit the FrameState.
|
||||
+ current->SetValueChanged();
|
||||
break;
|
||||
case IrOpcode::kFrameState: {
|
||||
// We mark the receiver as escaping due to the non-standard `.getThis`
|
||||
53
patches/v8/version_10_2_154_10_cherry-pick.patch
Normal file
53
patches/v8/version_10_2_154_10_cherry-pick.patch
Normal file
@@ -0,0 +1,53 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Tobias Tebbi <tebbi@chromium.org>
|
||||
Date: Fri, 10 Jun 2022 12:53:10 +0000
|
||||
Subject: Version 10.2.154.10 (cherry-pick)
|
||||
|
||||
Merged 85b4b5d719c50ecc8f376ca853c9001d5ac0ed9a
|
||||
|
||||
[compiler] only enable MidTierRegisterAllocator for Wasm
|
||||
|
||||
Bug: chromium:1335054
|
||||
Change-Id: I61ab97d4fbfcbb81319e611a64a6454e050a1d65
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3705397
|
||||
Owners-Override: Tobias Tebbi <tebbi@chromium.org>
|
||||
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
|
||||
Cr-Commit-Position: refs/branch-heads/10.2@{#18}
|
||||
Cr-Branched-From: 374091f382e88095694c1283cbdc2acddc1b1417-refs/heads/10.2.154@{#1}
|
||||
Cr-Branched-From: f0c353f6315eeb2212ba52478983a3b3af07b5b1-refs/heads/main@{#79976}
|
||||
|
||||
diff --git a/include/v8-version.h b/include/v8-version.h
|
||||
index 7999dcb13f34b68d12660cdc0f6767e5e8bf6d18..ebd8289678fcf2d746e3a67a49c086058651fb28 100644
|
||||
--- a/include/v8-version.h
|
||||
+++ b/include/v8-version.h
|
||||
@@ -11,7 +11,7 @@
|
||||
#define V8_MAJOR_VERSION 10
|
||||
#define V8_MINOR_VERSION 0
|
||||
#define V8_BUILD_NUMBER 139
|
||||
-#define V8_PATCH_LEVEL 17
|
||||
+#define V8_PATCH_LEVEL 18
|
||||
|
||||
// Use 1 for candidates and 0 otherwise.
|
||||
// (Boolean macro values are not supported by all preprocessors.)
|
||||
diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc
|
||||
index 6ea2df76c863ed0466ee96fcf1ad5736192c6b44..555942c2be2287d41d443d109e305c4858579447 100644
|
||||
--- a/src/compiler/pipeline.cc
|
||||
+++ b/src/compiler/pipeline.cc
|
||||
@@ -3456,11 +3456,15 @@ bool PipelineImpl::SelectInstructions(Linkage* linkage) {
|
||||
|
||||
const RegisterConfiguration* config = RegisterConfiguration::Default();
|
||||
std::unique_ptr<const RegisterConfiguration> restricted_config;
|
||||
+ // The mid-tier register allocator keeps values in stack slots for too long.
|
||||
+ // This is incompatible with left-trimming, therefore we cannot enable it for
|
||||
+ // JS functions.
|
||||
bool use_mid_tier_register_allocator =
|
||||
- FLAG_turbo_force_mid_tier_regalloc ||
|
||||
+ data->info()->code_kind() == CodeKind::WASM_FUNCTION &&
|
||||
+ (FLAG_turbo_force_mid_tier_regalloc ||
|
||||
(FLAG_turbo_use_mid_tier_regalloc_for_huge_functions &&
|
||||
data->sequence()->VirtualRegisterCount() >
|
||||
- kTopTierVirtualRegistersLimit);
|
||||
+ kTopTierVirtualRegistersLimit));
|
||||
|
||||
if (call_descriptor->HasRestrictedAllocatableRegisters()) {
|
||||
RegList registers = call_descriptor->AllocatableRegisters();
|
||||
@@ -1,2 +1,4 @@
|
||||
add_thread_local_to_x_error_trap_cc.patch
|
||||
cherry-pick-a18fddcb53e6.patch
|
||||
cherry-pick-763d847f1e5a.patch
|
||||
cherry-pick-06aea31d10f8.patch
|
||||
|
||||
38
patches/webrtc/cherry-pick-06aea31d10f8.patch
Normal file
38
patches/webrtc/cherry-pick-06aea31d10f8.patch
Normal file
@@ -0,0 +1,38 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Henrik=20Bostr=C3=B6m?= <hbos@webrtc.org>
|
||||
Date: Wed, 13 Jul 2022 11:10:14 +0200
|
||||
Subject: Disallow invalid arguments in RestoreEncodingLayers.
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Changing DCHECK into CHECK for good measure.
|
||||
|
||||
(cherry picked from commit 2b1f509f3a05035a17917061a71b16114e8c72dc)
|
||||
|
||||
No-Try: True
|
||||
Bug: chromium:1343889
|
||||
Change-Id: I2cede85dc2d2a4238739f73afe25275047f4aa50
|
||||
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/268460
|
||||
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
|
||||
Commit-Queue: Henrik Boström <hbos@webrtc.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#37511}
|
||||
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/269242
|
||||
Cr-Commit-Position: refs/branch-heads/5112@{#8}
|
||||
Cr-Branched-From: a976a871159f85f975fbd6f170d0a8f00a4aee49-refs/heads/main@{#37168}
|
||||
|
||||
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc
|
||||
index d1355e31ab6331058cd557b0f164c903c9bc8f46..c41904ac8395c874ea327cab9c800d9311da8dd8 100644
|
||||
--- a/pc/rtp_sender.cc
|
||||
+++ b/pc/rtp_sender.cc
|
||||
@@ -74,8 +74,8 @@ RtpParameters RestoreEncodingLayers(
|
||||
const RtpParameters& parameters,
|
||||
const std::vector<std::string>& removed_rids,
|
||||
const std::vector<RtpEncodingParameters>& all_layers) {
|
||||
- RTC_DCHECK_EQ(parameters.encodings.size() + removed_rids.size(),
|
||||
- all_layers.size());
|
||||
+ RTC_CHECK_EQ(parameters.encodings.size() + removed_rids.size(),
|
||||
+ all_layers.size());
|
||||
RtpParameters result(parameters);
|
||||
result.encodings.clear();
|
||||
size_t index = 0;
|
||||
93
patches/webrtc/cherry-pick-763d847f1e5a.patch
Normal file
93
patches/webrtc/cherry-pick-763d847f1e5a.patch
Normal file
@@ -0,0 +1,93 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Taylor Brandstetter <deadbeef@webrtc.org>
|
||||
Date: Fri, 1 Jul 2022 12:20:05 -0700
|
||||
Subject: Do not allow simulcast to be turned off using SDP munging
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This is an error that puts the PC into an inconsistent state, so
|
||||
causing a crash is the right thing to do.
|
||||
|
||||
(cherry picked from commit 3fe8b0d9a980642ee5ebb1f9e429378b063c1f07)
|
||||
TBR=hta@webrtc.org
|
||||
|
||||
Bug: chromium:1341043
|
||||
Change-Id: Ie1eb89400ad87f0c83634b7073236b07e92ec7ab
|
||||
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/267281
|
||||
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
|
||||
Reviewed-by: Henrik Boström <hbos@webrtc.org>
|
||||
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
|
||||
Cr-Original-Commit-Position: refs/heads/main@{#37391}
|
||||
No-Presubmit: true
|
||||
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/267427
|
||||
Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org>
|
||||
Reviewed-by: Guido Urdaneta <guidou@webrtc.org>
|
||||
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
|
||||
Cr-Commit-Position: refs/branch-heads/5005@{#9}
|
||||
Cr-Branched-From: 8f9b44ba38c134e1c69126196e4d9e566ca5d5e3-refs/heads/main@{#36552}
|
||||
|
||||
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc
|
||||
index 110b5aae0a01a5d612eefdc3262b20e4dd5504fe..d1355e31ab6331058cd557b0f164c903c9bc8f46 100644
|
||||
--- a/pc/rtp_sender.cc
|
||||
+++ b/pc/rtp_sender.cc
|
||||
@@ -299,8 +299,8 @@ void RtpSenderBase::SetSsrc(uint32_t ssrc) {
|
||||
// we need to copy.
|
||||
RtpParameters current_parameters =
|
||||
media_channel_->GetRtpSendParameters(ssrc_);
|
||||
- RTC_DCHECK_GE(current_parameters.encodings.size(),
|
||||
- init_parameters_.encodings.size());
|
||||
+ RTC_CHECK_GE(current_parameters.encodings.size(),
|
||||
+ init_parameters_.encodings.size());
|
||||
for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) {
|
||||
init_parameters_.encodings[i].ssrc =
|
||||
current_parameters.encodings[i].ssrc;
|
||||
diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc
|
||||
index 2c6bc7b71a1e5e30609f382fee0b4cc30f03512e..388edbaf73f0570d895b4855bc2207afb8eff1a5 100644
|
||||
--- a/pc/rtp_sender_receiver_unittest.cc
|
||||
+++ b/pc/rtp_sender_receiver_unittest.cc
|
||||
@@ -1157,6 +1157,44 @@ TEST_F(RtpSenderReceiverTest,
|
||||
DestroyVideoRtpSender();
|
||||
}
|
||||
|
||||
+#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
|
||||
+using RtpSenderReceiverDeathTest = RtpSenderReceiverTest;
|
||||
+
|
||||
+TEST_F(RtpSenderReceiverDeathTest,
|
||||
+ VideoSenderManualRemoveSimulcastFailsDeathTest) {
|
||||
+ AddVideoTrack(false);
|
||||
+
|
||||
+ std::unique_ptr<MockSetStreamsObserver> set_streams_observer =
|
||||
+ std::make_unique<MockSetStreamsObserver>();
|
||||
+ video_rtp_sender_ = VideoRtpSender::Create(worker_thread_, video_track_->id(),
|
||||
+ set_streams_observer.get());
|
||||
+ ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get()));
|
||||
+ EXPECT_CALL(*set_streams_observer, OnSetStreams());
|
||||
+ video_rtp_sender_->SetStreams({local_stream_->id()});
|
||||
+
|
||||
+ std::vector<RtpEncodingParameters> init_encodings(2);
|
||||
+ init_encodings[0].max_bitrate_bps = 60000;
|
||||
+ init_encodings[1].max_bitrate_bps = 120000;
|
||||
+ video_rtp_sender_->set_init_send_encodings(init_encodings);
|
||||
+
|
||||
+ RtpParameters params = video_rtp_sender_->GetParameters();
|
||||
+ ASSERT_EQ(2u, params.encodings.size());
|
||||
+ EXPECT_EQ(params.encodings[0].max_bitrate_bps, 60000);
|
||||
+
|
||||
+ // Simulate the setLocalDescription call as if the user used SDP munging
|
||||
+ // to disable simulcast.
|
||||
+ std::vector<uint32_t> ssrcs;
|
||||
+ ssrcs.reserve(2);
|
||||
+ for (int i = 0; i < 2; ++i)
|
||||
+ ssrcs.push_back(kVideoSsrcSimulcast + i);
|
||||
+ cricket::StreamParams stream_params =
|
||||
+ cricket::StreamParams::CreateLegacy(kVideoSsrc);
|
||||
+ video_media_channel_->AddSendStream(stream_params);
|
||||
+ video_rtp_sender_->SetMediaChannel(video_media_channel_);
|
||||
+ EXPECT_DEATH(video_rtp_sender_->SetSsrc(kVideoSsrcSimulcast), "");
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
TEST_F(RtpSenderReceiverTest,
|
||||
VideoSenderMustCallGetParametersBeforeSetParametersBeforeNegotiation) {
|
||||
video_rtp_sender_ =
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user