mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b05ccd812e | ||
|
|
1bce5860e8 | ||
|
|
f93dc38e84 | ||
|
|
bf7741ab53 | ||
|
|
a0ea6795ca | ||
|
|
c48905aee6 | ||
|
|
4edf23f86d | ||
|
|
eeb3f505e6 | ||
|
|
3b683c1181 | ||
|
|
db6c2274d9 | ||
|
|
fb6de2d52b | ||
|
|
4b483799cb | ||
|
|
a5cafd174d | ||
|
|
d52ce309dc | ||
|
|
e5bf0d3c11 | ||
|
|
67a40dd5c6 | ||
|
|
0068195d56 | ||
|
|
fe5b3d2b80 | ||
|
|
eee147b483 | ||
|
|
21fcf51000 | ||
|
|
a07f56bf84 | ||
|
|
a90abbddbb | ||
|
|
4fa6f61f8c | ||
|
|
57a03c4d2a | ||
|
|
205cf24802 | ||
|
|
7e1099a8e4 | ||
|
|
438347d891 | ||
|
|
c005d4ff56 | ||
|
|
729b0f5508 | ||
|
|
ea21d940ec | ||
|
|
9df18f3fcf | ||
|
|
f724a9ca2f | ||
|
|
f3f1171a09 | ||
|
|
b7f68027a7 | ||
|
|
a72b6fb7a9 | ||
|
|
64443a7487 | ||
|
|
40fad5221b | ||
|
|
3c89c28e5c | ||
|
|
ef88af2703 | ||
|
|
586f84f957 | ||
|
|
98ccd203bc | ||
|
|
b1e5af755c | ||
|
|
a388f977a3 | ||
|
|
97ca81b1b5 | ||
|
|
d95d88d25d | ||
|
|
b1b44e0469 | ||
|
|
f56cb78c89 | ||
|
|
c67ca40ed6 | ||
|
|
e702a8ef3b | ||
|
|
54fa16a3df | ||
|
|
af4f598e81 | ||
|
|
b9da235ec6 | ||
|
|
2f4e7679f8 | ||
|
|
50d45d07ac | ||
|
|
39df0bdb74 | ||
|
|
8fa0cbe27a | ||
|
|
addf23c579 | ||
|
|
6b67219dbf | ||
|
|
2298908a1f | ||
|
|
b8af801f82 | ||
|
|
c38f15eb03 | ||
|
|
6d65180819 | ||
|
|
6ba162de48 | ||
|
|
e9e2b6dfda | ||
|
|
88632cbdc7 | ||
|
|
d6b2e757f8 | ||
|
|
b91ab0ebf8 | ||
|
|
c0588770ea | ||
|
|
2a9a71af29 | ||
|
|
7073603150 | ||
|
|
82bb684765 | ||
|
|
36169d15da | ||
|
|
5174419819 | ||
|
|
64fc21fec9 | ||
|
|
74df9ad42f | ||
|
|
12f4be7fba | ||
|
|
9b8cd7cc53 | ||
|
|
59a3898ba8 | ||
|
|
6cb2b75fbb | ||
|
|
0764bb1560 | ||
|
|
5dd4b6aaed | ||
|
|
4e97448f70 | ||
|
|
92528220db | ||
|
|
108d1f9a29 | ||
|
|
4dedbadcb5 | ||
|
|
ea2bea7382 | ||
|
|
3cf901e45b | ||
|
|
e87d17b728 | ||
|
|
def1ec7f99 | ||
|
|
85f6bffee0 | ||
|
|
94632e9703 | ||
|
|
7acd622750 | ||
|
|
c050839202 | ||
|
|
c0555c1668 | ||
|
|
07344857d6 | ||
|
|
f57ca1174c | ||
|
|
a189d3dde6 | ||
|
|
a49f0f7318 |
@@ -53,14 +53,8 @@ executors:
|
||||
description: "macOS executor size"
|
||||
type: enum
|
||||
enum: ["macos.x86.medium.gen2", "large"]
|
||||
xcode:
|
||||
description: "xcode version"
|
||||
default: 13.3.0
|
||||
type: enum
|
||||
enum: ["12.4.0", "13.3.0"]
|
||||
|
||||
macos:
|
||||
xcode: << parameters.xcode >>
|
||||
xcode: 13.3.0
|
||||
resource_class: << parameters.size >>
|
||||
|
||||
# Electron Runners
|
||||
@@ -125,6 +119,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
|
||||
@@ -349,7 +346,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:
|
||||
@@ -511,6 +508,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
|
||||
@@ -527,15 +525,6 @@ step-install-gnutar-on-mac: &step-install-gnutar-on-mac
|
||||
ln -fs /usr/local/bin/gtar /usr/local/bin/tar
|
||||
fi
|
||||
|
||||
step-install-python2-on-mac: &step-install-python2-on-mac
|
||||
run:
|
||||
name: Install python2 on macos
|
||||
command: |
|
||||
if [ "`uname`" == "Darwin" ]; then
|
||||
curl -O https://www.python.org/ftp/python/2.7.18/python-2.7.18-macosx10.9.pkg
|
||||
sudo installer -pkg python-2.7.18-macosx10.9.pkg -target /
|
||||
fi
|
||||
|
||||
step-gn-gen-default: &step-gn-gen-default
|
||||
run:
|
||||
name: Default GN gen
|
||||
@@ -558,7 +547,7 @@ step-gn-check: &step-gn-check
|
||||
step-electron-build: &step-electron-build
|
||||
run:
|
||||
name: Electron build
|
||||
no_output_timeout: 30m
|
||||
no_output_timeout: 60m
|
||||
command: |
|
||||
# On arm platforms we generate a cross-arch ffmpeg that ninja does not seem
|
||||
# to realize is not correct / should be rebuilt. We delete it here so it is
|
||||
@@ -1024,7 +1013,7 @@ steps-electron-gn-check: &steps-electron-gn-check
|
||||
- *step-checkout-electron
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-install-python2-on-mac
|
||||
- install-python2-mac
|
||||
- *step-setup-env-for-build
|
||||
- *step-setup-goma-for-build
|
||||
- *step-generate-deps-hash
|
||||
@@ -1048,7 +1037,7 @@ steps-native-tests: &steps-native-tests
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-install-python2-on-mac
|
||||
- install-python2-mac
|
||||
- *step-setup-env-for-build
|
||||
- *step-setup-goma-for-build
|
||||
- *step-wait-for-goma
|
||||
@@ -1101,6 +1090,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 +1180,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:
|
||||
@@ -1356,7 +1371,7 @@ commands:
|
||||
- run: rm -rf src/electron
|
||||
- *step-restore-brew-cache
|
||||
- *step-install-gnutar-on-mac
|
||||
- *step-install-python2-on-mac
|
||||
- install-python2-mac
|
||||
- *step-save-brew-cache
|
||||
- when:
|
||||
condition: << parameters.build >>
|
||||
@@ -1548,7 +1563,7 @@ commands:
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-restore-brew-cache
|
||||
- *step-install-python2-on-mac
|
||||
- install-python2-mac
|
||||
- *step-get-more-space-on-mac
|
||||
- when:
|
||||
condition: << parameters.checkout >>
|
||||
@@ -2098,7 +2113,6 @@ jobs:
|
||||
osx-testing-x64-tests:
|
||||
executor:
|
||||
name: macos
|
||||
xcode: 12.4.0
|
||||
size: macos.x86.medium.gen2
|
||||
environment:
|
||||
<<: *env-mac-large
|
||||
@@ -2112,12 +2126,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
|
||||
@@ -2131,6 +2145,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",
|
||||
|
||||
2
DEPS
2
DEPS
@@ -2,7 +2,7 @@ gclient_gn_args_from = 'src'
|
||||
|
||||
vars = {
|
||||
'chromium_version':
|
||||
'102.0.5005.63',
|
||||
'102.0.5005.167',
|
||||
'node_version':
|
||||
'v16.14.2',
|
||||
'nan_version':
|
||||
|
||||
@@ -1 +1 @@
|
||||
19.0.2
|
||||
19.0.12
|
||||
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]) {
|
||||
@@ -238,7 +228,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:
|
||||
- cd ..
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -69,9 +69,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
|
||||
|
||||
@@ -1025,7 +1025,7 @@ is emitted.
|
||||
|
||||
#### `ses.getStoragePath()`
|
||||
|
||||
A `string | null` indicating the absolute file system path where data for this
|
||||
Returns `string | null` - The absolute file system path where data for this
|
||||
session is persisted on disk. For in memory sessions this returns `null`.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
@@ -25,15 +25,20 @@ app.whenReady().then(() => {
|
||||
})
|
||||
```
|
||||
|
||||
__Platform limitations:__
|
||||
__Platform Considerations__
|
||||
|
||||
If you want to keep exact same behaviors on all platforms, you should not
|
||||
rely on the `click` event; instead, always attach a context menu to the tray icon.
|
||||
|
||||
__Linux__
|
||||
|
||||
* On Linux the app indicator will be used if it is supported, otherwise
|
||||
`GtkStatusIcon` will be used instead.
|
||||
* On Linux distributions that only have app indicator support, you have to
|
||||
install `libappindicator1` to make the tray icon work.
|
||||
* The app indicator will be used if it is supported, otherwise
|
||||
`GtkStatusIcon` will be used instead.
|
||||
* App indicator will only be shown when it has a context menu.
|
||||
* When app indicator is used on Linux, the `click` event is ignored.
|
||||
* On Linux in order for changes made to individual `MenuItem`s to take effect,
|
||||
* The `click` event is ignored when using the app indicator.
|
||||
* In order for changes made to individual `MenuItem`s to take effect,
|
||||
you have to call `setContextMenu` again. For example:
|
||||
|
||||
```javascript
|
||||
@@ -55,10 +60,16 @@ app.whenReady().then(() => {
|
||||
})
|
||||
```
|
||||
|
||||
* On Windows it is recommended to use `ICO` icons to get best visual effects.
|
||||
__MacOS__
|
||||
|
||||
If you want to keep exact same behaviors on all platforms, you should not
|
||||
rely on the `click` event and always attach a context menu to the tray icon.
|
||||
* Icons passed to the Tray constructor should be [Template Images](native-image.md#template-image).
|
||||
* To make sure your icon isn't grainy on retina monitors, be sure your `@2x` image is 144dpi.
|
||||
* If you are bundling your application (e.g., with webpack for development), be sure that the file names are not being mangled or hashed. The filename needs to end in Template, and the `@2x` image needs to have the same filename as the standard image, or MacOS will not magically invert your image's colors or use the high density image.
|
||||
* 16x16 (72dpi) and 32x32@2x (144dpi) work well for most icons.
|
||||
|
||||
__Windows__
|
||||
|
||||
* It is recommended to use `ICO` icons to get best visual effects.
|
||||
|
||||
### `new Tray(image, [guid])`
|
||||
|
||||
|
||||
@@ -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")'
|
||||
|
||||
@@ -36,7 +36,10 @@ requires unsafe mode), so Electron is unable to support this feature on Linux.
|
||||
|
||||
## Planned Breaking API Changes (19.0)
|
||||
|
||||
None
|
||||
### Removed: IA32 Linux binaries
|
||||
|
||||
This is a result of Chromium 102.0.4999.0 dropping support for IA32 Linux.
|
||||
This concludes the [removal of support for IA32 Linux](#removed-ia32-linux-support).
|
||||
|
||||
## Planned Breaking API Changes (18.0)
|
||||
|
||||
@@ -1203,6 +1206,10 @@ not present, then the native module will fail to load on Windows, with an error
|
||||
message like `Cannot find module`. See the [native module
|
||||
guide](/docs/tutorial/using-native-node-modules.md) for more.
|
||||
|
||||
### Removed: IA32 Linux support
|
||||
|
||||
Electron 18 will no longer run on 32-bit Linux systems. See [discontinuing support for 32-bit Linux](https://www.electronjs.org/blog/linux-32bit-support) for more information.
|
||||
|
||||
## Breaking API Changes (3.0)
|
||||
|
||||
The following list includes the breaking API changes in Electron 3.0.
|
||||
|
||||
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,45 @@ 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.
|
||||
- **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.
|
||||
- **References**: Useful links to better understand how the Electron project works
|
||||
and is organized.
|
||||
- **Contributing**: 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 -->
|
||||
|
||||
[tutorial]: tutorial-1-prerequisites.md
|
||||
[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
|
||||
|
||||
@@ -40,7 +47,7 @@ to `require` modules and use all of Node.js APIs.
|
||||
|
||||
### Window management
|
||||
|
||||
The primary purpose of the main process is to create and manage application windows with the
|
||||
The main process' primary purpose is to create and manage application windows with the
|
||||
[`BrowserWindow`][browser-window] module.
|
||||
|
||||
Each instance of the `BrowserWindow` class creates an application window that loads
|
||||
@@ -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
|
||||
|
||||
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 need to 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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -504,6 +504,7 @@ WebContents.prototype._callWindowOpenHandler = function (event: Electron.Event,
|
||||
if (!this._windowOpenHandler) {
|
||||
return defaultResponse;
|
||||
}
|
||||
|
||||
const response = this._windowOpenHandler(details);
|
||||
|
||||
if (typeof response !== 'object') {
|
||||
@@ -666,7 +667,15 @@ WebContents.prototype._init = function () {
|
||||
postBody,
|
||||
disposition
|
||||
};
|
||||
const result = this._callWindowOpenHandler(event, details);
|
||||
|
||||
let result: ReturnType<typeof this._callWindowOpenHandler>;
|
||||
try {
|
||||
result = this._callWindowOpenHandler(event, details);
|
||||
} catch (err) {
|
||||
event.preventDefault();
|
||||
throw err;
|
||||
}
|
||||
|
||||
const options = result.browserWindowConstructorOptions;
|
||||
if (!event.defaultPrevented) {
|
||||
openGuestWindow({
|
||||
@@ -697,7 +706,15 @@ WebContents.prototype._init = function () {
|
||||
referrer,
|
||||
postBody
|
||||
};
|
||||
const result = this._callWindowOpenHandler(event, details);
|
||||
|
||||
let result: ReturnType<typeof this._callWindowOpenHandler>;
|
||||
try {
|
||||
result = this._callWindowOpenHandler(event, details);
|
||||
} catch (err) {
|
||||
event.preventDefault();
|
||||
throw err;
|
||||
}
|
||||
|
||||
windowOpenOutlivesOpenerOption = result.outlivesOpener;
|
||||
windowOpenOverriddenOptions = result.browserWindowConstructorOptions;
|
||||
if (!event.defaultPrevented) {
|
||||
|
||||
@@ -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": "19.0.2",
|
||||
"version": "19.0.12",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
@@ -77,7 +77,7 @@
|
||||
"scripts": {
|
||||
"asar": "asar",
|
||||
"generate-version-json": "node script/generate-version-json.js",
|
||||
"lint": "node ./script/lint.js && npm run lint:clang-format && npm run lint:docs",
|
||||
"lint": "node ./script/lint.js && npm run lint:docs",
|
||||
"lint:js": "node ./script/lint.js --js",
|
||||
"lint:clang-format": "python3 script/run-clang-format.py -r -c shell/ || (echo \"\\nCode not formatted correctly.\" && exit 1)",
|
||||
"lint:clang-tidy": "ts-node ./script/run-clang-tidy.ts",
|
||||
@@ -94,6 +94,7 @@
|
||||
"gn-typescript-definitions": "npm run create-typescript-definitions && shx cp electron.d.ts",
|
||||
"pre-flight": "pre-flight",
|
||||
"gn-check": "node ./script/gn-check.js",
|
||||
"gn-format": "python3 script/run-gn-format.py",
|
||||
"precommit": "lint-staged",
|
||||
"preinstall": "node -e 'process.exit(0)'",
|
||||
"prepack": "check-for-leaks",
|
||||
@@ -124,7 +125,7 @@
|
||||
],
|
||||
"*.{gn,gni}": [
|
||||
"npm run gn-check",
|
||||
"python3 script/run-gn-format.py"
|
||||
"npm run gn-format"
|
||||
],
|
||||
"*.py": [
|
||||
"node script/lint.js --py --fix --only --"
|
||||
|
||||
@@ -118,3 +118,16 @@ make_gtk_getlibgtk_public.patch
|
||||
build_disable_print_content_analysis.patch
|
||||
feat_move_firstpartysets_to_content_browser_client.patch
|
||||
custom_protocols_plzserviceworker.patch
|
||||
posix_replace_doubleforkandexec_with_forkandspawn.patch
|
||||
cherry-pick-22c61cfae5d1.patch
|
||||
remove_default_window_title.patch
|
||||
keep_handling_scroll_update_if_you_can.patch
|
||||
chore_add_electron_deps_to_gitignores.patch
|
||||
chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch
|
||||
disable_gpu_acceleration_on_vmware_on_linux.patch
|
||||
add_maximized_parameter_to_linuxui_getwindowframeprovider.patch
|
||||
cherry-pick-94a8bdafc8c6.patch
|
||||
fix_mac_build_with_enable_plugins_false.patch
|
||||
fix_windows_build_with_enable_plugins_false.patch
|
||||
cherry-pick-bd9724c9fe63.patch
|
||||
cherry-pick-c643d18a078d.patch
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: msizanoen1 <msizanoen@qtmlabs.xyz>
|
||||
Date: Tue, 19 Jul 2022 05:11:06 +0200
|
||||
Subject: Add maximized parameter to LinuxUI::GetWindowFrameProvider
|
||||
|
||||
This allows ClientFrameViewLinux to instruct the toolkit to draw the window
|
||||
decorations in maximized mode where needed, preventing empty space caused
|
||||
by decoration shadows and rounded titlebars around the window while maximized.
|
||||
|
||||
diff --git a/ui/gtk/gtk_ui.cc b/ui/gtk/gtk_ui.cc
|
||||
index c037fa8db6d4a5ebeb7257627db206e9bab57ecc..14eddfd043e748400a9ee81ce5346a53f10ea03c 100644
|
||||
--- a/ui/gtk/gtk_ui.cc
|
||||
+++ b/ui/gtk/gtk_ui.cc
|
||||
@@ -757,13 +757,15 @@ std::unique_ptr<views::NavButtonProvider> GtkUi::CreateNavButtonProvider() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
-views::WindowFrameProvider* GtkUi::GetWindowFrameProvider(bool solid_frame) {
|
||||
+views::WindowFrameProvider* GtkUi::GetWindowFrameProvider(bool solid_frame, bool maximized) {
|
||||
if (!GtkCheckVersion(3, 14))
|
||||
return nullptr;
|
||||
auto& provider =
|
||||
- solid_frame ? solid_frame_provider_ : transparent_frame_provider_;
|
||||
+ maximized
|
||||
+ ? (solid_frame ? solid_maximized_frame_provider_ : transparent_maximized_frame_provider_)
|
||||
+ : (solid_frame ? solid_frame_provider_ : transparent_frame_provider_);
|
||||
if (!provider)
|
||||
- provider = std::make_unique<gtk::WindowFrameProviderGtk>(solid_frame);
|
||||
+ provider = std::make_unique<gtk::WindowFrameProviderGtk>(solid_frame, maximized);
|
||||
return provider.get();
|
||||
}
|
||||
|
||||
diff --git a/ui/gtk/gtk_ui.h b/ui/gtk/gtk_ui.h
|
||||
index 8e609b8311c6b69ef7b6753117542d9e60b2c8ab..a329822c3285599954d77dd5a8e33ccc10e5f626 100644
|
||||
--- a/ui/gtk/gtk_ui.h
|
||||
+++ b/ui/gtk/gtk_ui.h
|
||||
@@ -107,7 +107,7 @@ class GtkUi : public views::LinuxUI {
|
||||
bool PreferDarkTheme() const override;
|
||||
bool AnimationsEnabled() const override;
|
||||
std::unique_ptr<views::NavButtonProvider> CreateNavButtonProvider() override;
|
||||
- views::WindowFrameProvider* GetWindowFrameProvider(bool solid_frame) override;
|
||||
+ views::WindowFrameProvider* GetWindowFrameProvider(bool solid_frame, bool maximized) override;
|
||||
base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override;
|
||||
std::string GetCursorThemeName() override;
|
||||
int GetCursorThemeSize() override;
|
||||
@@ -218,6 +218,8 @@ class GtkUi : public views::LinuxUI {
|
||||
// while Chrome is running.
|
||||
std::unique_ptr<views::WindowFrameProvider> solid_frame_provider_;
|
||||
std::unique_ptr<views::WindowFrameProvider> transparent_frame_provider_;
|
||||
+ std::unique_ptr<views::WindowFrameProvider> solid_maximized_frame_provider_;
|
||||
+ std::unique_ptr<views::WindowFrameProvider> transparent_maximized_frame_provider_;
|
||||
};
|
||||
|
||||
} // namespace gtk
|
||||
diff --git a/ui/gtk/window_frame_provider_gtk.cc b/ui/gtk/window_frame_provider_gtk.cc
|
||||
index e4dbdad327eb77994ffd7f068c67336a19897915..d3ae0636455489a7c7443df85cb769952c98aca2 100644
|
||||
--- a/ui/gtk/window_frame_provider_gtk.cc
|
||||
+++ b/ui/gtk/window_frame_provider_gtk.cc
|
||||
@@ -38,16 +38,18 @@ std::string GetThemeName() {
|
||||
return theme_string;
|
||||
}
|
||||
|
||||
-GtkCssContext WindowContext(bool solid_frame, bool focused) {
|
||||
+GtkCssContext WindowContext(bool solid_frame, bool maximized, bool focused) {
|
||||
std::string selector = "#window.background.";
|
||||
selector += solid_frame ? "solid-csd" : "csd";
|
||||
+ if (maximized)
|
||||
+ selector += ".maximized";
|
||||
if (!focused)
|
||||
selector += ":inactive";
|
||||
return AppendCssNodeToStyleContext({}, selector);
|
||||
}
|
||||
|
||||
-GtkCssContext DecorationContext(bool solid_frame, bool focused) {
|
||||
- auto context = WindowContext(solid_frame, focused);
|
||||
+GtkCssContext DecorationContext(bool solid_frame, bool maximized, bool focused) {
|
||||
+ auto context = WindowContext(solid_frame, maximized, focused);
|
||||
// GTK4 renders the decoration directly on the window.
|
||||
if (!GtkCheckVersion(4))
|
||||
context = AppendCssNodeToStyleContext(context, "#decoration");
|
||||
@@ -64,8 +66,8 @@ GtkCssContext DecorationContext(bool solid_frame, bool focused) {
|
||||
return context;
|
||||
}
|
||||
|
||||
-GtkCssContext HeaderContext(bool solid_frame, bool focused) {
|
||||
- auto context = WindowContext(solid_frame, focused);
|
||||
+GtkCssContext HeaderContext(bool solid_frame, bool maximized, bool focused) {
|
||||
+ auto context = WindowContext(solid_frame, maximized, focused);
|
||||
context =
|
||||
AppendCssNodeToStyleContext(context, "#headerbar.header-bar.titlebar");
|
||||
if (!focused)
|
||||
@@ -110,8 +112,8 @@ int ComputeTopCornerRadius() {
|
||||
// need to experimentally determine the corner radius by rendering a sample.
|
||||
// Additionally, in GTK4, the headerbar corners get clipped by the window
|
||||
// rather than the headerbar having its own rounded corners.
|
||||
- auto context = GtkCheckVersion(4) ? DecorationContext(false, false)
|
||||
- : HeaderContext(false, false);
|
||||
+ auto context = GtkCheckVersion(4) ? DecorationContext(false, false, false)
|
||||
+ : HeaderContext(false, false, false);
|
||||
ApplyCssToContext(context, R"(window, headerbar {
|
||||
background-image: none;
|
||||
background-color: black;
|
||||
@@ -169,8 +171,8 @@ void WindowFrameProviderGtk::Asset::CloneFrom(
|
||||
unfocused_bitmap = src.unfocused_bitmap;
|
||||
}
|
||||
|
||||
-WindowFrameProviderGtk::WindowFrameProviderGtk(bool solid_frame)
|
||||
- : solid_frame_(solid_frame) {}
|
||||
+WindowFrameProviderGtk::WindowFrameProviderGtk(bool solid_frame, bool maximized)
|
||||
+ : solid_frame_(solid_frame), maximized_(maximized) {}
|
||||
|
||||
WindowFrameProviderGtk::~WindowFrameProviderGtk() = default;
|
||||
|
||||
@@ -264,7 +266,7 @@ void WindowFrameProviderGtk::PaintWindowFrame(gfx::Canvas* canvas,
|
||||
top_area_height_dip * scale - asset.frame_thickness_px.top();
|
||||
|
||||
auto header = PaintHeaderbar({client_bounds_px.width(), top_area_height_px},
|
||||
- HeaderContext(solid_frame_, focused), scale);
|
||||
+ HeaderContext(solid_frame_, maximized_, focused), scale);
|
||||
image = gfx::ImageSkia::CreateFrom1xBitmap(header);
|
||||
// In GTK4, the headerbar gets clipped by the window.
|
||||
if (GtkCheckVersion(4)) {
|
||||
@@ -296,7 +298,7 @@ void WindowFrameProviderGtk::MaybeUpdateBitmaps(float scale) {
|
||||
|
||||
gfx::Rect frame_bounds_dip(kMaxFrameSizeDip, kMaxFrameSizeDip,
|
||||
2 * kMaxFrameSizeDip, 2 * kMaxFrameSizeDip);
|
||||
- auto focused_context = DecorationContext(solid_frame_, true);
|
||||
+ auto focused_context = DecorationContext(solid_frame_, maximized_, true);
|
||||
frame_bounds_dip.Inset(-GtkStyleContextGetPadding(focused_context));
|
||||
frame_bounds_dip.Inset(-GtkStyleContextGetBorder(focused_context));
|
||||
gfx::Size bitmap_size(BitmapSizePx(asset), BitmapSizePx(asset));
|
||||
@@ -304,7 +306,7 @@ void WindowFrameProviderGtk::MaybeUpdateBitmaps(float scale) {
|
||||
PaintBitmap(bitmap_size, frame_bounds_dip, focused_context, scale);
|
||||
asset.unfocused_bitmap =
|
||||
PaintBitmap(bitmap_size, frame_bounds_dip,
|
||||
- DecorationContext(solid_frame_, false), scale);
|
||||
+ DecorationContext(solid_frame_, maximized_, false), scale);
|
||||
|
||||
// In GTK4, there's no way to obtain the frame thickness from CSS values
|
||||
// directly, so we must determine it experimentally based on the drawn
|
||||
diff --git a/ui/gtk/window_frame_provider_gtk.h b/ui/gtk/window_frame_provider_gtk.h
|
||||
index d3039d73161378197557947aece88d2710c1e486..f7d4605938210b0b75517bb7bcab28b588a16520 100644
|
||||
--- a/ui/gtk/window_frame_provider_gtk.h
|
||||
+++ b/ui/gtk/window_frame_provider_gtk.h
|
||||
@@ -14,7 +14,7 @@ namespace gtk {
|
||||
|
||||
class WindowFrameProviderGtk : public views::WindowFrameProvider {
|
||||
public:
|
||||
- explicit WindowFrameProviderGtk(bool solid_frame);
|
||||
+ explicit WindowFrameProviderGtk(bool solid_frame, bool maximized);
|
||||
|
||||
WindowFrameProviderGtk(const WindowFrameProviderGtk&) = delete;
|
||||
WindowFrameProviderGtk& operator=(const WindowFrameProviderGtk&) = delete;
|
||||
@@ -69,6 +69,9 @@ class WindowFrameProviderGtk : public views::WindowFrameProvider {
|
||||
|
||||
// Cached bitmaps and metrics. The scale is rounded to percent.
|
||||
base::flat_map<int, Asset> assets_;
|
||||
+
|
||||
+ // Whether to draw the window decorations as maximized.
|
||||
+ bool maximized_;
|
||||
};
|
||||
|
||||
} // namespace gtk
|
||||
diff --git a/ui/views/linux_ui/linux_ui.h b/ui/views/linux_ui/linux_ui.h
|
||||
index 5f312e01508c49033bf2d05b9dc083e524f37e5f..7590acd760ba4f36e5a77c1972afc5fc08e616ff 100644
|
||||
--- a/ui/views/linux_ui/linux_ui.h
|
||||
+++ b/ui/views/linux_ui/linux_ui.h
|
||||
@@ -189,7 +189,7 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory,
|
||||
// if transparency is unsupported and the frame should be rendered opaque.
|
||||
// The returned object is not owned by the caller and will remain alive until
|
||||
// the process ends.
|
||||
- virtual WindowFrameProvider* GetWindowFrameProvider(bool solid_frame) = 0;
|
||||
+ virtual WindowFrameProvider* GetWindowFrameProvider(bool solid_frame, bool maximized) = 0;
|
||||
|
||||
// Returns a map of KeyboardEvent code to KeyboardEvent key values.
|
||||
virtual base::flat_map<std::string, std::string> GetKeyboardLayoutMap() = 0;
|
||||
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 f8cd9d51222c70166a82cdd6dd4b7d0c24970606..8c38f004109aa967e1e5439a17dc35d3013e8ecf 100644
|
||||
--- a/content/browser/file_system_access/file_system_chooser.cc
|
||||
+++ b/content/browser/file_system_access/file_system_chooser.cc
|
||||
@@ -275,13 +275,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);
|
||||
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 c7e6f906ff32cb198f8723d6678971ae6cff28a2..d52dc0e925c54634452f86ca90792b318776e895 100644
|
||||
--- a/components/autofill/core/browser/ui/address_combobox_model.cc
|
||||
+++ b/components/autofill/core/browser/ui/address_combobox_model.cc
|
||||
@@ -84,14 +84,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();
|
||||
@@ -131,7 +123,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 6439ccb1f074f1c3965dfb2b45ab6f6636437c17..42626aff427f1b3f3400aba399e3f9c89faa06ba 100644
|
||||
--- a/components/autofill/core/browser/ui/region_combobox_model.cc
|
||||
+++ b/components/autofill/core/browser/ui/region_combobox_model.cc
|
||||
@@ -67,14 +67,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.
|
||||
@@ -95,7 +87,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 aa135250e469d2405fba618c96b0bf38acaf59f0..b8d41cfeb67f3b4c098b2b329b2dfa7fe246dabb 100644
|
||||
--- a/ui/views/controls/combobox/combobox.cc
|
||||
+++ b/ui/views/controls/combobox/combobox.cc
|
||||
@@ -578,6 +578,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 b5301bfb0a3c8da3daa1d3646c2f4872cef2cdab..faac607fefcc8caf572eb83456e7520146c1d0ad 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:
|
||||
203
patches/chromium/cherry-pick-bd9724c9fe63.patch
Normal file
203
patches/chromium/cherry-pick-bd9724c9fe63.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 5f88192eb4e92d4c41c79db188b8b9982f005258..26f7716ae83b70cdcf7cdb23652c2c56d7dc27bf 100644
|
||||
--- a/components/background_fetch/background_fetch_delegate_base.cc
|
||||
+++ b/components/background_fetch/background_fetch_delegate_base.cc
|
||||
@@ -101,6 +101,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 2b2da55b06e3b4859f439d5b3bf7013b981b75a0..b207ce5c6bd3c332a34885e1dc58cc11d91fe31c 100644
|
||||
--- a/components/download/content/internal/download_driver_impl.cc
|
||||
+++ b/components/download/content/internal/download_driver_impl.cc
|
||||
@@ -229,6 +229,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 11f524861320eae8ab694022d97a6fe2ac7366ce..454326720b5c88cc61e4527790797fd7f522014d 100644
|
||||
--- a/components/download/internal/common/download_utils.cc
|
||||
+++ b/components/download/internal/common/download_utils.cc
|
||||
@@ -386,8 +386,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());
|
||||
}
|
||||
|
||||
@@ -395,7 +397,8 @@ std::unique_ptr<network::ResourceRequest> CreateResourceRequest(
|
||||
request->referrer = params->referrer();
|
||||
request->referrer_policy = params->referrer_policy();
|
||||
request->is_outermost_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 8b911276e757ee889ed05a3fd60cc365ad8f1982..a976d470094462be735e81cf097f32b34c258270 100644
|
||||
--- a/components/download/public/background_service/download_params.h
|
||||
+++ b/components/download/public/background_service/download_params.h
|
||||
@@ -126,6 +126,12 @@ struct RequestParams {
|
||||
// be invalidate during download resumption in new browser session. Not
|
||||
// supported on iOS.
|
||||
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 6dcc861171b7034d7426c5c683cafca689e3d7ea..500521ce2c383d40c13a9359ba4c49d3417670f7 100644
|
||||
--- a/content/browser/download/download_browsertest.cc
|
||||
+++ b/content/browser/download/download_browsertest.cc
|
||||
@@ -3763,6 +3763,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) {
|
||||
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 32a818ba8723f020fa536eb93a31c16b76ed92a1..d729dfc5d505c4c23872ef535a8b09f1af065914 100644
|
||||
--- a/content/browser/background_fetch/storage/mark_request_complete_task.cc
|
||||
+++ b/content/browser/background_fetch/storage/mark_request_complete_task.cc
|
||||
@@ -104,6 +104,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));
|
||||
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 cc0150c39bf635fa09d3efe9bfea2997b0b8263a..cdca4c4a9cccb7de0b13c8b803d6c74ba4d63d3a 100644
|
||||
--- a/.gitignore
|
||||
+++ b/.gitignore
|
||||
@@ -224,6 +224,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 567e1de3c31b2275e07b897c2644c91aef9ca076..a6744fa9d31781c6ee866ba6291588caf44e8559 100644
|
||||
--- a/third_party/.gitignore
|
||||
+++ b/third_party/.gitignore
|
||||
@@ -83,6 +83,7 @@
|
||||
/directxsdk
|
||||
/dom_distiller_js/dist
|
||||
/eigen3/src
|
||||
+/electron_node
|
||||
/elfutils/src
|
||||
/emoji-segmenter/src
|
||||
/emoji-metadata/src
|
||||
@@ -177,6 +178,7 @@
|
||||
/mocha
|
||||
/mockito/src
|
||||
/nacl_sdk_binaries/
|
||||
+/nan
|
||||
/nasm
|
||||
/nearby/src
|
||||
/neon_2_sse/src
|
||||
@@ -239,6 +241,7 @@
|
||||
/speex
|
||||
/sqlite/src
|
||||
/sqlite4java/lib/
|
||||
+/squirrel.mac
|
||||
/subresource-filter-ruleset/data/UnindexedRules
|
||||
/swift-format
|
||||
/swiftshader/
|
||||
@@ -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 9fd052c00a484cd1acd2031fda79e6307fd01b60..016dfe880a48168154c08839afa540880c2d52be 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 01ff95be00b3911749f66a136b2b5a6c02156bd3..23e8794c0551c377269ebecd6684206fc7087553 100644
|
||||
--- a/ui/views/win/hwnd_message_handler.cc
|
||||
+++ b/ui/views/win/hwnd_message_handler.cc
|
||||
@@ -3064,15 +3064,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;
|
||||
};
|
||||
@@ -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 14e13169de8b5e808e3284792237bef9939b1e00..7628a96dc8b2c9e8128cd462138d3487304c6e10 100644
|
||||
index 14e13169de8b5e808e3284792237bef9939b1e00..67839f87fb0dcb2eed9bc001806020befbed5f66 100644
|
||||
--- a/cc/trees/layer_tree_host_impl.cc
|
||||
+++ b/cc/trees/layer_tree_host_impl.cc
|
||||
@@ -1894,6 +1894,9 @@ void LayerTreeHostImpl::SetIsLikelyToRequireADraw(
|
||||
@@ -1894,6 +1894,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 09f72f1fbd7b782c5bf52245482b358103f0c243..80d9fd40efed1edc90e7bdf1db534acb
|
||||
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 648f25d99884b99f49e26cd9f280a8a6ae63e1c7..0f87961ecd8c24e3ba82c6bae187a12c9b112bd4 100644
|
||||
index 648f25d99884b99f49e26cd9f280a8a6ae63e1c7..e42f8ba89070477b06777c7a0c37d30c8c5e4ed1 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 648f25d99884b99f49e26cd9f280a8a6ae63e1c7..0f87961ecd8c24e3ba82c6bae187a12c
|
||||
|
||||
namespace blink {
|
||||
|
||||
@@ -118,6 +120,11 @@ uint8_t CanvasColorParams::BytesPerPixel() const {
|
||||
@@ -19,6 +21,10 @@ namespace blink {
|
||||
// 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,40 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Zhenyao Mo <zmo@chromium.org>
|
||||
Date: Mon, 23 May 2022 21:43:16 +0000
|
||||
Subject: Disable GPU acceleration on VMware on Linux
|
||||
|
||||
TEST=manual
|
||||
R=sunnyps@chromium.org
|
||||
|
||||
Bug: 1327939
|
||||
Change-Id: Ib09b6623665ffecf0af1f9f8bc3ebef1ac042b2f
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3661941
|
||||
Auto-Submit: Zhenyao Mo <zmo@chromium.org>
|
||||
Commit-Queue: Zhenyao Mo <zmo@chromium.org>
|
||||
Commit-Queue: Maggie Chen <magchen@chromium.org>
|
||||
Reviewed-by: Maggie Chen <magchen@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1006613}
|
||||
|
||||
diff --git a/gpu/config/software_rendering_list.json b/gpu/config/software_rendering_list.json
|
||||
index 8ed8faf0a0733568a81d955a5546d864d24f78b5..73b6e998f753cee5d77e6bd5983ed46702f13580 100644
|
||||
--- a/gpu/config/software_rendering_list.json
|
||||
+++ b/gpu/config/software_rendering_list.json
|
||||
@@ -1699,6 +1699,18 @@
|
||||
"features": [
|
||||
"accelerated_video_encode"
|
||||
]
|
||||
+ },
|
||||
+ {
|
||||
+ "id": 176,
|
||||
+ "description": "VMware is buggy on Linux",
|
||||
+ "cr_bugs": [1327939],
|
||||
+ "os": {
|
||||
+ "type": "linux"
|
||||
+ },
|
||||
+ "vendor_id": "0x15ad",
|
||||
+ "features": [
|
||||
+ "all"
|
||||
+ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -103,7 +103,7 @@ index 4c4cc16db82d7434573f7740855fbe72d68815e6..f71290800b6bb51a39b1f86be36f02d6
|
||||
string mime_type;
|
||||
|
||||
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
|
||||
index c79ad06ecb80ea431bf48c16e413286c768e2316..2a5cce5b467e79df1f34d7318862fd16134b74c3 100644
|
||||
index 85e984259a1e067c76d57e5e54eb3ed1d9694ec0..dbe9e809898ee720c0670e9c6db73c38658613b2 100644
|
||||
--- a/services/network/url_loader.cc
|
||||
+++ b/services/network/url_loader.cc
|
||||
@@ -469,6 +469,7 @@ URLLoader::URLLoader(
|
||||
@@ -123,7 +123,7 @@ index c79ad06ecb80ea431bf48c16e413286c768e2316..2a5cce5b467e79df1f34d7318862fd16
|
||||
url_request_->SetResponseHeadersCallback(base::BindRepeating(
|
||||
&URLLoader::SetRawResponseHeaders, base::Unretained(this)));
|
||||
}
|
||||
@@ -1427,6 +1428,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) {
|
||||
@@ -1428,6 +1429,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) {
|
||||
}
|
||||
|
||||
response_ = BuildResponseHead();
|
||||
|
||||
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 480244fb50f636f519485693b8d1a4cf5033c3b0..f426e30ff371c35382512d7ec7846ca6cf58515e 100644
|
||||
--- a/chrome/browser/app_controller_mac.mm
|
||||
+++ b/chrome/browser/app_controller_mac.mm
|
||||
@@ -108,7 +108,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 05e9b29d7a039094fb6fd63fb8b152439f089d41..b860c1e54f0ced814fb269087924d2f695f816f3 100644
|
||||
--- a/content/browser/sandbox_mac_unittest.mm
|
||||
+++ b/content/browser/sandbox_mac_unittest.mm
|
||||
@@ -30,6 +30,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"
|
||||
@@ -93,7 +94,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"
|
||||
@@ -59,10 +59,10 @@ index ad366d0fd4c3a637d75a102ab56984f0d01bfc04..d63eb133fd4bab1ea309bb8c742acf88
|
||||
// true if register successfully, or false if 1) the specificied |accelerator|
|
||||
// has been registered by another caller or other native applications, or
|
||||
diff --git a/content/browser/media/media_keys_listener_manager_impl.cc b/content/browser/media/media_keys_listener_manager_impl.cc
|
||||
index b954f8dde00d4f5257223c464e9145a6bef48900..b58999f295586a61bcc2648488a8b28f15d80a7e 100644
|
||||
index b954f8dde00d4f5257223c464e9145a6bef48900..ee9da826014d3aae9675daac6cdbc0f447a14efd 100644
|
||||
--- a/content/browser/media/media_keys_listener_manager_impl.cc
|
||||
+++ b/content/browser/media/media_keys_listener_manager_impl.cc
|
||||
@@ -56,7 +56,12 @@ bool MediaKeysListenerManagerImpl::StartWatchingMediaKey(
|
||||
@@ -56,7 +56,11 @@ bool MediaKeysListenerManagerImpl::StartWatchingMediaKey(
|
||||
CanActiveMediaSessionControllerReceiveEvents();
|
||||
|
||||
// Tell the underlying MediaKeysListener to listen for the key.
|
||||
@@ -71,12 +71,11 @@ index b954f8dde00d4f5257223c464e9145a6bef48900..b58999f295586a61bcc2648488a8b28f
|
||||
+#if BUILDFLAG(IS_MAC)
|
||||
+ !media_key_handling_enabled_ &&
|
||||
+#endif // BUILDFLAG(IS_MAC)
|
||||
+ should_start_watching &&
|
||||
+ media_keys_listener_ &&
|
||||
+ should_start_watching && media_keys_listener_ &&
|
||||
!media_keys_listener_->StartWatchingMediaKey(key_code)) {
|
||||
return false;
|
||||
}
|
||||
@@ -239,18 +244,18 @@ void MediaKeysListenerManagerImpl::StartListeningForMediaKeysIfNecessary() {
|
||||
@@ -239,6 +243,7 @@ void MediaKeysListenerManagerImpl::StartListeningForMediaKeysIfNecessary() {
|
||||
#endif
|
||||
|
||||
if (system_media_controls_) {
|
||||
@@ -84,19 +83,22 @@ index b954f8dde00d4f5257223c464e9145a6bef48900..b58999f295586a61bcc2648488a8b28f
|
||||
system_media_controls_->AddObserver(this);
|
||||
system_media_controls_notifier_ =
|
||||
std::make_unique<SystemMediaControlsNotifier>(
|
||||
system_media_controls_.get());
|
||||
- } else {
|
||||
- // If we can't access system media controls, then directly listen for media
|
||||
- // key keypresses instead.
|
||||
- media_keys_listener_ = ui::MediaKeysListener::Create(
|
||||
- this, ui::MediaKeysListener::Scope::kGlobal);
|
||||
- DCHECK(media_keys_listener_);
|
||||
@@ -251,6 +256,19 @@ void MediaKeysListenerManagerImpl::StartListeningForMediaKeysIfNecessary() {
|
||||
DCHECK(media_keys_listener_);
|
||||
}
|
||||
|
||||
+ // Directly listen for media key keypresses when using GlobalShortcuts.
|
||||
+ media_keys_listener_ = ui::MediaKeysListener::Create(
|
||||
+ this, ui::MediaKeysListener::Scope::kGlobal);
|
||||
+ DCHECK(media_keys_listener_);
|
||||
+#if BUILDFLAG(IS_MAC)
|
||||
+ // Chromium's implementation of SystemMediaControls falls
|
||||
+ // down into MPRemoteCommandCenter, which makes it such that an app will not
|
||||
+ // will not receive remote control events until it begins playing audio.
|
||||
+ // If there's not already a MediaKeysListener instance, create one so
|
||||
+ // that globalShortcuts work correctly.
|
||||
+ if (!media_keys_listener_) {
|
||||
+ media_keys_listener_ = ui::MediaKeysListener::Create(
|
||||
+ this, ui::MediaKeysListener::Scope::kGlobal);
|
||||
+ DCHECK(media_keys_listener_);
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
EnsureAuxiliaryServices();
|
||||
}
|
||||
|
||||
@@ -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 939c28a029418bc353795aa1a007508680f42e57..2db6a0d23d72c3d77db6eec4de7c9d0f0500fbe2 100644
|
||||
--- a/chrome/browser/chrome_content_browser_client.cc
|
||||
+++ b/chrome/browser/chrome_content_browser_client.cc
|
||||
@@ -3951,7 +3951,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:
|
||||
@@ -4023,7 +4025,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 dec80de25675467a07d3c213538d240507916254..1df3675fed1656094e808fa4477ffd2ea126b83d 100644
|
||||
--- a/content/test/BUILD.gn
|
||||
+++ b/content/test/BUILD.gn
|
||||
@@ -2168,7 +2168,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",
|
||||
@@ -2870,8 +2869,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 e4e7cfe65b87ef3bc6b88073dc384a50fd8256ea..0f89e8b8e971fe77cc537cb2e92b54f1d988bc2a 100644
|
||||
--- a/sandbox/policy/win/sandbox_win.cc
|
||||
+++ b/sandbox/policy/win/sandbox_win.cc
|
||||
@@ -38,6 +38,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/features.h"
|
||||
#include "sandbox/policy/features.h"
|
||||
@@ -1216,8 +1217,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:
|
||||
164
patches/chromium/keep_handling_scroll_update_if_you_can.patch
Normal file
164
patches/chromium/keep_handling_scroll_update_if_you_can.patch
Normal file
@@ -0,0 +1,164 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Mehdi Kazemi <mehdika@chromium.org>
|
||||
Date: Tue, 7 Jun 2022 17:32:20 +0000
|
||||
Subject: Keep handling scroll update if you can!
|
||||
|
||||
If scroll_gesture_handling_node_ doesn't have a layout object, but you can still handle GSU, do that!
|
||||
|
||||
Change-Id: Ib82dad96d319b186a5cc2f2eb07495ca5ae994a3
|
||||
Bug: 1330045
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3681882
|
||||
Reviewed-by: Steve Kobes <skobes@chromium.org>
|
||||
Commit-Queue: Mehdi Kazemi <mehdika@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#1011539}
|
||||
|
||||
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
|
||||
index 28b6092d264219802f718bfaf58f2da4c6054e43..604cad242a9e5626c7d6bac305cb2e323e1f1fca 100644
|
||||
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
|
||||
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
|
||||
@@ -589,12 +589,6 @@ WebInputEventResult ScrollManager::HandleGestureScrollUpdate(
|
||||
return WebInputEventResult::kNotHandled;
|
||||
}
|
||||
|
||||
- Node* node = scroll_gesture_handling_node_.Get();
|
||||
- if (!node || !node->GetLayoutObject()) {
|
||||
- TRACE_EVENT_INSTANT0("input", "Lost scroll_gesture_handling_node",
|
||||
- TRACE_EVENT_SCOPE_THREAD);
|
||||
- return WebInputEventResult::kNotHandled;
|
||||
- }
|
||||
if (snap_fling_controller_) {
|
||||
if (snap_fling_controller_->HandleGestureScrollUpdate(
|
||||
GetGestureScrollUpdateInfo(gesture_event))) {
|
||||
@@ -615,7 +609,10 @@ WebInputEventResult ScrollManager::HandleGestureScrollUpdate(
|
||||
if (delta.IsZero())
|
||||
return WebInputEventResult::kNotHandled;
|
||||
|
||||
- LayoutObject* layout_object = node->GetLayoutObject();
|
||||
+ LayoutObject* layout_object =
|
||||
+ scroll_gesture_handling_node_
|
||||
+ ? scroll_gesture_handling_node_->GetLayoutObject()
|
||||
+ : nullptr;
|
||||
|
||||
// Try to send the event to the correct view.
|
||||
WebInputEventResult result =
|
||||
diff --git a/third_party/blink/web_tests/fast/events/touch/touch-latched-scroll-node-removed.html b/third_party/blink/web_tests/fast/events/touch/touch-latched-scroll-node-removed.html
|
||||
index 90265f908ad0ce93d7bd401df2aa6657cf25e6fb..b9b34c24496868355c6eff06f738a95832956481 100644
|
||||
--- a/third_party/blink/web_tests/fast/events/touch/touch-latched-scroll-node-removed.html
|
||||
+++ b/third_party/blink/web_tests/fast/events/touch/touch-latched-scroll-node-removed.html
|
||||
@@ -62,6 +62,7 @@ childDiv.addEventListener('scroll', () => {
|
||||
|
||||
promise_test( async () => {
|
||||
setUpForTest();
|
||||
+ await waitForCompositorCommit();
|
||||
// Start scrolling on the child div and remove the div in the middle of
|
||||
// scrolling, then check that parentDiv have not scrolled.
|
||||
var x = (rect.left + rect.right) / 2;
|
||||
@@ -69,6 +70,7 @@ promise_test( async () => {
|
||||
// Slow scrolling gives enough time to switch from cc to main.
|
||||
var pixels_per_sec = 100;
|
||||
await smoothScroll(400, x, y, GestureSourceType.TOUCH_INPUT, 'down', pixels_per_sec);
|
||||
- await waitFor( () => {return parentDiv.scrollTop === 0});
|
||||
-}, "New node must start wheel scrolling when the latched node is removed.");
|
||||
+ await conditionHolds( () => { return parentDiv.scrollTop === 0; },
|
||||
+ "parentDiv has scrolled, which should not have!" );
|
||||
+}, "New node must NOT start wheel scrolling when the latched node is removed.");
|
||||
</script>
|
||||
diff --git a/third_party/blink/web_tests/fast/events/wheel/wheel-latched-scroll-node-removed.html b/third_party/blink/web_tests/fast/events/wheel/wheel-latched-scroll-node-removed.html
|
||||
index 966a887b2129fa26cd990b367a7df1fc9135a207..493f13d9c519422b00fe0e11874032fdf25130db 100644
|
||||
--- a/third_party/blink/web_tests/fast/events/wheel/wheel-latched-scroll-node-removed.html
|
||||
+++ b/third_party/blink/web_tests/fast/events/wheel/wheel-latched-scroll-node-removed.html
|
||||
@@ -62,6 +62,7 @@ childDiv.addEventListener('scroll', () => {
|
||||
|
||||
promise_test( async () => {
|
||||
setUpForTest();
|
||||
+ await waitForCompositorCommit();
|
||||
// Start scrolling on the child div and remove the div in the middle of
|
||||
// scrolling, then check that parentDiv have not scrolled.
|
||||
var x = (rect.left + rect.right) / 2;
|
||||
@@ -69,6 +70,7 @@ promise_test( async () => {
|
||||
// Slow scrolling gives enough time to switch from cc to main.
|
||||
var pixels_per_sec = 100;
|
||||
await smoothScroll(400, x, y, GestureSourceType.MOUSE_INPUT, 'down', pixels_per_sec);
|
||||
- await waitFor( () => {return parentDiv.scrollTop === 0});
|
||||
-}, "New node must start wheel scrolling when the latched node is removed.");
|
||||
+ await conditionHolds( () => { return parentDiv.scrollTop === 0; },
|
||||
+ "parentDiv has scrolled, which should not have!" );
|
||||
+}, "New node must NOT start wheel scrolling when the latched node is removed.");
|
||||
</script>
|
||||
diff --git a/third_party/blink/web_tests/fast/scrolling/inertial-scrolling-with-pointer-events-none-overlay.html b/third_party/blink/web_tests/fast/scrolling/inertial-scrolling-with-pointer-events-none-overlay.html
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..47291b70316beaac16adaa6ddd0035ebeb9ec84f
|
||||
--- /dev/null
|
||||
+++ b/third_party/blink/web_tests/fast/scrolling/inertial-scrolling-with-pointer-events-none-overlay.html
|
||||
@@ -0,0 +1,71 @@
|
||||
+<!DOCTYPE HTML>
|
||||
+<script src="../../resources/testharness.js"></script>
|
||||
+<script src="../../resources/testharnessreport.js"></script>
|
||||
+<script src="../../resources/testdriver.js"></script>
|
||||
+<script src="../../resources/testdriver-actions.js"></script>
|
||||
+<script src="../../resources/testdriver-vendor.js"></script>
|
||||
+<script src="../../resources/gesture-util.js"></script>
|
||||
+
|
||||
+<style>
|
||||
+#container {
|
||||
+ width: 400px;
|
||||
+ height: 400px;
|
||||
+ overflow: auto;
|
||||
+}
|
||||
+
|
||||
+#inner {
|
||||
+ height: 3000px;
|
||||
+ background-color: #eee;
|
||||
+}
|
||||
+
|
||||
+#overlay {
|
||||
+ position: absolute;
|
||||
+ left: 0;
|
||||
+ top: 0;
|
||||
+ width: 100%;
|
||||
+ height: 100%;
|
||||
+ pointer-events: none;
|
||||
+}
|
||||
+
|
||||
+p {
|
||||
+ margin: 0;
|
||||
+ padding: 1000px 0;
|
||||
+}
|
||||
+</style>
|
||||
+
|
||||
+<body style="margin:0" onload=runTest()>
|
||||
+ <div id="container">
|
||||
+ <div id="inner"></div>
|
||||
+ </div>
|
||||
+ <div id="overlay"></div>
|
||||
+</body>
|
||||
+
|
||||
+<script>
|
||||
+const container = document.getElementById('container');
|
||||
+const inner = document.getElementById('inner');
|
||||
+
|
||||
+const update = () => inner.innerHTML = '<p>Content</p>';
|
||||
+setInterval(update, 200);
|
||||
+
|
||||
+function runTest() {
|
||||
+ promise_test (async (t) => {
|
||||
+ const pixels_to_scroll = 100;
|
||||
+ const start_x = 200;
|
||||
+ const start_y = 200;
|
||||
+ const speed_in_pixels_s = 900;
|
||||
+
|
||||
+ await waitForCompositorCommit();
|
||||
+ await swipe(pixels_to_scroll, start_x, start_y, 'up', speed_in_pixels_s);
|
||||
+ await waitForAnimationEndTimeBased(() => { return container.scrollTop; });
|
||||
+ assert_greater_than(container.scrollTop, pixels_to_scroll,
|
||||
+ "container should scroll at least 100 pixels, which is the length of the swipe.");
|
||||
+
|
||||
+ const scroll_top_previous_value = container.scrollTop;
|
||||
+
|
||||
+ await waitForCompositorCommit();
|
||||
+ await swipe(pixels_to_scroll, start_x, start_y, 'up', speed_in_pixels_s);
|
||||
+ await waitForAnimationEndTimeBased(() => { return container.scrollTop; });
|
||||
+ assert_greater_than(container.scrollTop, scroll_top_previous_value + pixels_to_scroll);
|
||||
+ }, "Make sure inertial scrolling is not broken with pointer-events:none overlay.");
|
||||
+}
|
||||
+</script>
|
||||
@@ -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 0ed04582106639911d9a4e0284ff880be9c3bc74..bfda81b08be52406048be9b96d2de59223d56ee7 100644
|
||||
index 0ed04582106639911d9a4e0284ff880be9c3bc74..0f7aaf3254864e92a54634dedb2fd2fbf8eb8990 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 0ed04582106639911d9a4e0284ff880be9c3bc74..bfda81b08be52406048be9b96d2de592
|
||||
bool LoadGtk3() {
|
||||
if (!GetLibGtk3(false))
|
||||
return false;
|
||||
@@ -134,6 +128,12 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
|
||||
@@ -134,6 +123,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 0ed04582106639911d9a4e0284ff880be9c3bc74..bfda81b08be52406048be9b96d2de592
|
||||
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..63e383f03f51124f67a0c522f57eb4cc23857bc1 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,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 53c2dadac9d6cb74a3c3e07d9917e9162474b8a0..6761db0e8ac5edba6fac90cdf79e38dfd4cd9d70 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"
|
||||
|
||||
@@ -454,7 +454,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;
|
||||
}
|
||||
|
||||
@@ -609,7 +609,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(
|
||||
@@ -658,7 +658,7 @@ bool CrashpadClient::StartHandlerWithLinkerForClient(
|
||||
annotations,
|
||||
arguments,
|
||||
socket);
|
||||
- return DoubleForkAndExec(argv, env, socket, false, nullptr);
|
||||
+ return ForkAndSpawn(argv, env, socket, false, nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -692,7 +692,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 39e35678ecdd036f8c8ae27c973c27102b77da96..84385f2569f2bd00ca8aed0aa332fb450b2de1d3 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 30f03b13f3a018e6a96b0689452a344992675c23..64fa2e9e89b700d8a265a9737cb6a30a210cdffe 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 3f696838ee798103480d86aee77d08bac86a5803..0a2d68371785718a736e3ba9eaf12a0fe2b59bd8 100644
|
||||
--- a/ui/ozone/platform/x11/x11_window.cc
|
||||
+++ b/ui/ozone/platform/x11/x11_window.cc
|
||||
@@ -361,6 +361,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 81d42ae3167c0a55fbe3642c5ca862deed291793..1d37c798dad0246c24726e16a9a05b3407b88668 100644
|
||||
--- a/ui/ozone/platform/x11/x11_window.h
|
||||
+++ b/ui/ozone/platform/x11/x11_window.h
|
||||
@@ -364,7 +364,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;
|
||||
@@ -1 +1,2 @@
|
||||
add_thread_local_to_x_error_trap_cc.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 58fef4041ecfcef3a76fcfb296c923355f68625c..3604d198e8fabf8a689746f073b6783fc827e7c5 100644
|
||||
--- a/pc/rtp_sender.cc
|
||||
+++ b/pc/rtp_sender.cc
|
||||
@@ -75,8 +75,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;
|
||||
@@ -70,9 +70,9 @@ const LINTERS = [{
|
||||
test: filename => filename.endsWith('.cc') || (filename.endsWith('.h') && !isObjCHeader(filename)),
|
||||
run: (opts, filenames) => {
|
||||
if (opts.fix) {
|
||||
spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]);
|
||||
spawnAndCheckExitCode('python3', ['script/run-clang-format.py', '-r', '--fix', ...filenames]);
|
||||
} else {
|
||||
spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]);
|
||||
spawnAndCheckExitCode('python3', ['script/run-clang-format.py', '-r', ...filenames]);
|
||||
}
|
||||
cpplint(filenames);
|
||||
}
|
||||
@@ -82,9 +82,9 @@ const LINTERS = [{
|
||||
test: filename => filename.endsWith('.mm') || (filename.endsWith('.h') && isObjCHeader(filename)),
|
||||
run: (opts, filenames) => {
|
||||
if (opts.fix) {
|
||||
spawnAndCheckExitCode('python', ['script/run-clang-format.py', '--fix', ...filenames]);
|
||||
spawnAndCheckExitCode('python3', ['script/run-clang-format.py', '-r', '--fix', ...filenames]);
|
||||
} else {
|
||||
spawnAndCheckExitCode('python', ['script/run-clang-format.py', ...filenames]);
|
||||
spawnAndCheckExitCode('python3', ['script/run-clang-format.py', '-r', ...filenames]);
|
||||
}
|
||||
const filter = [
|
||||
'-readability/braces',
|
||||
|
||||
@@ -18,7 +18,8 @@ const args = require('minimist')(process.argv.slice(2), {
|
||||
});
|
||||
|
||||
async function main () {
|
||||
const nodeDir = path.resolve(BASE, `out/${utils.getOutDir({ shouldLog: true })}/gen/node_headers`);
|
||||
const outDir = utils.getOutDir({ shouldLog: true });
|
||||
const nodeDir = path.resolve(BASE, 'out', outDir, 'gen', 'node_headers');
|
||||
const env = Object.assign({}, process.env, {
|
||||
npm_config_nodedir: nodeDir,
|
||||
npm_config_msvs_version: '2019',
|
||||
@@ -31,6 +32,25 @@ async function main () {
|
||||
const cxx = path.resolve(clangDir, 'clang++');
|
||||
const ld = path.resolve(clangDir, 'lld');
|
||||
|
||||
const platformFlags = [];
|
||||
if (process.platform === 'darwin') {
|
||||
const sdkPath = path.resolve(BASE, 'out', outDir, 'sdk', 'xcode_links');
|
||||
const sdks = (await fs.promises.readdir(sdkPath)).filter(fileName => fileName.endsWith('.sdk'));
|
||||
const sdkToUse = sdks[0];
|
||||
if (!sdkToUse) {
|
||||
console.error('Could not find an SDK to use for the NAN tests');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (sdks.length) {
|
||||
console.warn(`Multiple SDKs found in the xcode_links directory - using ${sdkToUse}`);
|
||||
}
|
||||
|
||||
platformFlags.push(
|
||||
`-isysroot ${path.resolve(sdkPath, sdkToUse)}`
|
||||
);
|
||||
}
|
||||
|
||||
// TODO(ckerr) this is cribbed from read obj/electron/electron_app.ninja.
|
||||
// Maybe it would be better to have this script literally open up that
|
||||
// file and pull cflags_cc from it instead of using bespoke code here?
|
||||
@@ -41,15 +61,17 @@ async function main () {
|
||||
`-isystem"${path.resolve(BASE, 'buildtools', 'third_party', 'libc++')}"`,
|
||||
`-isystem"${path.resolve(BASE, 'buildtools', 'third_party', 'libc++', 'trunk', 'include')}"`,
|
||||
`-isystem"${path.resolve(BASE, 'buildtools', 'third_party', 'libc++abi', 'trunk', 'include')}"`,
|
||||
'-fPIC'
|
||||
'-fPIC',
|
||||
...platformFlags
|
||||
].join(' ');
|
||||
|
||||
const ldflags = [
|
||||
'-stdlib=libc++',
|
||||
'-fuse-ld=lld',
|
||||
`-L"${path.resolve(BASE, 'out', `${utils.getOutDir({ shouldLog: true })}`, 'obj', 'buildtools', 'third_party', 'libc++abi')}"`,
|
||||
`-L"${path.resolve(BASE, 'out', `${utils.getOutDir({ shouldLog: true })}`, 'obj', 'buildtools', 'third_party', 'libc++')}"`,
|
||||
'-lc++abi'
|
||||
`-L"${path.resolve(BASE, 'out', outDir, 'obj', 'buildtools', 'third_party', 'libc++abi')}"`,
|
||||
`-L"${path.resolve(BASE, 'out', outDir, 'obj', 'buildtools', 'third_party', 'libc++')}"`,
|
||||
'-lc++abi',
|
||||
...platformFlags
|
||||
].join(' ');
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
@@ -66,6 +88,7 @@ async function main () {
|
||||
cwd: NAN_DIR,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
|
||||
if (buildStatus !== 0) {
|
||||
console.error('Failed to build nan test modules');
|
||||
return process.exit(buildStatus);
|
||||
|
||||
@@ -28,6 +28,7 @@ const defaultOptions = [
|
||||
'--mode=debug',
|
||||
'default',
|
||||
`--skip-tests=${DISABLED_TESTS.join(',')}`,
|
||||
'--flaky-tests=dontcare',
|
||||
'--shell',
|
||||
utils.getAbsoluteElectronExec(),
|
||||
'-J'
|
||||
|
||||
@@ -2,11 +2,10 @@ if (!process.env.CI) require('dotenv-safe').load();
|
||||
|
||||
const assert = require('assert');
|
||||
const got = require('got');
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
|
||||
const BUILD_APPVEYOR_URL = 'https://ci.appveyor.com/api/builds';
|
||||
const CIRCLECI_PIPELINE_URL = 'https://circleci.com/api/v2/project/gh/electron/electron/pipeline';
|
||||
const VSTS_URL = 'https://github.visualstudio.com/electron/_apis/build';
|
||||
const DEVOPS_URL = 'https://dev.azure.com/electron-ci/electron/_apis/build';
|
||||
const CIRCLECI_WAIT_TIME = process.env.CIRCLECI_WAIT_TIME || 30000;
|
||||
|
||||
const appVeyorJobs = {
|
||||
@@ -25,13 +24,7 @@ const circleCIPublishIndividualArches = {
|
||||
'linux-publish': ['arm', 'arm64', 'x64']
|
||||
};
|
||||
|
||||
const vstsArmJobs = [
|
||||
'electron-arm-testing',
|
||||
'electron-osx-arm64-testing',
|
||||
'electron-mas-arm64-testing',
|
||||
'electron-arm64-testing',
|
||||
'electron-woa-testing'
|
||||
];
|
||||
const GHAJobs = ['electron-woa-testing'];
|
||||
|
||||
let jobRequestedCount = 0;
|
||||
|
||||
@@ -247,75 +240,28 @@ function buildCircleCI (targetBranch, options) {
|
||||
}
|
||||
}
|
||||
|
||||
async function buildVSTS (targetBranch, options) {
|
||||
assert(options.armTest, `${options.ci} only works with the --armTest option.`);
|
||||
assert(vstsArmJobs.includes(options.job), `Unknown VSTS CI arm test job name: ${options.job}. Valid values are: ${vstsArmJobs}.`);
|
||||
async function buildGHA (targetBranch, options) {
|
||||
const { GHA_TOKEN } = process.env;
|
||||
assert(GHA_TOKEN, `${options.ci} requires the $GHA_TOKEN environment variable to be provided`);
|
||||
|
||||
console.log(`Triggering VSTS to run build on branch: ${targetBranch}.`);
|
||||
const environmentVariables = {};
|
||||
const octokit = new Octokit({ auth: GHA_TOKEN });
|
||||
|
||||
if (options.circleBuildNum) {
|
||||
environmentVariables.CIRCLE_BUILD_NUM = options.circleBuildNum;
|
||||
} else if (options.appveyorJobId) {
|
||||
environmentVariables.APPVEYOR_JOB_ID = options.appveyorJobId;
|
||||
}
|
||||
assert(GHAJobs.includes(options.job), `Unknown GitHub Actions arm test job name: ${options.job}. Valid values are: ${GHAJobs}.`);
|
||||
assert(options.commit !== null, 'commit is a required option for GitHub Actions');
|
||||
|
||||
let vstsURL = VSTS_URL;
|
||||
let vstsToken = process.env.VSTS_TOKEN;
|
||||
assert(vstsToken, `${options.ci} requires the $VSTS_TOKEN environment variable to be provided`);
|
||||
if (options.ci === 'DevOps') {
|
||||
vstsURL = DEVOPS_URL;
|
||||
vstsToken = process.env.DEVOPS_TOKEN;
|
||||
}
|
||||
const requestOpts = {
|
||||
url: `${vstsURL}/definitions?api-version=4.1`,
|
||||
auth: {
|
||||
user: '',
|
||||
password: vstsToken
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
console.log(`Triggering GitHub Actions to run build on branch: ${targetBranch}.`);
|
||||
|
||||
jobRequestedCount++;
|
||||
|
||||
try {
|
||||
const vstsResponse = await makeRequest(requestOpts, true);
|
||||
const buildToRun = vstsResponse.value.find(build => build.name === options.job);
|
||||
callVSTSBuild(buildToRun, targetBranch, environmentVariables, vstsURL, vstsToken);
|
||||
const response = await octokit.request('POST /repos/electron/electron/actions/workflows/electron_woa_testing.yml/dispatches', {
|
||||
ref: targetBranch,
|
||||
inputs: {
|
||||
appveyor_job_id: `${options.appveyorJobId}`
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.log('Problem calling VSTS to get build definitions: ', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function callVSTSBuild (build, targetBranch, environmentVariables, vstsURL, vstsToken) {
|
||||
const buildBody = {
|
||||
definition: build,
|
||||
sourceBranch: targetBranch,
|
||||
priority: 'high'
|
||||
};
|
||||
if (Object.keys(environmentVariables).length !== 0) {
|
||||
buildBody.parameters = JSON.stringify(environmentVariables);
|
||||
}
|
||||
const requestOpts = {
|
||||
url: `${vstsURL}/builds?api-version=4.1`,
|
||||
auth: {
|
||||
user: '',
|
||||
password: vstsToken
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(buildBody),
|
||||
method: 'POST'
|
||||
};
|
||||
|
||||
try {
|
||||
const { _links } = await makeRequest(requestOpts, true);
|
||||
console.log(`VSTS release build request for ${build.name} successful. Check ${_links.web.href} for status.`);
|
||||
} catch (err) {
|
||||
console.log(`Could not call VSTS for job ${build.name}: `, err);
|
||||
console.log('Problem calling GitHub Actions to get build definitions: ', err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,9 +276,8 @@ function runRelease (targetBranch, options) {
|
||||
buildAppVeyor(targetBranch, options);
|
||||
break;
|
||||
}
|
||||
case 'DevOps':
|
||||
case 'VSTS': {
|
||||
buildVSTS(targetBranch, options);
|
||||
case 'GHA': {
|
||||
buildGHA(targetBranch, options);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -351,13 +296,13 @@ module.exports = runRelease;
|
||||
|
||||
if (require.main === module) {
|
||||
const args = require('minimist')(process.argv.slice(2), {
|
||||
boolean: ['ghRelease', 'armTest']
|
||||
boolean: ['ghRelease']
|
||||
});
|
||||
const targetBranch = args._[0];
|
||||
if (args._.length < 1) {
|
||||
console.log(`Trigger CI to build release builds of electron.
|
||||
Usage: ci-release-build.js [--job=CI_JOB_NAME] [--arch=INDIVIDUAL_ARCH] [--ci=CircleCI|AppVeyor|VSTS|DevOps]
|
||||
[--ghRelease] [--armTest] [--circleBuildNum=xxx] [--appveyorJobId=xxx] [--commit=sha] TARGET_BRANCH
|
||||
Usage: ci-release-build.js [--job=CI_JOB_NAME] [--arch=INDIVIDUAL_ARCH] [--ci=CircleCI|AppVeyor|GHA]
|
||||
[--ghRelease] [--circleBuildNum=xxx] [--appveyorJobId=xxx] [--commit=sha] TARGET_BRANCH
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
"""A wrapper script around clang-format, suitable for linting multiple files
|
||||
and to use for continuous integration.
|
||||
|
||||
This is an alternative API for the clang-format command line.
|
||||
It runs over multiple files and directories in parallel.
|
||||
A diff output is produced and a sensible exit code is returned.
|
||||
@@ -11,6 +12,7 @@ from __future__ import print_function, unicode_literals
|
||||
import argparse
|
||||
import codecs
|
||||
import difflib
|
||||
import errno
|
||||
import fnmatch
|
||||
import io
|
||||
import multiprocessing
|
||||
@@ -26,13 +28,28 @@ from functools import partial
|
||||
from lib.util import get_buildtools_executable
|
||||
|
||||
DEFAULT_EXTENSIONS = 'c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx,mm'
|
||||
|
||||
DEFAULT_CLANG_FORMAT_IGNORE = '.clang-format-ignore'
|
||||
|
||||
class ExitStatus:
|
||||
SUCCESS = 0
|
||||
DIFF = 1
|
||||
TROUBLE = 2
|
||||
|
||||
def excludes_from_file(ignore_file):
|
||||
excludes = []
|
||||
try:
|
||||
with io.open(ignore_file, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
pattern = line.rstrip()
|
||||
if not pattern:
|
||||
continue
|
||||
excludes.append(pattern)
|
||||
except EnvironmentError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
return excludes
|
||||
|
||||
def list_files(files, recursive=False, extensions=None, exclude=None):
|
||||
if extensions is None:
|
||||
@@ -78,15 +95,13 @@ def make_diff(diff_file, original, reformatted):
|
||||
|
||||
class DiffError(Exception):
|
||||
def __init__(self, message, errs=None):
|
||||
# pylint: disable=R1725
|
||||
super(DiffError, self).__init__(message)
|
||||
super().__init__(message)
|
||||
self.errs = errs or []
|
||||
|
||||
|
||||
class UnexpectedError(Exception):
|
||||
def __init__(self, message, exc=None):
|
||||
# pylint: disable=R1725
|
||||
super(UnexpectedError, self).__init__(message)
|
||||
super().__init__(message)
|
||||
self.formatted_traceback = traceback.format_exc()
|
||||
self.exc = exc
|
||||
|
||||
@@ -113,6 +128,11 @@ def run_clang_format_diff(args, file_name):
|
||||
invocation = [args.clang_format_executable, file_name]
|
||||
if args.fix:
|
||||
invocation.append('-i')
|
||||
if args.style:
|
||||
invocation.extend(['--style', args.style])
|
||||
if args.dry_run:
|
||||
print(" ".join(invocation))
|
||||
return [], []
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
' '.join(invocation),
|
||||
@@ -122,19 +142,13 @@ def run_clang_format_diff(args, file_name):
|
||||
shell=True)
|
||||
except OSError as exc:
|
||||
# pylint: disable=W0707
|
||||
raise DiffError(str(exc))
|
||||
proc_stdout = proc.stdout
|
||||
proc_stderr = proc.stderr
|
||||
if sys.version_info[0] == 3:
|
||||
proc_stdout = proc_stdout.detach()
|
||||
proc_stderr = proc_stderr.detach()
|
||||
# make the pipes compatible with Python 3,
|
||||
# reading lines should output unicode
|
||||
encoding = 'utf-8'
|
||||
proc_stdout = codecs.getreader(encoding)(proc_stdout)
|
||||
proc_stderr = codecs.getreader(encoding)(proc_stderr)
|
||||
outs = list(proc_stdout.readlines())
|
||||
errs = list(proc_stderr.readlines())
|
||||
raise DiffError(
|
||||
"Command '{}' failed to start: {}".format(
|
||||
subprocess.list2cmdline(invocation), exc
|
||||
)
|
||||
)
|
||||
outs = list(proc.stdout.readlines())
|
||||
errs = list(proc.stderr.readlines())
|
||||
proc.wait()
|
||||
if proc.returncode:
|
||||
raise DiffError("clang-format exited with status {}: '{}'".format(
|
||||
@@ -213,6 +227,11 @@ def main():
|
||||
'--recursive',
|
||||
action='store_true',
|
||||
help='run recursively over directories')
|
||||
parser.add_argument(
|
||||
'-d',
|
||||
'--dry-run',
|
||||
action='store_true',
|
||||
help='just print the list of files')
|
||||
parser.add_argument('files', metavar='file', nargs='+')
|
||||
parser.add_argument(
|
||||
'-q',
|
||||
@@ -243,6 +262,10 @@ def main():
|
||||
default=[],
|
||||
help='exclude paths matching the given glob-like pattern(s)'
|
||||
' from recursive search')
|
||||
parser.add_argument(
|
||||
'--style',
|
||||
help='formatting style to apply '
|
||||
'(LLVM/Google/Chromium/Mozilla/WebKit)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -270,13 +293,14 @@ def main():
|
||||
|
||||
parse_files = []
|
||||
if args.changed:
|
||||
popen = subprocess.Popen(
|
||||
'git diff --name-only --cached',
|
||||
stdout = subprocess.Popen(
|
||||
"git diff --name-only --cached",
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
shell=True
|
||||
)
|
||||
for line in popen.stdout:
|
||||
shell=True,
|
||||
universal_newlines=True
|
||||
).communicate()[0].split("\n")
|
||||
for line in stdout:
|
||||
file_name = line.rstrip()
|
||||
# don't check deleted files
|
||||
if os.path.isfile(file_name):
|
||||
@@ -285,14 +309,17 @@ def main():
|
||||
else:
|
||||
parse_files = args.files
|
||||
|
||||
excludes = excludes_from_file(DEFAULT_CLANG_FORMAT_IGNORE)
|
||||
excludes.extend(args.exclude)
|
||||
|
||||
files = list_files(
|
||||
parse_files,
|
||||
recursive=args.recursive,
|
||||
exclude=args.exclude,
|
||||
exclude=excludes,
|
||||
extensions=args.extensions.split(','))
|
||||
|
||||
if not files:
|
||||
return 0
|
||||
return ExitStatus.SUCCESS
|
||||
|
||||
njobs = args.j
|
||||
if njobs == 0:
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
"Tarball": "debian_bullseye_arm_sysroot.tar.xz"
|
||||
},
|
||||
"bullseye_arm64": {
|
||||
"Sha1Sum": "de38cc85d51a820c3307e1937f3c2d3cedcce988",
|
||||
"Sha1Sum": "5a56c1ef714154ea5003bcafb16f21b0f8dde023",
|
||||
"SysrootDir": "debian_bullseye_arm64-sysroot",
|
||||
"Tarball": "debian_bullseye_arm64_sysroot.tar.xz"
|
||||
"Tarball": "debian_sid_arm64_sysroot.tar.xz"
|
||||
},
|
||||
"bullseye_armel": {
|
||||
"Sha1Sum": "db15aab39af3cfbc55a8ff0386943db1b78a1eab",
|
||||
|
||||
@@ -22,17 +22,18 @@
|
||||
#include "ppapi/buildflags/buildflags.h"
|
||||
#include "shell/common/electron_paths.h"
|
||||
#include "shell/common/options_switches.h"
|
||||
#include "third_party/widevine/cdm/buildflags.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
#include "url/url_constants.h"
|
||||
// In SHARED_INTERMEDIATE_DIR.
|
||||
#include "widevine_cdm_version.h" // NOLINT(build/include_directory)
|
||||
|
||||
#if defined(WIDEVINE_CDM_AVAILABLE)
|
||||
#if BUILDFLAG(ENABLE_WIDEVINE)
|
||||
#include "base/native_library.h"
|
||||
#include "content/public/common/cdm_info.h"
|
||||
#include "media/base/video_codecs.h"
|
||||
#endif // defined(WIDEVINE_CDM_AVAILABLE)
|
||||
#endif // BUILDFLAG(ENABLE_WIDEVINE)
|
||||
|
||||
#if BUILDFLAG(ENABLE_PDF_VIEWER)
|
||||
#include "chrome/common/pdf_util.h"
|
||||
@@ -58,7 +59,7 @@ enum class WidevineCdmFileCheck {
|
||||
kNotFound,
|
||||
};
|
||||
|
||||
#if defined(WIDEVINE_CDM_AVAILABLE)
|
||||
#if BUILDFLAG(ENABLE_WIDEVINE)
|
||||
bool IsWidevineAvailable(
|
||||
base::FilePath* cdm_path,
|
||||
std::vector<media::VideoCodec>* codecs_supported,
|
||||
@@ -102,7 +103,7 @@ bool IsWidevineAvailable(
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif // defined(WIDEVINE_CDM_AVAILABLE)
|
||||
#endif // BUILDFLAG(ENABLE_WIDEVINE)
|
||||
|
||||
#if BUILDFLAG(ENABLE_PLUGINS)
|
||||
void ComputeBuiltInPlugins(std::vector<content::PepperPluginInfo>* plugins) {
|
||||
@@ -227,7 +228,7 @@ void ElectronContentClient::AddContentDecryptionModules(
|
||||
std::vector<content::CdmInfo>* cdms,
|
||||
std::vector<media::CdmHostFilePath>* cdm_host_file_paths) {
|
||||
if (cdms) {
|
||||
#if defined(WIDEVINE_CDM_AVAILABLE)
|
||||
#if BUILDFLAG(ENABLE_WIDEVINE)
|
||||
base::FilePath cdm_path;
|
||||
std::vector<media::VideoCodec> video_codecs_supported;
|
||||
base::flat_set<media::CdmSessionType> session_types_supported;
|
||||
@@ -252,7 +253,7 @@ void ElectronContentClient::AddContentDecryptionModules(
|
||||
kWidevineCdmDisplayName, kWidevineCdmGuid, version, cdm_path,
|
||||
kWidevineCdmFileSystemId, capability, kWidevineKeySystem, false));
|
||||
}
|
||||
#endif // defined(WIDEVINE_CDM_AVAILABLE)
|
||||
#endif // BUILDFLAG(ENABLE_WIDEVINE)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,9 @@ bool ElectronCrashReporterClient::GetShouldCompressUploads() {
|
||||
|
||||
void ElectronCrashReporterClient::GetProcessSimpleAnnotations(
|
||||
std::map<std::string, std::string>* annotations) {
|
||||
*annotations = global_annotations_;
|
||||
for (auto&& pair : global_annotations_) {
|
||||
(*annotations)[pair.first] = pair.second;
|
||||
}
|
||||
(*annotations)["prod"] = ELECTRON_PRODUCT_NAME;
|
||||
(*annotations)["ver"] = ELECTRON_VERSION_STRING;
|
||||
}
|
||||
|
||||
@@ -1152,7 +1152,9 @@ bool App::Relaunch(gin::Arguments* js_args) {
|
||||
|
||||
gin_helper::Dictionary options;
|
||||
if (js_args->GetNext(&options)) {
|
||||
if (options.Get("execPath", &exec_path) || options.Get("args", &args))
|
||||
bool has_exec_path = options.Get("execPath", &exec_path);
|
||||
bool has_args = options.Get("args", &args);
|
||||
if (has_exec_path || has_args)
|
||||
override_argv = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
|
||||
if (!item) {
|
||||
CGFloat windowBottom = CGRectGetMinY([view window].frame);
|
||||
CGFloat lowestMenuPoint = windowBottom + position.y - [menu size].height;
|
||||
CGFloat screenBottom = CGRectGetMinY([view window].screen.frame);
|
||||
CGFloat screenBottom = CGRectGetMinY([view window].screen.visibleFrame);
|
||||
CGFloat distanceFromBottom = lowestMenuPoint - screenBottom;
|
||||
if (distanceFromBottom < 0)
|
||||
position.y = position.y - distanceFromBottom + 4;
|
||||
@@ -143,7 +143,7 @@ void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
|
||||
// Place the menu left of cursor if it is overflowing off right of screen.
|
||||
CGFloat windowLeft = CGRectGetMinX([view window].frame);
|
||||
CGFloat rightmostMenuPoint = windowLeft + position.x + [menu size].width;
|
||||
CGFloat screenRight = CGRectGetMaxX([view window].screen.frame);
|
||||
CGFloat screenRight = CGRectGetMaxX([view window].screen.visibleFrame);
|
||||
if (rightmostMenuPoint > screenRight)
|
||||
position.x = position.x - [menu size].width;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
|
||||
#include "chrome/browser/ui/views/eye_dropper/eye_dropper.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "components/embedder_support/user_agent_utils.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
#include "components/prefs/scoped_user_pref_update.h"
|
||||
#include "components/security_state/content/content_utils.h"
|
||||
@@ -419,21 +420,34 @@ bool IsDeviceNameValid(const std::u16string& device_name) {
|
||||
bool printer_exists = new_printer != nullptr;
|
||||
PMRelease(new_printer);
|
||||
return printer_exists;
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
printing::ScopedPrinterHandle printer;
|
||||
return printer.OpenPrinterWithName(base::as_wcstr(device_name));
|
||||
#else
|
||||
return true;
|
||||
scoped_refptr<printing::PrintBackend> print_backend =
|
||||
printing::PrintBackend::CreateInstance(
|
||||
g_browser_process->GetApplicationLocale());
|
||||
return print_backend->IsValidPrinter(base::UTF16ToUTF8(device_name));
|
||||
#endif
|
||||
}
|
||||
|
||||
std::pair<std::string, std::u16string> GetDefaultPrinterAsync() {
|
||||
// This function returns a validated device name.
|
||||
// If the user passed one to webContents.print(), we check that it's valid and
|
||||
// return it or fail if the network doesn't recognize it. If the user didn't
|
||||
// pass a device name, we first try to return the system default printer. If one
|
||||
// isn't set, then pull all the printers and use the first one or fail if none
|
||||
// exist.
|
||||
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
||||
const std::u16string& device_name) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Blocking is needed here because Windows printer drivers are oftentimes
|
||||
// not thread-safe and have to be accessed on the UI thread.
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
#endif
|
||||
|
||||
if (!device_name.empty()) {
|
||||
if (!IsDeviceNameValid(device_name))
|
||||
return std::make_pair("Invalid deviceName provided", std::u16string());
|
||||
return std::make_pair(std::string(), device_name);
|
||||
}
|
||||
|
||||
scoped_refptr<printing::PrintBackend> print_backend =
|
||||
printing::PrintBackend::CreateInstance(
|
||||
g_browser_process->GetApplicationLocale());
|
||||
@@ -446,14 +460,16 @@ std::pair<std::string, std::u16string> GetDefaultPrinterAsync() {
|
||||
if (code != printing::mojom::ResultCode::kSuccess)
|
||||
LOG(ERROR) << "Failed to get default printer name";
|
||||
|
||||
// Check for existing printers and pick the first one should it exist.
|
||||
if (printer_name.empty()) {
|
||||
printing::PrinterList printers;
|
||||
if (print_backend->EnumeratePrinters(&printers) !=
|
||||
printing::mojom::ResultCode::kSuccess)
|
||||
return std::make_pair("Failed to enumerate printers", std::u16string());
|
||||
if (!printers.empty())
|
||||
printer_name = printers.front().printer_name;
|
||||
if (printers.empty())
|
||||
return std::make_pair("No printers available on the network",
|
||||
std::u16string());
|
||||
|
||||
printer_name = printers.front().printer_name;
|
||||
}
|
||||
|
||||
return std::make_pair(std::string(), base::UTF8ToUTF16(printer_name));
|
||||
@@ -660,9 +676,8 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||
auto session = Session::CreateFrom(isolate, GetBrowserContext());
|
||||
session_.Reset(isolate, session.ToV8());
|
||||
|
||||
web_contents->SetUserAgentOverride(blink::UserAgentOverride::UserAgentOnly(
|
||||
GetBrowserContext()->GetUserAgent()),
|
||||
false);
|
||||
SetUserAgent(GetBrowserContext()->GetUserAgent());
|
||||
|
||||
web_contents->SetUserData(kElectronApiWebContentsKey,
|
||||
std::make_unique<UserDataLink>(GetWeakPtr()));
|
||||
InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate));
|
||||
@@ -868,9 +883,7 @@ void WebContents::InitWithSessionAndOptions(
|
||||
|
||||
AutofillDriverFactory::CreateForWebContents(web_contents());
|
||||
|
||||
web_contents()->SetUserAgentOverride(blink::UserAgentOverride::UserAgentOnly(
|
||||
GetBrowserContext()->GetUserAgent()),
|
||||
false);
|
||||
SetUserAgent(GetBrowserContext()->GetUserAgent());
|
||||
|
||||
if (IsGuest()) {
|
||||
NativeWindow* owner_window = nullptr;
|
||||
@@ -1327,6 +1340,8 @@ void WebContents::OnEnterFullscreenModeForTab(
|
||||
return;
|
||||
}
|
||||
|
||||
owner_window()->set_fullscreen_transition_type(
|
||||
NativeWindow::FullScreenTransitionType::HTML);
|
||||
exclusive_access_manager_->fullscreen_controller()->EnterFullscreenModeForTab(
|
||||
requesting_frame, options.display_id);
|
||||
|
||||
@@ -2157,8 +2172,7 @@ void WebContents::LoadURL(const GURL& url,
|
||||
|
||||
std::string user_agent;
|
||||
if (options.Get("userAgent", &user_agent))
|
||||
web_contents()->SetUserAgentOverride(
|
||||
blink::UserAgentOverride::UserAgentOnly(user_agent), false);
|
||||
SetUserAgent(user_agent);
|
||||
|
||||
std::string extra_headers;
|
||||
if (options.Get("extraHeaders", &extra_headers))
|
||||
@@ -2373,8 +2387,12 @@ void WebContents::ForcefullyCrashRenderer() {
|
||||
}
|
||||
|
||||
void WebContents::SetUserAgent(const std::string& user_agent) {
|
||||
web_contents()->SetUserAgentOverride(
|
||||
blink::UserAgentOverride::UserAgentOnly(user_agent), false);
|
||||
blink::UserAgentOverride ua_override;
|
||||
ua_override.ua_string_override = user_agent;
|
||||
if (!user_agent.empty())
|
||||
ua_override.ua_metadata_override = embedder_support::GetUserAgentMetadata();
|
||||
|
||||
web_contents()->SetUserAgentOverride(ua_override, false);
|
||||
}
|
||||
|
||||
std::string WebContents::GetUserAgent() {
|
||||
@@ -2593,12 +2611,11 @@ bool WebContents::IsCurrentlyAudible() {
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
void WebContents::OnGetDefaultPrinter(
|
||||
void WebContents::OnGetDeviceNameToUse(
|
||||
base::Value::Dict print_settings,
|
||||
printing::CompletionCallback print_callback,
|
||||
std::u16string device_name,
|
||||
bool silent,
|
||||
// <error, default_printer>
|
||||
// <error, device_name>
|
||||
std::pair<std::string, std::u16string> info) {
|
||||
// The content::WebContents might be already deleted at this point, and the
|
||||
// PrintViewManagerElectron class does not do null check.
|
||||
@@ -2615,16 +2632,7 @@ void WebContents::OnGetDefaultPrinter(
|
||||
}
|
||||
|
||||
// If the user has passed a deviceName use it, otherwise use default printer.
|
||||
std::u16string printer_name = device_name.empty() ? info.second : device_name;
|
||||
|
||||
// If there are no valid printers available on the network, we bail.
|
||||
if (printer_name.empty() || !IsDeviceNameValid(printer_name)) {
|
||||
if (print_callback)
|
||||
std::move(print_callback).Run(false, "no valid printers available");
|
||||
return;
|
||||
}
|
||||
|
||||
print_settings.Set(printing::kSettingDeviceName, printer_name);
|
||||
print_settings.Set(printing::kSettingDeviceName, info.second);
|
||||
|
||||
auto* print_view_manager =
|
||||
PrintViewManagerElectron::FromWebContents(web_contents());
|
||||
@@ -2714,11 +2722,6 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
// Printer device name as opened by the OS.
|
||||
std::u16string device_name;
|
||||
options.Get("deviceName", &device_name);
|
||||
if (!device_name.empty() && !IsDeviceNameValid(device_name)) {
|
||||
gin_helper::ErrorThrower(args->isolate())
|
||||
.ThrowError("webContents.print(): Invalid deviceName provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
int scale_factor = 100;
|
||||
options.Get("scaleFactor", &scale_factor);
|
||||
@@ -2808,10 +2811,10 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
}
|
||||
|
||||
print_task_runner_->PostTaskAndReplyWithResult(
|
||||
FROM_HERE, base::BindOnce(&GetDefaultPrinterAsync),
|
||||
base::BindOnce(&WebContents::OnGetDefaultPrinter,
|
||||
FROM_HERE, base::BindOnce(&GetDeviceNameToUse, device_name),
|
||||
base::BindOnce(&WebContents::OnGetDeviceNameToUse,
|
||||
weak_factory_.GetWeakPtr(), std::move(settings),
|
||||
std::move(callback), device_name, silent));
|
||||
std::move(callback), silent));
|
||||
}
|
||||
|
||||
v8::Local<v8::Promise> WebContents::PrintToPDF(base::DictionaryValue settings) {
|
||||
@@ -3518,7 +3521,15 @@ void WebContents::EnumerateDirectory(
|
||||
|
||||
bool WebContents::IsFullscreenForTabOrPending(
|
||||
const content::WebContents* source) {
|
||||
return html_fullscreen_;
|
||||
if (!owner_window())
|
||||
return html_fullscreen_;
|
||||
|
||||
bool in_transition = owner_window()->fullscreen_transition_state() !=
|
||||
NativeWindow::FullScreenTransitionState::NONE;
|
||||
bool is_html_transition = owner_window()->fullscreen_transition_type() ==
|
||||
NativeWindow::FullScreenTransitionType::HTML;
|
||||
|
||||
return html_fullscreen_ || (in_transition && is_html_transition);
|
||||
}
|
||||
|
||||
bool WebContents::TakeFocus(content::WebContents* source, bool reverse) {
|
||||
@@ -3840,9 +3851,8 @@ void WebContents::SetHtmlApiFullscreen(bool enter_fullscreen) {
|
||||
? !web_preferences->ShouldDisableHtmlFullscreenWindowResize()
|
||||
: true;
|
||||
|
||||
if (html_fullscreenable) {
|
||||
if (html_fullscreenable)
|
||||
owner_window_->SetFullScreen(enter_fullscreen);
|
||||
}
|
||||
|
||||
UpdateHtmlApiFullscreen(enter_fullscreen);
|
||||
native_fullscreen_ = false;
|
||||
|
||||
@@ -222,12 +222,11 @@ class WebContents : public ExclusiveAccessContext,
|
||||
void HandleNewRenderFrame(content::RenderFrameHost* render_frame_host);
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
void OnGetDefaultPrinter(base::Value::Dict print_settings,
|
||||
printing::CompletionCallback print_callback,
|
||||
std::u16string device_name,
|
||||
bool silent,
|
||||
// <error, default_printer_name>
|
||||
std::pair<std::string, std::u16string> info);
|
||||
void OnGetDeviceNameToUse(base::Value::Dict print_settings,
|
||||
printing::CompletionCallback print_callback,
|
||||
bool silent,
|
||||
// <error, device_name>
|
||||
std::pair<std::string, std::u16string> info);
|
||||
void Print(gin::Arguments* args);
|
||||
// Print current page as PDF.
|
||||
v8::Local<v8::Promise> PrintToPDF(base::DictionaryValue settings);
|
||||
|
||||
@@ -189,13 +189,25 @@ const mojo::Remote<mojom::ElectronRenderer>& WebFrameMain::GetRendererApi() {
|
||||
}
|
||||
|
||||
void WebFrameMain::MaybeSetupMojoConnection() {
|
||||
if (render_frame_disposed_) {
|
||||
// RFH may not be set yet if called between when a new RFH is created and
|
||||
// before it's been swapped with an old RFH.
|
||||
LOG(INFO) << "Attempt to setup WebFrameMain connection while render frame "
|
||||
"is disposed";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!renderer_api_) {
|
||||
pending_receiver_ = renderer_api_.BindNewPipeAndPassReceiver();
|
||||
renderer_api_.set_disconnect_handler(base::BindOnce(
|
||||
&WebFrameMain::OnRendererConnectionError, weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
DCHECK(render_frame_);
|
||||
|
||||
// Wait for RenderFrame to be created in renderer before accessing remote.
|
||||
if (pending_receiver_ && render_frame_->IsRenderFrameCreated()) {
|
||||
if (pending_receiver_ && render_frame_ &&
|
||||
render_frame_->IsRenderFrameCreated()) {
|
||||
render_frame_->GetRemoteInterfaces()->GetInterface(
|
||||
std::move(pending_receiver_));
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "chrome/common/chrome_version.h"
|
||||
#include "components/embedder_support/user_agent_utils.h"
|
||||
#include "components/net_log/chrome_net_log.h"
|
||||
#include "components/network_hints/common/network_hints.mojom.h"
|
||||
#include "content/browser/keyboard_lock/keyboard_lock_service_impl.h" // nogncheck
|
||||
@@ -454,6 +455,9 @@ void ElectronBrowserClient::RenderProcessWillLaunch(
|
||||
new extensions::MessagingAPIMessageFilter(process_id, browser_context));
|
||||
#endif
|
||||
|
||||
// Remove in case the host is reused after a crash, otherwise noop.
|
||||
host->RemoveObserver(this);
|
||||
|
||||
// ensure the ProcessPreferences is removed later
|
||||
host->AddObserver(this);
|
||||
}
|
||||
@@ -1161,6 +1165,10 @@ void ElectronBrowserClient::SetUserAgent(const std::string& user_agent) {
|
||||
user_agent_override_ = user_agent;
|
||||
}
|
||||
|
||||
blink::UserAgentMetadata ElectronBrowserClient::GetUserAgentMetadata() {
|
||||
return embedder_support::GetUserAgentMetadata();
|
||||
}
|
||||
|
||||
void ElectronBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories(
|
||||
int frame_tree_node_id,
|
||||
ukm::SourceIdObj ukm_source_id,
|
||||
|
||||
@@ -93,6 +93,7 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||
|
||||
std::string GetUserAgent() override;
|
||||
void SetUserAgent(const std::string& user_agent);
|
||||
blink::UserAgentMetadata GetUserAgentMetadata() override;
|
||||
|
||||
content::SerialDelegate* GetSerialDelegate() override;
|
||||
|
||||
|
||||
@@ -108,8 +108,6 @@ ElectronBrowserContext::ElectronBrowserContext(const std::string& partition,
|
||||
protocol_registry_(base::WrapUnique(new ProtocolRegistry)),
|
||||
in_memory_(in_memory),
|
||||
ssl_config_(network::mojom::SSLConfig::New()) {
|
||||
user_agent_ = ElectronBrowserClient::Get()->GetUserAgent();
|
||||
|
||||
// Read options.
|
||||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||
use_cache_ = !command_line->HasSwitch(switches::kDisableHttpCache);
|
||||
@@ -307,7 +305,7 @@ ElectronBrowserContext::GetSpecialStoragePolicy() {
|
||||
}
|
||||
|
||||
std::string ElectronBrowserContext::GetUserAgent() const {
|
||||
return user_agent_;
|
||||
return user_agent_.value_or(ElectronBrowserClient::Get()->GetUserAgent());
|
||||
}
|
||||
|
||||
predictors::PreconnectManager* ElectronBrowserContext::GetPreconnectManager() {
|
||||
|
||||
@@ -170,7 +170,7 @@ class ElectronBrowserContext : public content::BrowserContext {
|
||||
std::unique_ptr<predictors::PreconnectManager> preconnect_manager_;
|
||||
std::unique_ptr<ProtocolRegistry> protocol_registry_;
|
||||
|
||||
std::string user_agent_;
|
||||
absl::optional<std::string> user_agent_;
|
||||
base::FilePath path_;
|
||||
bool in_memory_ = false;
|
||||
bool use_cache_ = true;
|
||||
|
||||
@@ -218,6 +218,7 @@ int ElectronBrowserMainParts::PreEarlyInitialization() {
|
||||
HandleSIGCHLD();
|
||||
#endif
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
DetectOzonePlatform();
|
||||
ui::OzonePlatform::PreEarlyInitialization();
|
||||
#endif
|
||||
|
||||
@@ -376,6 +377,10 @@ void ElectronBrowserMainParts::ToolkitInitialized() {
|
||||
electron::UninitializeElectron_gtk();
|
||||
}
|
||||
|
||||
electron::InitializeElectron_gdk_pixbuf(gtk::GetLibGdkPixbuf());
|
||||
CHECK(electron::IsElectron_gdk_pixbufInitialized())
|
||||
<< "Failed to initialize libgdk_pixbuf-2.0.so.0";
|
||||
|
||||
// Chromium does not respect GTK dark theme setting, but they may change
|
||||
// in future and this code might be no longer needed. Check the Chromium
|
||||
// issue to keep updated:
|
||||
|
||||
@@ -122,6 +122,10 @@ class ElectronBrowserMainParts : public content::BrowserMainParts {
|
||||
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
void DetectOzonePlatform();
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
void FreeAppDelegate();
|
||||
void RegisterURLHandler();
|
||||
|
||||
135
shell/browser/electron_browser_main_parts_linux.cc
Normal file
135
shell/browser/electron_browser_main_parts_linux.cc
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright (c) 2022 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/electron_browser_main_parts.h"
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/environment.h"
|
||||
#include "ui/ozone/buildflags.h"
|
||||
#include "ui/ozone/public/ozone_switches.h"
|
||||
|
||||
#if BUILDFLAG(OZONE_PLATFORM_WAYLAND)
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/nix/xdg_util.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(OZONE_PLATFORM_WAYLAND)
|
||||
|
||||
constexpr char kPlatformWayland[] = "wayland";
|
||||
|
||||
bool HasWaylandDisplay(base::Environment* env) {
|
||||
std::string wayland_display;
|
||||
const bool has_wayland_display =
|
||||
env->GetVar("WAYLAND_DISPLAY", &wayland_display) &&
|
||||
!wayland_display.empty();
|
||||
if (has_wayland_display)
|
||||
return true;
|
||||
|
||||
std::string xdg_runtime_dir;
|
||||
const bool has_xdg_runtime_dir =
|
||||
env->GetVar("XDG_RUNTIME_DIR", &xdg_runtime_dir) &&
|
||||
!xdg_runtime_dir.empty();
|
||||
if (has_xdg_runtime_dir) {
|
||||
auto wayland_server_pipe =
|
||||
base::FilePath(xdg_runtime_dir).Append("wayland-0");
|
||||
// Normally, this should happen exactly once, at the startup of the main
|
||||
// process.
|
||||
base::ScopedAllowBlocking allow_blocking;
|
||||
return base::PathExists(wayland_server_pipe);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // BUILDFLAG(OZONE_PLATFORM_WAYLAND)
|
||||
|
||||
#if BUILDFLAG(OZONE_PLATFORM_X11)
|
||||
constexpr char kPlatformX11[] = "x11";
|
||||
#endif
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
// Evaluates the environment and returns the effective platform name for the
|
||||
// given |ozone_platform_hint|.
|
||||
// For the "auto" value, returns "wayland" if the XDG session type is "wayland",
|
||||
// "x11" otherwise.
|
||||
// For the "wayland" value, checks if the Wayland server is available, and
|
||||
// returns "x11" if it is not.
|
||||
// See https://crbug.com/1246928.
|
||||
std::string MaybeFixPlatformName(const std::string& ozone_platform_hint) {
|
||||
#if BUILDFLAG(OZONE_PLATFORM_WAYLAND)
|
||||
// Wayland is selected if both conditions below are true:
|
||||
// 1. The user selected either 'wayland' or 'auto'.
|
||||
// 2. The XDG session type is 'wayland', OR the user has selected 'wayland'
|
||||
// explicitly and a Wayland server is running.
|
||||
// Otherwise, fall back to X11.
|
||||
if (ozone_platform_hint == kPlatformWayland ||
|
||||
ozone_platform_hint == "auto") {
|
||||
auto env(base::Environment::Create());
|
||||
|
||||
std::string xdg_session_type;
|
||||
const bool has_xdg_session_type =
|
||||
env->GetVar(base::nix::kXdgSessionTypeEnvVar, &xdg_session_type) &&
|
||||
!xdg_session_type.empty();
|
||||
|
||||
if ((has_xdg_session_type && xdg_session_type == "wayland") ||
|
||||
(ozone_platform_hint == kPlatformWayland &&
|
||||
HasWaylandDisplay(env.get()))) {
|
||||
return kPlatformWayland;
|
||||
}
|
||||
}
|
||||
#endif // BUILDFLAG(OZONE_PLATFORM_WAYLAND)
|
||||
|
||||
#if BUILDFLAG(OZONE_PLATFORM_X11)
|
||||
if (ozone_platform_hint == kPlatformX11) {
|
||||
return kPlatformX11;
|
||||
}
|
||||
#if BUILDFLAG(OZONE_PLATFORM_WAYLAND)
|
||||
if (ozone_platform_hint == kPlatformWayland ||
|
||||
ozone_platform_hint == "auto") {
|
||||
// We are here if:
|
||||
// - The binary has both X11 and Wayland backends.
|
||||
// - The user wanted Wayland but that did not work, otherwise it would have
|
||||
// been returned above.
|
||||
if (ozone_platform_hint == kPlatformWayland) {
|
||||
LOG(WARNING) << "No Wayland server is available. Falling back to X11.";
|
||||
} else {
|
||||
LOG(WARNING) << "This is not a Wayland session. Falling back to X11. "
|
||||
"If you need to run Chrome on Wayland using some "
|
||||
"embedded compositor, e. g., Weston, please specify "
|
||||
"Wayland as your preferred Ozone platform, or use "
|
||||
"--ozone-platform=wayland.";
|
||||
}
|
||||
return kPlatformX11;
|
||||
}
|
||||
#endif // BUILDFLAG(OZONE_PLATFORM_WAYLAND)
|
||||
#endif // BUILDFLAG(OZONE_PLATFORM_X11)
|
||||
|
||||
return ozone_platform_hint;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ElectronBrowserMainParts::DetectOzonePlatform() {
|
||||
auto* const command_line = base::CommandLine::ForCurrentProcess();
|
||||
if (!command_line->HasSwitch(switches::kOzonePlatform)) {
|
||||
const auto ozone_platform_hint =
|
||||
command_line->GetSwitchValueASCII(switches::kOzonePlatformHint);
|
||||
if (!ozone_platform_hint.empty()) {
|
||||
command_line->AppendSwitchASCII(
|
||||
switches::kOzonePlatform, MaybeFixPlatformName(ozone_platform_hint));
|
||||
}
|
||||
}
|
||||
|
||||
auto env = base::Environment::Create();
|
||||
std::string desktop_startup_id;
|
||||
if (env->GetVar("DESKTOP_STARTUP_ID", &desktop_startup_id))
|
||||
command_line->AppendSwitchASCII("desktop-startup-id", desktop_startup_id);
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
@@ -31,6 +31,16 @@
|
||||
#include "shell/common/gin_converters/file_path_converter.h"
|
||||
#include "shell/common/options_switches.h"
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
#include <vector>
|
||||
|
||||
#include "base/i18n/case_conversion.h"
|
||||
#include "base/win/registry.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/shell_dialogs/execute_select_file_win.h"
|
||||
#include "ui/strings/grit/ui_strings.h"
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
@@ -63,6 +73,112 @@ base::FilePath CreateDownloadPath(const GURL& url,
|
||||
return download_path.Append(generated_name);
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
// Get the file type description from the registry. This will be "Text Document"
|
||||
// for .txt files, "JPEG Image" for .jpg files, etc. If the registry doesn't
|
||||
// have an entry for the file type, we return false, true if the description was
|
||||
// found. 'file_ext' must be in form ".txt".
|
||||
// Modified from ui/shell_dialogs/select_file_dialog_win.cc
|
||||
bool GetRegistryDescriptionFromExtension(const std::string& file_ext,
|
||||
std::string* reg_description) {
|
||||
DCHECK(reg_description);
|
||||
base::win::RegKey reg_ext(HKEY_CLASSES_ROOT,
|
||||
base::UTF8ToWide(file_ext).c_str(), KEY_READ);
|
||||
std::wstring reg_app;
|
||||
if (reg_ext.ReadValue(nullptr, ®_app) == ERROR_SUCCESS &&
|
||||
!reg_app.empty()) {
|
||||
base::win::RegKey reg_link(HKEY_CLASSES_ROOT, reg_app.c_str(), KEY_READ);
|
||||
std::wstring description;
|
||||
if (reg_link.ReadValue(nullptr, &description) == ERROR_SUCCESS) {
|
||||
*reg_description = base::WideToUTF8(description);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set up a filter for a Save/Open dialog, |ext_desc| as the text descriptions
|
||||
// of the |file_ext| types (optional), and (optionally) the default 'All Files'
|
||||
// view. The purpose of the filter is to show only files of a particular type in
|
||||
// a Windows Save/Open dialog box. The resulting filter is returned. The filter
|
||||
// created here are:
|
||||
// 1. only files that have 'file_ext' as their extension
|
||||
// 2. all files (only added if 'include_all_files' is true)
|
||||
// If a description is not provided for a file extension, it will be retrieved
|
||||
// from the registry. If the file extension does not exist in the registry, a
|
||||
// default description will be created (e.g. "qqq" yields "QQQ File").
|
||||
// Copied from ui/shell_dialogs/select_file_dialog_win.cc
|
||||
file_dialog::Filters FormatFilterForExtensions(
|
||||
const std::vector<std::string>& file_ext,
|
||||
const std::vector<std::string>& ext_desc,
|
||||
bool include_all_files,
|
||||
bool keep_extension_visible) {
|
||||
const std::string all_ext = "*";
|
||||
const std::string all_desc =
|
||||
l10n_util::GetStringUTF8(IDS_APP_SAVEAS_ALL_FILES);
|
||||
|
||||
DCHECK(file_ext.size() >= ext_desc.size());
|
||||
|
||||
if (file_ext.empty())
|
||||
include_all_files = true;
|
||||
|
||||
file_dialog::Filters result;
|
||||
result.reserve(file_ext.size() + 1);
|
||||
|
||||
for (size_t i = 0; i < file_ext.size(); ++i) {
|
||||
std::string ext = file_ext[i];
|
||||
std::string desc;
|
||||
if (i < ext_desc.size())
|
||||
desc = ext_desc[i];
|
||||
|
||||
if (ext.empty()) {
|
||||
// Force something reasonable to appear in the dialog box if there is no
|
||||
// extension provided.
|
||||
include_all_files = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (desc.empty()) {
|
||||
DCHECK(ext.find('.') != std::string::npos);
|
||||
std::string first_extension = ext.substr(ext.find('.'));
|
||||
size_t first_separator_index = first_extension.find(';');
|
||||
if (first_separator_index != std::string::npos)
|
||||
first_extension = first_extension.substr(0, first_separator_index);
|
||||
|
||||
// Find the extension name without the preceeding '.' character.
|
||||
std::string ext_name = first_extension;
|
||||
size_t ext_index = ext_name.find_first_not_of('.');
|
||||
if (ext_index != std::string::npos)
|
||||
ext_name = ext_name.substr(ext_index);
|
||||
|
||||
if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) {
|
||||
// The extension doesn't exist in the registry. Create a description
|
||||
// based on the unknown extension type (i.e. if the extension is .qqq,
|
||||
// then we create a description "QQQ File").
|
||||
desc = l10n_util::GetStringFUTF8(
|
||||
IDS_APP_SAVEAS_EXTENSION_FORMAT,
|
||||
base::i18n::ToUpper(base::UTF8ToUTF16(ext_name)));
|
||||
include_all_files = true;
|
||||
}
|
||||
if (desc.empty())
|
||||
desc = "*." + ext_name;
|
||||
} else if (keep_extension_visible) {
|
||||
// Having '*' in the description could cause the windows file dialog to
|
||||
// not include the file extension in the file dialog. So strip out any '*'
|
||||
// characters if `keep_extension_visible` is set.
|
||||
base::ReplaceChars(desc, "*", base::StringPiece(), &desc);
|
||||
}
|
||||
|
||||
result.push_back({desc, {ext}});
|
||||
}
|
||||
|
||||
if (include_all_files)
|
||||
result.push_back({all_desc, {all_ext}});
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
|
||||
} // namespace
|
||||
|
||||
ElectronDownloadManagerDelegate::ElectronDownloadManagerDelegate(
|
||||
@@ -131,6 +247,16 @@ void ElectronDownloadManagerDelegate::OnDownloadPathGenerated(
|
||||
const bool offscreen = !web_preferences || web_preferences->IsOffscreen();
|
||||
settings.force_detached = offscreen;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
if (settings.filters.empty()) {
|
||||
const std::wstring extension = settings.default_path.FinalExtension();
|
||||
if (!extension.empty()) {
|
||||
settings.filters = FormatFilterForExtensions(
|
||||
{base::WideToUTF8(extension)}, {""}, true, true);
|
||||
}
|
||||
}
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
gin_helper::Promise<gin_helper::Dictionary> dialog_promise(isolate);
|
||||
|
||||
@@ -27,6 +27,9 @@ void NativeBrowserViewViews::SetAutoResizeFlags(uint8_t flags) {
|
||||
|
||||
void NativeBrowserViewViews::UpdateDraggableRegions(
|
||||
const std::vector<mojom::DraggableRegionPtr>& regions) {
|
||||
if (&draggable_regions_ != ®ions)
|
||||
draggable_regions_ = mojo::Clone(regions);
|
||||
|
||||
// We need to snap the regions to the bounds of the current BrowserView.
|
||||
// For example, if an attached BrowserView is draggable but its bounds are
|
||||
// { x: 200, y: 100, width: 300, height: 300 }
|
||||
@@ -35,12 +38,10 @@ void NativeBrowserViewViews::UpdateDraggableRegions(
|
||||
// assumed that the regions begin in the top left corner as they
|
||||
// would for the main client window.
|
||||
auto const offset = GetBounds().OffsetFromOrigin();
|
||||
auto snapped_regions = mojo::Clone(regions);
|
||||
for (auto& snapped_region : snapped_regions) {
|
||||
for (auto& snapped_region : draggable_regions_) {
|
||||
snapped_region->bounds.Offset(offset);
|
||||
}
|
||||
|
||||
draggable_region_ = DraggableRegionsToSkRegion(snapped_regions);
|
||||
draggable_region_ = DraggableRegionsToSkRegion(draggable_regions_);
|
||||
}
|
||||
|
||||
void NativeBrowserViewViews::SetAutoResizeProportions(
|
||||
@@ -128,6 +129,12 @@ void NativeBrowserViewViews::SetBounds(const gfx::Rect& bounds) {
|
||||
auto* view = iwc_view->GetView();
|
||||
view->SetBoundsRect(bounds);
|
||||
ResetAutoResizeProportions();
|
||||
|
||||
view->InvalidateLayout();
|
||||
view->SchedulePaint();
|
||||
|
||||
// Ensure draggable regions are properly updated to reflect new bounds.
|
||||
UpdateDraggableRegions(draggable_regions_);
|
||||
}
|
||||
|
||||
gfx::Rect NativeBrowserViewViews::GetBounds() {
|
||||
|
||||
@@ -718,6 +718,17 @@ std::string NativeWindow::GetAccessibleTitle() {
|
||||
return base::UTF16ToUTF8(accessible_title_);
|
||||
}
|
||||
|
||||
void NativeWindow::HandlePendingFullscreenTransitions() {
|
||||
if (pending_transitions_.empty()) {
|
||||
set_fullscreen_transition_type(FullScreenTransitionType::NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
bool next_transition = pending_transitions_.front();
|
||||
pending_transitions_.pop();
|
||||
SetFullScreen(next_transition);
|
||||
}
|
||||
|
||||
// static
|
||||
int32_t NativeWindow::next_id_ = 0;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -317,6 +318,27 @@ class NativeWindow : public base::SupportsUserData,
|
||||
observers_.RemoveObserver(obs);
|
||||
}
|
||||
|
||||
// Handle fullscreen transitions.
|
||||
void HandlePendingFullscreenTransitions();
|
||||
|
||||
enum class FullScreenTransitionState { ENTERING, EXITING, NONE };
|
||||
|
||||
void set_fullscreen_transition_state(FullScreenTransitionState state) {
|
||||
fullscreen_transition_state_ = state;
|
||||
}
|
||||
FullScreenTransitionState fullscreen_transition_state() const {
|
||||
return fullscreen_transition_state_;
|
||||
}
|
||||
|
||||
enum class FullScreenTransitionType { HTML, NATIVE, NONE };
|
||||
|
||||
void set_fullscreen_transition_type(FullScreenTransitionType type) {
|
||||
fullscreen_transition_type_ = type;
|
||||
}
|
||||
FullScreenTransitionType fullscreen_transition_type() const {
|
||||
return fullscreen_transition_type_;
|
||||
}
|
||||
|
||||
views::Widget* widget() const { return widget_.get(); }
|
||||
views::View* content_view() const { return content_view_; }
|
||||
|
||||
@@ -375,6 +397,12 @@ class NativeWindow : public base::SupportsUserData,
|
||||
// The "titleBarStyle" option.
|
||||
TitleBarStyle title_bar_style_ = TitleBarStyle::kNormal;
|
||||
|
||||
std::queue<bool> pending_transitions_;
|
||||
FullScreenTransitionState fullscreen_transition_state_ =
|
||||
FullScreenTransitionState::NONE;
|
||||
FullScreenTransitionType fullscreen_transition_type_ =
|
||||
FullScreenTransitionType::NONE;
|
||||
|
||||
private:
|
||||
std::unique_ptr<views::Widget> widget_;
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -166,16 +165,14 @@ class NativeWindowMac : public NativeWindow,
|
||||
|
||||
void UpdateVibrancyRadii(bool fullscreen);
|
||||
|
||||
void UpdateWindowOriginalFrame();
|
||||
|
||||
// Set the attribute of NSWindow while work around a bug of zoom button.
|
||||
bool HasStyleMask(NSUInteger flag) const;
|
||||
void SetStyleMask(bool on, NSUInteger flag);
|
||||
void SetCollectionBehavior(bool on, NSUInteger flag);
|
||||
void SetWindowLevel(int level);
|
||||
|
||||
enum class FullScreenTransitionState { ENTERING, EXITING, NONE };
|
||||
|
||||
// Handle fullscreen transitions.
|
||||
void SetFullScreenTransitionState(FullScreenTransitionState state);
|
||||
void HandlePendingFullscreenTransitions();
|
||||
bool HandleDeferredClose();
|
||||
void SetHasDeferredWindowClose(bool defer_close) {
|
||||
has_deferred_window_close_ = defer_close;
|
||||
@@ -246,13 +243,6 @@ class NativeWindowMac : public NativeWindow,
|
||||
bool zoom_to_page_width_ = false;
|
||||
absl::optional<gfx::Point> traffic_light_position_;
|
||||
|
||||
std::queue<bool> pending_transitions_;
|
||||
FullScreenTransitionState fullscreen_transition_state() const {
|
||||
return fullscreen_transition_state_;
|
||||
}
|
||||
FullScreenTransitionState fullscreen_transition_state_ =
|
||||
FullScreenTransitionState::NONE;
|
||||
|
||||
// Trying to close an NSWindow during a fullscreen transition will cause the
|
||||
// window to lock up. Use this to track if CloseWindow was called during a
|
||||
// fullscreen transition, to defer the -[NSWindow close] call until the
|
||||
|
||||
@@ -451,7 +451,7 @@ NativeWindowMac::NativeWindowMac(const gin_helper::Dictionary& options,
|
||||
SetContentView(new views::View());
|
||||
AddContentViewLayers();
|
||||
|
||||
original_frame_ = [window_ frame];
|
||||
UpdateWindowOriginalFrame();
|
||||
original_level_ = [window_ level];
|
||||
}
|
||||
|
||||
@@ -483,7 +483,8 @@ void NativeWindowMac::Close() {
|
||||
// [window_ performClose:nil], the window won't close properly
|
||||
// even after the user has ended the sheet.
|
||||
// Ensure it's closed before calling [window_ performClose:nil].
|
||||
SetEnabled(true);
|
||||
if ([window_ attachedSheet])
|
||||
[window_ endSheet:[window_ attachedSheet]];
|
||||
|
||||
[window_ performClose:nil];
|
||||
|
||||
@@ -553,7 +554,8 @@ void NativeWindowMac::Hide() {
|
||||
// If a sheet is attached to the window when we call [window_ orderOut:nil],
|
||||
// the sheet won't be able to show again on the same window.
|
||||
// Ensure it's closed before calling [window_ orderOut:nil].
|
||||
SetEnabled(true);
|
||||
if ([window_ attachedSheet])
|
||||
[window_ endSheet:[window_ attachedSheet]];
|
||||
|
||||
if (is_modal() && parent()) {
|
||||
[window_ orderOut:nil];
|
||||
@@ -586,20 +588,24 @@ bool NativeWindowMac::IsVisible() {
|
||||
return [window_ isVisible] && !occluded && !IsMinimized();
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetFullScreenTransitionState(
|
||||
FullScreenTransitionState state) {
|
||||
fullscreen_transition_state_ = state;
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsEnabled() {
|
||||
return [window_ attachedSheet] == nil;
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetEnabled(bool enable) {
|
||||
if (!enable) {
|
||||
[window_ beginSheet:window_
|
||||
NSRect frame = [window_ frame];
|
||||
NSWindow* window =
|
||||
[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, frame.size.width,
|
||||
frame.size.height)
|
||||
styleMask:NSWindowStyleMaskTitled
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:NO];
|
||||
[window setAlphaValue:0.5];
|
||||
|
||||
[window_ beginSheet:window
|
||||
completionHandler:^(NSModalResponse returnCode) {
|
||||
NSLog(@"modal enabled");
|
||||
NSLog(@"main window disabled");
|
||||
return;
|
||||
}];
|
||||
} else if ([window_ attachedSheet]) {
|
||||
@@ -608,13 +614,23 @@ void NativeWindowMac::SetEnabled(bool enable) {
|
||||
}
|
||||
|
||||
void NativeWindowMac::Maximize() {
|
||||
if (IsMaximized())
|
||||
const bool is_visible = [window_ isVisible];
|
||||
|
||||
if (IsMaximized()) {
|
||||
if (!is_visible)
|
||||
ShowInactive();
|
||||
return;
|
||||
}
|
||||
|
||||
// Take note of the current window size
|
||||
if (IsNormal())
|
||||
original_frame_ = [window_ frame];
|
||||
UpdateWindowOriginalFrame();
|
||||
[window_ zoom:nil];
|
||||
|
||||
if (!is_visible) {
|
||||
ShowInactive();
|
||||
NotifyWindowMaximize();
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::Unmaximize() {
|
||||
@@ -636,7 +652,7 @@ void NativeWindowMac::Unmaximize() {
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsMaximized() {
|
||||
if (([window_ styleMask] & NSWindowStyleMaskResizable) != 0)
|
||||
if (HasStyleMask(NSWindowStyleMaskResizable) != 0)
|
||||
return [window_ isZoomed];
|
||||
|
||||
NSRect rectScreen = GetAspectRatio() > 0.0
|
||||
@@ -652,7 +668,7 @@ void NativeWindowMac::Minimize() {
|
||||
|
||||
// Take note of the current window size
|
||||
if (IsNormal())
|
||||
original_frame_ = [window_ frame];
|
||||
UpdateWindowOriginalFrame();
|
||||
[window_ miniaturize:nil];
|
||||
}
|
||||
|
||||
@@ -664,15 +680,6 @@ bool NativeWindowMac::IsMinimized() {
|
||||
return [window_ isMiniaturized];
|
||||
}
|
||||
|
||||
void NativeWindowMac::HandlePendingFullscreenTransitions() {
|
||||
if (pending_transitions_.empty())
|
||||
return;
|
||||
|
||||
bool next_transition = pending_transitions_.front();
|
||||
pending_transitions_.pop();
|
||||
SetFullScreen(next_transition);
|
||||
}
|
||||
|
||||
bool NativeWindowMac::HandleDeferredClose() {
|
||||
if (has_deferred_window_close_) {
|
||||
SetHasDeferredWindowClose(false);
|
||||
@@ -705,7 +712,7 @@ void NativeWindowMac::SetFullScreen(bool fullscreen) {
|
||||
|
||||
// Take note of the current window size
|
||||
if (IsNormal())
|
||||
original_frame_ = [window_ frame];
|
||||
UpdateWindowOriginalFrame();
|
||||
|
||||
// This needs to be set here because it can be the case that
|
||||
// SetFullScreen is called by a user before windowWillEnterFullScreen
|
||||
@@ -719,7 +726,7 @@ void NativeWindowMac::SetFullScreen(bool fullscreen) {
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsFullscreen() const {
|
||||
return [window_ styleMask] & NSWindowStyleMaskFullScreen;
|
||||
return HasStyleMask(NSWindowStyleMaskFullScreen);
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetBounds(const gfx::Rect& bounds, bool animate) {
|
||||
@@ -741,6 +748,7 @@ void NativeWindowMac::SetBounds(const gfx::Rect& bounds, bool animate) {
|
||||
|
||||
[window_ setFrame:cocoa_bounds display:YES animate:animate];
|
||||
user_set_bounds_maximized_ = IsMaximized() ? true : false;
|
||||
UpdateWindowOriginalFrame();
|
||||
}
|
||||
|
||||
gfx::Rect NativeWindowMac::GetBounds() {
|
||||
@@ -819,7 +827,10 @@ void NativeWindowMac::SetResizable(bool resizable) {
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsResizable() {
|
||||
return [window_ styleMask] & NSWindowStyleMaskResizable;
|
||||
bool in_fs_transition =
|
||||
fullscreen_transition_state() != FullScreenTransitionState::NONE;
|
||||
bool has_rs_mask = HasStyleMask(NSWindowStyleMaskResizable);
|
||||
return has_rs_mask && !IsFullscreen() && !in_fs_transition;
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetMovable(bool movable) {
|
||||
@@ -835,7 +846,7 @@ void NativeWindowMac::SetMinimizable(bool minimizable) {
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsMinimizable() {
|
||||
return [window_ styleMask] & NSMiniaturizableWindowMask;
|
||||
return HasStyleMask(NSWindowStyleMaskMiniaturizable);
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetMaximizable(bool maximizable) {
|
||||
@@ -865,7 +876,7 @@ void NativeWindowMac::SetClosable(bool closable) {
|
||||
}
|
||||
|
||||
bool NativeWindowMac::IsClosable() {
|
||||
return [window_ styleMask] & NSWindowStyleMaskClosable;
|
||||
return HasStyleMask(NSWindowStyleMaskClosable);
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetAlwaysOnTop(ui::ZOrderLevel z_order,
|
||||
@@ -1015,7 +1026,7 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
|
||||
|
||||
// Take note of the current window size and level
|
||||
if (IsNormal()) {
|
||||
original_frame_ = [window_ frame];
|
||||
UpdateWindowOriginalFrame();
|
||||
original_level_ = [window_ level];
|
||||
}
|
||||
|
||||
@@ -1153,6 +1164,8 @@ double NativeWindowMac::GetOpacity() {
|
||||
|
||||
void NativeWindowMac::SetRepresentedFilename(const std::string& filename) {
|
||||
[window_ setRepresentedFilename:base::SysUTF8ToNSString(filename)];
|
||||
if (buttons_proxy_)
|
||||
[buttons_proxy_ redraw];
|
||||
}
|
||||
|
||||
std::string NativeWindowMac::GetRepresentedFilename() {
|
||||
@@ -1378,8 +1391,7 @@ void NativeWindowMac::UpdateVibrancyRadii(bool fullscreen) {
|
||||
NSVisualEffectView* vibrantView = [window_ vibrantView];
|
||||
|
||||
if (vibrantView != nil && !vibrancy_type_.empty()) {
|
||||
const bool no_rounded_corner =
|
||||
!([window_ styleMask] & NSWindowStyleMaskTitled);
|
||||
const bool no_rounded_corner = !HasStyleMask(NSWindowStyleMaskTitled);
|
||||
if (!has_frame() && !is_modal() && !no_rounded_corner) {
|
||||
CGFloat radius;
|
||||
if (fullscreen) {
|
||||
@@ -1413,6 +1425,10 @@ void NativeWindowMac::UpdateVibrancyRadii(bool fullscreen) {
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::UpdateWindowOriginalFrame() {
|
||||
original_frame_ = [window_ frame];
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetVibrancy(const std::string& type) {
|
||||
NSVisualEffectView* vibrantView = [window_ vibrantView];
|
||||
|
||||
@@ -1517,12 +1533,15 @@ void NativeWindowMac::SetVibrancy(const std::string& type) {
|
||||
|
||||
void NativeWindowMac::SetWindowButtonVisibility(bool visible) {
|
||||
window_button_visibility_ = visible;
|
||||
// The visibility of window buttons are managed by |buttons_proxy_| if the
|
||||
// style is customButtonsOnHover.
|
||||
if (title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover)
|
||||
if (buttons_proxy_) {
|
||||
if (visible)
|
||||
[buttons_proxy_ redraw];
|
||||
[buttons_proxy_ setVisible:visible];
|
||||
else
|
||||
}
|
||||
|
||||
if (title_bar_style_ != TitleBarStyle::kCustomButtonsOnHover)
|
||||
InternalSetWindowButtonVisibility(visible);
|
||||
|
||||
NotifyLayoutWindowControlsOverlay();
|
||||
}
|
||||
|
||||
@@ -1747,6 +1766,10 @@ void NativeWindowMac::OverrideNSWindowContentView() {
|
||||
AddContentViewLayers();
|
||||
}
|
||||
|
||||
bool NativeWindowMac::HasStyleMask(NSUInteger flag) const {
|
||||
return [window_ styleMask] & flag;
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetStyleMask(bool on, NSUInteger flag) {
|
||||
// Changing the styleMask of a frameless windows causes it to change size so
|
||||
// we explicitly disable resizing while setting it.
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include "shell/browser/ui/views/client_frame_view_linux.h"
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#include "shell/browser/ui/views/native_frame_view.h"
|
||||
#include "shell/common/platform_util.h"
|
||||
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
|
||||
#include "ui/views/window/native_frame_view.h"
|
||||
|
||||
@@ -271,6 +272,8 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options,
|
||||
// Set WM_CLASS.
|
||||
params.wm_class_name = base::ToLowerASCII(name);
|
||||
params.wm_class_class = name;
|
||||
// Set Wayland application ID.
|
||||
params.wayland_app_id = platform_util::GetXdgAppId();
|
||||
|
||||
auto* native_widget = new views::DesktopNativeWidgetAura(widget());
|
||||
params.native_widget = native_widget;
|
||||
@@ -495,6 +498,13 @@ void NativeWindowViews::Show() {
|
||||
if (global_menu_bar_)
|
||||
global_menu_bar_->OnWindowMapped();
|
||||
#endif
|
||||
|
||||
#if defined(USE_OZONE_PLATFORM_X11)
|
||||
// On X11, setting Z order before showing the window doesn't take effect,
|
||||
// so we have to call it again.
|
||||
if (IsX11())
|
||||
widget()->SetZOrderLevel(widget()->GetZOrderLevel());
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::ShowInactive() {
|
||||
@@ -719,7 +729,6 @@ void NativeWindowViews::SetBounds(const gfx::Rect& bounds, bool animate) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
if (is_moving_ || is_resizing_) {
|
||||
pending_bounds_change_ = bounds;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -876,6 +885,11 @@ bool NativeWindowViews::IsMovable() {
|
||||
void NativeWindowViews::SetMinimizable(bool minimizable) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
FlipWindowStyle(GetAcceleratedWidget(), minimizable, WS_MINIMIZEBOX);
|
||||
if (IsWindowControlsOverlayEnabled()) {
|
||||
auto* frame_view =
|
||||
static_cast<WinFrameView*>(widget()->non_client_view()->frame_view());
|
||||
frame_view->caption_button_container()->UpdateButtons();
|
||||
}
|
||||
#endif
|
||||
minimizable_ = minimizable;
|
||||
}
|
||||
@@ -891,6 +905,11 @@ bool NativeWindowViews::IsMinimizable() {
|
||||
void NativeWindowViews::SetMaximizable(bool maximizable) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
FlipWindowStyle(GetAcceleratedWidget(), maximizable, WS_MAXIMIZEBOX);
|
||||
if (IsWindowControlsOverlayEnabled()) {
|
||||
auto* frame_view =
|
||||
static_cast<WinFrameView*>(widget()->non_client_view()->frame_view());
|
||||
frame_view->caption_button_container()->UpdateButtons();
|
||||
}
|
||||
#endif
|
||||
maximizable_ = maximizable;
|
||||
}
|
||||
@@ -926,6 +945,11 @@ void NativeWindowViews::SetClosable(bool closable) {
|
||||
} else {
|
||||
EnableMenuItem(menu, SC_CLOSE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
|
||||
}
|
||||
if (IsWindowControlsOverlayEnabled()) {
|
||||
auto* frame_view =
|
||||
static_cast<WinFrameView*>(widget()->non_client_view()->frame_view());
|
||||
frame_view->caption_button_container()->UpdateButtons();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ class NativeWindowViews : public NativeWindow,
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
void HandleSizeEvent(WPARAM w_param, LPARAM l_param);
|
||||
void ResetWindowControls();
|
||||
void SetForwardMouseMessages(bool forward);
|
||||
static LRESULT CALLBACK SubclassProc(HWND hwnd,
|
||||
UINT msg,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/views/root_view.h"
|
||||
#include "shell/browser/ui/views/win_frame_view.h"
|
||||
#include "shell/common/electron_constants.h"
|
||||
#include "ui/display/display.h"
|
||||
#include "ui/display/win/screen_win.h"
|
||||
@@ -414,6 +415,7 @@ void NativeWindowViews::HandleSizeEvent(WPARAM w_param, LPARAM l_param) {
|
||||
last_window_state_ != ui::SHOW_STATE_MAXIMIZED) {
|
||||
last_window_state_ = ui::SHOW_STATE_MAXIMIZED;
|
||||
NotifyWindowMaximize();
|
||||
ResetWindowControls();
|
||||
} else if (w_param == SIZE_MINIMIZED &&
|
||||
last_window_state_ != ui::SHOW_STATE_MINIMIZED) {
|
||||
last_window_state_ = ui::SHOW_STATE_MINIMIZED;
|
||||
@@ -421,7 +423,7 @@ void NativeWindowViews::HandleSizeEvent(WPARAM w_param, LPARAM l_param) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SIZE_RESTORED:
|
||||
case SIZE_RESTORED: {
|
||||
switch (last_window_state_) {
|
||||
case ui::SHOW_STATE_MAXIMIZED:
|
||||
last_window_state_ = ui::SHOW_STATE_NORMAL;
|
||||
@@ -439,7 +441,20 @@ void NativeWindowViews::HandleSizeEvent(WPARAM w_param, LPARAM l_param) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ResetWindowControls();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowViews::ResetWindowControls() {
|
||||
// If a given window was minimized and has since been
|
||||
// unminimized (restored/maximized), ensure the WCO buttons
|
||||
// are reset to their default unpressed state.
|
||||
auto* ncv = widget()->non_client_view();
|
||||
if (IsWindowControlsOverlayEnabled() && ncv) {
|
||||
auto* frame_view = static_cast<WinFrameView*>(ncv->frame_view());
|
||||
frame_view->caption_button_container()->ResetWindowControls();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include "base/files/file_enumerator.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "shell/browser/notifications/notification_delegate.h"
|
||||
#include "shell/browser/ui/gtk_util.h"
|
||||
@@ -138,13 +137,8 @@ void LibnotifyNotification::Show(const NotificationOptions& options) {
|
||||
|
||||
// Send the desktop name to identify the application
|
||||
// The desktop-entry is the part before the .desktop
|
||||
std::string desktop_id;
|
||||
if (platform_util::GetDesktopName(&desktop_id)) {
|
||||
const std::string suffix{".desktop"};
|
||||
if (base::EndsWith(desktop_id, suffix,
|
||||
base::CompareCase::INSENSITIVE_ASCII)) {
|
||||
desktop_id.resize(desktop_id.size() - suffix.size());
|
||||
}
|
||||
std::string desktop_id = platform_util::GetXdgAppId();
|
||||
if (!desktop_id.empty()) {
|
||||
libnotify_loader_.notify_notification_set_hint_string(
|
||||
notification_, "desktop-entry", desktop_id.c_str());
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 19,0,2,0
|
||||
PRODUCTVERSION 19,0,2,0
|
||||
FILEVERSION 19,0,12,0
|
||||
PRODUCTVERSION 19,0,12,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,12 +68,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "19.0.2"
|
||||
VALUE "FileVersion", "19.0.12"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "19.0.2"
|
||||
VALUE "ProductVersion", "19.0.12"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
using TitleBarStyle = electron::NativeWindowMac::TitleBarStyle;
|
||||
using FullScreenTransitionState =
|
||||
electron::NativeWindowMac::FullScreenTransitionState;
|
||||
electron::NativeWindow::FullScreenTransitionState;
|
||||
|
||||
@implementation ElectronNSWindowDelegate
|
||||
|
||||
@@ -203,6 +203,7 @@ using FullScreenTransitionState =
|
||||
// windowDidDeminiaturize
|
||||
level_ = [window level];
|
||||
shell_->SetWindowLevel(NSNormalWindowLevel);
|
||||
shell_->UpdateWindowOriginalFrame();
|
||||
}
|
||||
|
||||
- (void)windowDidMiniaturize:(NSNotification*)notification {
|
||||
@@ -234,17 +235,19 @@ using FullScreenTransitionState =
|
||||
}
|
||||
|
||||
- (void)windowWillEnterFullScreen:(NSNotification*)notification {
|
||||
shell_->SetFullScreenTransitionState(FullScreenTransitionState::ENTERING);
|
||||
// Store resizable mask so it can be restored after exiting fullscreen.
|
||||
is_resizable_ = shell_->HasStyleMask(NSWindowStyleMaskResizable);
|
||||
|
||||
shell_->set_fullscreen_transition_state(FullScreenTransitionState::ENTERING);
|
||||
|
||||
shell_->NotifyWindowWillEnterFullScreen();
|
||||
|
||||
// Setting resizable to true before entering fullscreen.
|
||||
is_resizable_ = shell_->IsResizable();
|
||||
// Set resizable to true before entering fullscreen.
|
||||
shell_->SetResizable(true);
|
||||
}
|
||||
|
||||
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
|
||||
shell_->SetFullScreenTransitionState(FullScreenTransitionState::NONE);
|
||||
shell_->set_fullscreen_transition_state(FullScreenTransitionState::NONE);
|
||||
|
||||
shell_->NotifyWindowEnterFullScreen();
|
||||
|
||||
@@ -255,13 +258,13 @@ using FullScreenTransitionState =
|
||||
}
|
||||
|
||||
- (void)windowWillExitFullScreen:(NSNotification*)notification {
|
||||
shell_->SetFullScreenTransitionState(FullScreenTransitionState::EXITING);
|
||||
shell_->set_fullscreen_transition_state(FullScreenTransitionState::EXITING);
|
||||
|
||||
shell_->NotifyWindowWillLeaveFullScreen();
|
||||
}
|
||||
|
||||
- (void)windowDidExitFullScreen:(NSNotification*)notification {
|
||||
shell_->SetFullScreenTransitionState(FullScreenTransitionState::NONE);
|
||||
shell_->set_fullscreen_transition_state(FullScreenTransitionState::NONE);
|
||||
|
||||
shell_->SetResizable(is_resizable_);
|
||||
shell_->NotifyWindowLeaveFullScreen();
|
||||
|
||||
@@ -10,52 +10,78 @@
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "shell/browser/ui/drag_util.h"
|
||||
|
||||
namespace electron {
|
||||
// Contents largely copied from
|
||||
// chrome/browser/download/drag_download_item_mac.mm.
|
||||
|
||||
@interface DragDownloadItemSource : NSObject <NSDraggingSource>
|
||||
@end
|
||||
|
||||
@implementation DragDownloadItemSource
|
||||
|
||||
- (NSDragOperation)draggingSession:(NSDraggingSession*)session
|
||||
sourceOperationMaskForDraggingContext:(NSDraggingContext)context {
|
||||
return NSDragOperationEvery;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace {
|
||||
|
||||
// Write information about the file being dragged to the pasteboard.
|
||||
void AddFilesToPasteboard(NSPasteboard* pasteboard,
|
||||
const std::vector<base::FilePath>& files) {
|
||||
NSMutableArray* fileList = [NSMutableArray array];
|
||||
for (const base::FilePath& file : files)
|
||||
[fileList addObject:base::SysUTF8ToNSString(file.value())];
|
||||
[pasteboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType]
|
||||
owner:nil];
|
||||
[pasteboard setPropertyList:fileList forType:NSFilenamesPboardType];
|
||||
id<NSDraggingSource> GetDraggingSource() {
|
||||
static id<NSDraggingSource> source = [[DragDownloadItemSource alloc] init];
|
||||
return source;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace electron {
|
||||
|
||||
void DragFileItems(const std::vector<base::FilePath>& files,
|
||||
const gfx::Image& icon,
|
||||
gfx::NativeView view) {
|
||||
NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
|
||||
AddFilesToPasteboard(pasteboard, files);
|
||||
auto* native_view = view.GetNativeNSView();
|
||||
NSPoint current_position =
|
||||
[[native_view window] mouseLocationOutsideOfEventStream];
|
||||
current_position =
|
||||
[native_view backingAlignedRect:NSMakeRect(current_position.x,
|
||||
current_position.y, 0, 0)
|
||||
options:NSAlignAllEdgesOutward]
|
||||
.origin;
|
||||
|
||||
NSMutableArray* file_items = [NSMutableArray array];
|
||||
for (auto const& file : files) {
|
||||
NSURL* file_url =
|
||||
[NSURL fileURLWithPath:base::SysUTF8ToNSString(file.value())];
|
||||
NSDraggingItem* file_item = [[[NSDraggingItem alloc]
|
||||
initWithPasteboardWriter:file_url] autorelease];
|
||||
NSImage* file_image = icon.ToNSImage();
|
||||
NSSize image_size = file_image.size;
|
||||
NSRect image_rect = NSMakeRect(current_position.x - image_size.width / 2,
|
||||
current_position.y - image_size.height / 2,
|
||||
image_size.width, image_size.height);
|
||||
[file_item setDraggingFrame:image_rect contents:file_image];
|
||||
[file_items addObject:file_item];
|
||||
}
|
||||
|
||||
// Synthesize a drag event, since we don't have access to the actual event
|
||||
// that initiated a drag (possibly consumed by the Web UI, for example).
|
||||
NSWindow* window = [view.GetNativeNSView() window];
|
||||
NSPoint position = [window mouseLocationOutsideOfEventStream];
|
||||
NSPoint position = [[native_view window] mouseLocationOutsideOfEventStream];
|
||||
NSTimeInterval eventTime = [[NSApp currentEvent] timestamp];
|
||||
NSEvent* dragEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged
|
||||
location:position
|
||||
modifierFlags:NSLeftMouseDraggedMask
|
||||
timestamp:eventTime
|
||||
windowNumber:[window windowNumber]
|
||||
context:nil
|
||||
eventNumber:0
|
||||
clickCount:1
|
||||
pressure:1.0];
|
||||
NSEvent* dragEvent =
|
||||
[NSEvent mouseEventWithType:NSEventTypeLeftMouseDragged
|
||||
location:position
|
||||
modifierFlags:NSEventMaskLeftMouseDragged
|
||||
timestamp:eventTime
|
||||
windowNumber:[[native_view window] windowNumber]
|
||||
context:nil
|
||||
eventNumber:0
|
||||
clickCount:1
|
||||
pressure:1.0];
|
||||
|
||||
// Run the drag operation.
|
||||
[window dragImage:icon.ToNSImage()
|
||||
at:position
|
||||
offset:NSZeroSize
|
||||
event:dragEvent
|
||||
pasteboard:pasteboard
|
||||
source:view.GetNativeNSView()
|
||||
slideBack:YES];
|
||||
[native_view beginDraggingSessionWithItems:file_items
|
||||
event:dragEvent
|
||||
source:GetDraggingSource()];
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
|
||||
3
shell/browser/ui/electron_gdk_pixbuf.sigs
Normal file
3
shell/browser/ui/electron_gdk_pixbuf.sigs
Normal file
@@ -0,0 +1,3 @@
|
||||
GdkPixbuf* gdk_pixbuf_new(GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int width, int height)
|
||||
GdkPixbuf* gdk_pixbuf_scale_simple(const GdkPixbuf* src, int dest_width, int dest_height, GdkInterpType interp_type)
|
||||
guchar* gdk_pixbuf_get_pixels(const GdkPixbuf* pixbuf)
|
||||
@@ -1 +1,2 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user