mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
85 Commits
v19.0.0-be
...
v19.0.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ba32b32ec3 | ||
|
|
3ad5a45173 | ||
|
|
b9d16ab3eb | ||
|
|
7e2606df02 | ||
|
|
8692a5b921 | ||
|
|
abf438bddc | ||
|
|
56515ad544 | ||
|
|
a9ff8f1359 | ||
|
|
f84cafe4fd | ||
|
|
42266d2bf0 | ||
|
|
44b5c72f67 | ||
|
|
96e1c7ec92 | ||
|
|
0206a9b7ed | ||
|
|
10ad6f8295 | ||
|
|
23c18be06f | ||
|
|
cc4565bb41 | ||
|
|
9d1c53a7e4 | ||
|
|
c7cd23c069 | ||
|
|
f9dc5b52d0 | ||
|
|
aca4b543d5 | ||
|
|
c2a11cef63 | ||
|
|
85a7498bd6 | ||
|
|
b0c255b72c | ||
|
|
2d91a03b36 | ||
|
|
424fd85b1a | ||
|
|
ad48ccbb9b | ||
|
|
2ea9be3ade | ||
|
|
52a9566f28 | ||
|
|
4ecaae9555 | ||
|
|
21b8200170 | ||
|
|
2b6cd3458f | ||
|
|
00e747ac24 | ||
|
|
082b06cf4e | ||
|
|
b07e17a3bb | ||
|
|
49ee456797 | ||
|
|
85063322e9 | ||
|
|
633d2961eb | ||
|
|
4f0592101b | ||
|
|
8797485564 |
@@ -14,8 +14,7 @@ parameters:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
# TODO (vertedinde): migrate this variable to upload-to-az
|
||||
upload-to-s3:
|
||||
upload-to-storage:
|
||||
type: string
|
||||
default: '1'
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ parameters:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
upload-to-s3:
|
||||
upload-to-storage:
|
||||
type: string
|
||||
default: '1'
|
||||
|
||||
@@ -473,6 +473,13 @@ step-install-npm-deps-on-mac: &step-install-npm-deps-on-mac
|
||||
node script/yarn install
|
||||
fi
|
||||
|
||||
step-install-npm-deps: &step-install-npm-deps
|
||||
run:
|
||||
name: Install node_modules
|
||||
command: |
|
||||
cd src/electron
|
||||
node script/yarn install --frozen-lockfile
|
||||
|
||||
# This step handles the differences between the linux "gclient sync"
|
||||
# and the expected state on macOS
|
||||
step-fix-sync: &step-fix-sync
|
||||
@@ -520,15 +527,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
|
||||
@@ -551,7 +549,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
|
||||
@@ -637,9 +635,9 @@ step-electron-publish: &step-electron-publish
|
||||
fi
|
||||
|
||||
cd src/electron
|
||||
if [ "$UPLOAD_TO_S3" == "1" ]; then
|
||||
echo 'Uploading Electron release distribution to S3'
|
||||
script/release/uploaders/upload.py --verbose --upload_to_s3
|
||||
if [ "$UPLOAD_TO_STORAGE" == "1" ]; then
|
||||
echo 'Uploading Electron release distribution to Azure'
|
||||
script/release/uploaders/upload.py --verbose --UPLOAD_TO_STORAGE
|
||||
else
|
||||
echo 'Uploading Electron release distribution to Github releases'
|
||||
script/release/uploaders/upload.py --verbose
|
||||
@@ -1000,9 +998,16 @@ step-ts-compile: &step-ts-compile
|
||||
run:
|
||||
name: Run TS/JS compile on doc only change
|
||||
command: |
|
||||
cd src
|
||||
ninja -C out/Default electron:default_app_js -j $NUMBER_OF_NINJA_PROCESSES
|
||||
ninja -C out/Default electron:electron_js2c -j $NUMBER_OF_NINJA_PROCESSES
|
||||
cd src/electron
|
||||
node script/yarn create-typescript-definitions
|
||||
node script/yarn tsc -p tsconfig.default_app.json --noEmit
|
||||
for f in build/webpack/*.js
|
||||
do
|
||||
out="${f:29}"
|
||||
if [ "$out" != "base.js" ]; then
|
||||
node script/yarn webpack --config $f --output-filename=$out --output-path=./.tmp --env.mode=development
|
||||
fi
|
||||
done
|
||||
|
||||
# List of all steps.
|
||||
steps-electron-gn-check: &steps-electron-gn-check
|
||||
@@ -1010,7 +1015,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
|
||||
@@ -1024,37 +1029,7 @@ steps-electron-ts-compile-for-doc-change: &steps-electron-ts-compile-for-doc-cha
|
||||
steps:
|
||||
# Checkout - Copied from steps-checkout
|
||||
- *step-checkout-electron
|
||||
- *step-depot-tools-get
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-restore-brew-cache
|
||||
- *step-install-gnutar-on-mac
|
||||
- *step-install-python2-on-mac
|
||||
- *step-get-more-space-on-mac
|
||||
- *step-setup-goma-for-build
|
||||
- *step-generate-deps-hash
|
||||
- *step-touch-sync-done
|
||||
- maybe-restore-portaled-src-cache
|
||||
- *step-maybe-restore-git-cache
|
||||
- *step-set-git-cache-path
|
||||
# This sync call only runs if .circle-sync-done is an EMPTY file
|
||||
- *step-gclient-sync
|
||||
# These next few steps reset Electron to the correct commit regardless of which cache was restored
|
||||
- run:
|
||||
name: Wipe Electron
|
||||
command: rm -rf src/electron
|
||||
- *step-checkout-electron
|
||||
- *step-run-electron-only-hooks
|
||||
- *step-generate-deps-hash-cleanly
|
||||
- *step-mark-sync-done
|
||||
- *step-minimize-workspace-size-from-checkout
|
||||
|
||||
- *step-depot-tools-add-to-path
|
||||
- *step-setup-env-for-build
|
||||
- *step-wait-for-goma
|
||||
- *step-get-more-space-on-mac
|
||||
- *step-install-npm-deps-on-mac
|
||||
- *step-fix-sync
|
||||
- *step-gn-gen-default
|
||||
- *step-install-npm-deps
|
||||
|
||||
#Compile ts/js to verify doc change didn't break anything
|
||||
- *step-ts-compile
|
||||
@@ -1064,7 +1039,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
|
||||
@@ -1206,6 +1181,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" ]; 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:
|
||||
@@ -1372,7 +1372,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 >>
|
||||
@@ -1564,7 +1564,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 >>
|
||||
@@ -1747,7 +1747,7 @@ jobs:
|
||||
environment:
|
||||
<<: *env-linux-2xlarge-release
|
||||
<<: *env-release-build
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
|
||||
<<: *env-ninja-status
|
||||
steps:
|
||||
- run: echo running
|
||||
@@ -1791,7 +1791,7 @@ jobs:
|
||||
<<: *env-release-build
|
||||
<<: *env-32bit-release
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
|
||||
<<: *env-ninja-status
|
||||
steps:
|
||||
- run: echo running
|
||||
@@ -1844,7 +1844,7 @@ jobs:
|
||||
<<: *env-arm64
|
||||
<<: *env-release-build
|
||||
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm64=True'
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
|
||||
<<: *env-ninja-status
|
||||
steps:
|
||||
- run: echo running
|
||||
@@ -1892,7 +1892,7 @@ jobs:
|
||||
environment:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-release-build
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
|
||||
<<: *env-ninja-status
|
||||
steps:
|
||||
- run: echo running
|
||||
@@ -1914,7 +1914,7 @@ jobs:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-release-build
|
||||
<<: *env-apple-silicon
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
|
||||
<<: *env-ninja-status
|
||||
steps:
|
||||
- run: echo running
|
||||
@@ -1984,7 +1984,7 @@ jobs:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-mas
|
||||
<<: *env-release-build
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
|
||||
steps:
|
||||
- run: echo running
|
||||
- when:
|
||||
@@ -2005,7 +2005,7 @@ jobs:
|
||||
<<: *env-mac-large-release
|
||||
<<: *env-mas-apple-silicon
|
||||
<<: *env-release-build
|
||||
UPLOAD_TO_S3: << pipeline.parameters.upload-to-s3 >>
|
||||
UPLOAD_TO_STORAGE: << pipeline.parameters.upload-to-storage >>
|
||||
<<: *env-ninja-status
|
||||
steps:
|
||||
- run: echo running
|
||||
|
||||
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.40',
|
||||
'102.0.5005.134',
|
||||
'node_version':
|
||||
'v16.14.2',
|
||||
'nan_version':
|
||||
|
||||
@@ -1 +1 @@
|
||||
19.0.0-beta.5
|
||||
19.0.7
|
||||
54
appveyor.yml
54
appveyor.yml
@@ -11,7 +11,7 @@
|
||||
# - "TARGET_ARCH" Choose from {'ia32', 'x64', 'arm', 'arm64', 'mips64el'}.
|
||||
# Is used in some publishing scripts, but does NOT affect the Electron binary.
|
||||
# Must match 'target_cpu' passed to "GN_EXTRA_ARGS" and "NPM_CONFIG_ARCH" value.
|
||||
# - "UPLOAD_TO_S3" Set it to '1' upload a release to the S3 bucket.
|
||||
# - "UPLOAD_TO_STORAGE" Set it to '1' upload a release to the Azure bucket.
|
||||
# Otherwise the release will be uploaded to the Github Releases.
|
||||
# (The value is only checked if "ELECTRON_RELEASE" is defined.)
|
||||
#
|
||||
@@ -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]) {
|
||||
@@ -183,34 +173,21 @@ build_script:
|
||||
- ninja -C out/Default third_party/electron_node:headers
|
||||
- python %LOCAL_GOMA_DIR%\goma_ctl.py stat
|
||||
- python3 electron/build/profile_toolchain.py --output-json=out/Default/windows_toolchain_profile.json
|
||||
- appveyor PushArtifact out/Default/windows_toolchain_profile.json
|
||||
- appveyor PushArtifact out/Default/dist.zip
|
||||
- appveyor PushArtifact out/Default/shell_browser_ui_unittests.exe
|
||||
- appveyor PushArtifact out/Default/chromedriver.zip
|
||||
- appveyor PushArtifact out/ffmpeg/ffmpeg.zip
|
||||
- 7z a node_headers.zip out\Default\gen\node_headers
|
||||
- appveyor PushArtifact node_headers.zip
|
||||
- appveyor PushArtifact out/Default/mksnapshot.zip
|
||||
- appveyor PushArtifact out/Default/hunspell_dictionaries.zip
|
||||
- appveyor PushArtifact out/Default/electron.lib
|
||||
# Temporarily disable symbol generation on 32-bit Windows due to failures
|
||||
- ps: >-
|
||||
if ($env:GN_CONFIG -eq 'release' -And $env:TARGET_ARCH -ne 'ia32') {
|
||||
if ($env:GN_CONFIG -eq 'release') {
|
||||
# Needed for msdia140.dll on 64-bit windows
|
||||
$env:Path += ";$pwd\third_party\llvm-build\Release+Asserts\bin"
|
||||
ninja -C out/Default electron:electron_symbols
|
||||
}
|
||||
- ps: >-
|
||||
if ($env:GN_CONFIG -eq 'release') {
|
||||
if ($env:TARGET_ARCH -ne 'ia32') {
|
||||
python electron\script\zip-symbols.py
|
||||
appveyor-retry appveyor PushArtifact out/Default/symbols.zip
|
||||
}
|
||||
python electron\script\zip-symbols.py
|
||||
appveyor-retry appveyor PushArtifact out/Default/symbols.zip
|
||||
} else {
|
||||
# It's useful to have pdb files when debugging testing builds that are
|
||||
# built on CI.
|
||||
7z a pdb.zip out\Default\*.pdb
|
||||
appveyor-retry appveyor PushArtifact pdb.zip
|
||||
}
|
||||
- python electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.win.%TARGET_ARCH%.manifest
|
||||
test_script:
|
||||
@@ -239,14 +216,13 @@ test_script:
|
||||
- echo "Done verifying mksnapshot"
|
||||
- if "%RUN_TESTS%"=="true" ( echo Verifying chromedriver & python electron\script\verify-chromedriver.py --build-dir out\Default --source-root %cd% )
|
||||
- echo "Done verifying chromedriver"
|
||||
- if exist %cd%\electron.log ( appveyor-retry appveyor PushArtifact %cd%\electron.log )
|
||||
deploy_script:
|
||||
- cd electron
|
||||
- ps: >-
|
||||
if (Test-Path Env:\ELECTRON_RELEASE) {
|
||||
if (Test-Path Env:\UPLOAD_TO_S3) {
|
||||
Write-Output "Uploading Electron release distribution to s3"
|
||||
& python script\release\uploaders\upload.py --verbose --upload_to_s3
|
||||
if (Test-Path Env:\UPLOAD_TO_STORAGE) {
|
||||
Write-Output "Uploading Electron release distribution to azure"
|
||||
& python script\release\uploaders\upload.py --verbose --upload_to_storage
|
||||
} else {
|
||||
Write-Output "Uploading Electron release distribution to github releases"
|
||||
& python script\release\uploaders\upload.py --verbose
|
||||
@@ -255,4 +231,18 @@ deploy_script:
|
||||
node script/release/ci-release-build.js --job=electron-woa-testing --ci=VSTS --armTest --appveyorJobId=$env:APPVEYOR_JOB_ID $env:APPVEYOR_REPO_BRANCH
|
||||
}
|
||||
on_finish:
|
||||
- if exist src\electron\electron.log ( appveyor-retry appveyor PushArtifact src\electron\electron.log )
|
||||
- cd ..
|
||||
- if exist out\Default\windows_toolchain_profile.json ( appveyor-retry appveyor PushArtifact out\Default\windows_toolchain_profile.json )
|
||||
- if exist out\Default\dist.zip (appveyor-retry appveyor PushArtifact out\Default\dist.zip)
|
||||
- if exist out\Default\shell_browser_ui_unittests.exe (appveyor-retry appveyor PushArtifact out\Default\shell_browser_ui_unittests.exe)
|
||||
- if exist out\Default\chromedriver.zip (appveyor-retry appveyor PushArtifact out\Default\chromedriver.zip)
|
||||
- if exist out\ffmpeg\ffmpeg.zip (appveyor-retry appveyor PushArtifact out\ffmpeg\ffmpeg.zip)
|
||||
- if exist node_headers.zip (appveyor-retry appveyor PushArtifact node_headers.zip)
|
||||
- if exist out\Default\mksnapshot.zip (appveyor-retry appveyor PushArtifact out\Default\mksnapshot.zip)
|
||||
- if exist out\Default\hunspell_dictionaries.zip (appveyor-retry appveyor PushArtifact out\Default\hunspell_dictionaries.zip)
|
||||
- if exist out\Default\electron.lib (appveyor-retry appveyor PushArtifact out\Default\electron.lib)
|
||||
- ps: >-
|
||||
if ((Test-Path "pdb.zip") -And ($env:GN_CONFIG -ne 'release')) {
|
||||
appveyor-retry appveyor PushArtifact pdb.zip
|
||||
}
|
||||
- if exist electron\electron.log ( appveyor-retry appveyor PushArtifact electron\electron.log )
|
||||
|
||||
@@ -21,6 +21,9 @@ proprietary_codecs = true
|
||||
ffmpeg_branding = "Chrome"
|
||||
|
||||
enable_basic_printing = true
|
||||
|
||||
# Removes DLLs from the build, which are only meant to be used for Chromium development.
|
||||
# See https://github.com/electron/electron/pull/17985
|
||||
angle_enable_vulkan_validation_layers = false
|
||||
dawn_enable_vulkan_validation_layers = false
|
||||
|
||||
|
||||
@@ -484,7 +484,6 @@ Returns:
|
||||
* `argv` string[] - An array of the second instance's command line arguments
|
||||
* `workingDirectory` string - The second instance's working directory
|
||||
* `additionalData` unknown - A JSON object of additional data passed from the second instance
|
||||
* `ackCallback` unknown - A function that can be used to send data back to the second instance
|
||||
|
||||
This event will be emitted inside the primary instance of your application
|
||||
when a second instance has been executed and calls `app.requestSingleInstanceLock()`.
|
||||
@@ -496,35 +495,12 @@ non-minimized.
|
||||
|
||||
**Note:** If the second instance is started by a different user than the first, the `argv` array will not include the arguments.
|
||||
|
||||
**Note:** `ackCallback` allows the user to send data back to the
|
||||
second instance during the `app.requestSingleInstanceLock()` flow.
|
||||
This callback can be used for cases where the second instance
|
||||
needs to obtain additional information from the first instance
|
||||
before quitting.
|
||||
|
||||
Currently, the limit on the message size is kMaxMessageLength,
|
||||
or around 32kB. To be safe, keep the amount of data passed to 31kB at most.
|
||||
|
||||
In order to call the callback, `event.preventDefault()` must be called, first.
|
||||
If the callback is not called in either case, `null` will be sent back.
|
||||
If `event.preventDefault()` is not called, but `ackCallback` is called
|
||||
by the user in the event, then the behaviour is undefined.
|
||||
|
||||
This event is guaranteed to be emitted after the `ready` event of `app`
|
||||
gets emitted.
|
||||
|
||||
**Note:** Extra command line arguments might be added by Chromium,
|
||||
such as `--original-process-start-time`.
|
||||
|
||||
### Event: 'first-instance-ack'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `additionalData` unknown - A JSON object of additional data passed from the first instance, in response to the first instance's `second-instance` event.
|
||||
|
||||
This event will be emitted within the second instance during the call to `app.requestSingleInstanceLock()`, when the first instance calls the `ackCallback` provided by the `second-instance` event handler.
|
||||
|
||||
## Methods
|
||||
|
||||
The `app` object has the following methods:
|
||||
@@ -985,33 +961,21 @@ starts:
|
||||
const { app } = require('electron')
|
||||
let myWindow = null
|
||||
|
||||
app.on('first-instance-ack', (event, additionalData) => {
|
||||
// Print out the ack received from the first instance.
|
||||
// Note this event handler must come before the requestSingleInstanceLock call.
|
||||
// Expected output: '{"myAckKey":"myAckValue"}'
|
||||
console.log(JSON.stringify(additionalData))
|
||||
})
|
||||
|
||||
const additionalData = { myKey: 'myValue' }
|
||||
const gotTheLock = app.requestSingleInstanceLock(additionalData)
|
||||
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory, additionalData, ackCallback) => {
|
||||
// We must call preventDefault if we're sending back data.
|
||||
event.preventDefault()
|
||||
app.on('second-instance', (event, commandLine, workingDirectory, additionalData) => {
|
||||
// Print out data received from the second instance.
|
||||
// Expected output: '{"myKey":"myValue"}'
|
||||
console.log(JSON.stringify(additionalData))
|
||||
console.log(additionalData)
|
||||
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (myWindow) {
|
||||
if (myWindow.isMinimized()) myWindow.restore()
|
||||
myWindow.focus()
|
||||
}
|
||||
const ackData = { myAckKey: 'myAckValue' }
|
||||
ackCallback(ackData)
|
||||
})
|
||||
|
||||
// Create myWindow, load the rest of the app, etc...
|
||||
|
||||
@@ -18,8 +18,8 @@ The `safeStorage` module has the following methods:
|
||||
|
||||
Returns `boolean` - Whether encryption is available.
|
||||
|
||||
On Linux, returns true if the secret key is
|
||||
available. On MacOS, returns true if Keychain is available.
|
||||
On Linux, returns true if the app has emitted the `ready` event and the secret key is available.
|
||||
On MacOS, returns true if Keychain is available.
|
||||
On Windows, returns true once the app has emitted the `ready` event.
|
||||
|
||||
### `safeStorage.encryptString(plainText)`
|
||||
|
||||
@@ -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")'
|
||||
|
||||
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 |
@@ -22,11 +22,12 @@ check out our [Electron Versioning](./electron-versioning.md) doc.
|
||||
| 12.0.0 | -- | 2020-Nov-19 | 2021-Mar-02 | M89 | v14.16 | 🚫 |
|
||||
| 13.0.0 | -- | 2021-Mar-04 | 2021-May-25 | M91 | v14.16 | 🚫 |
|
||||
| 14.0.0 | -- | 2021-May-27 | 2021-Aug-31 | M93 | v14.17 | 🚫 |
|
||||
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | M94 | v16.5 | ✅ |
|
||||
| 16.0.0 | 2021-Sep-23 | 2021-Oct-20 | 2021-Nov-16 | M96 | v16.9 | ✅ |
|
||||
| 15.0.0 | 2021-Jul-20 | 2021-Sep-01 | 2021-Sep-21 | M94 | v16.5 | 🚫 |
|
||||
| 16.0.0 | 2021-Sep-23 | 2021-Oct-20 | 2021-Nov-16 | M96 | v16.9 | 🚫 |
|
||||
| 17.0.0 | 2021-Nov-18 | 2022-Jan-06 | 2022-Feb-01 | M98 | v16.13 | ✅ |
|
||||
| 18.0.0 | 2022-Feb-03 | 2022-Mar-03 | 2022-Mar-29 | M100 | v16.13 | ✅ |
|
||||
| 19.0.0 | 2022-Mar-31 | 2022-Apr-26 | 2022-May-24 | M102 | TBD | ✅ |
|
||||
| 19.0.0 | 2022-Mar-31 | 2022-Apr-26 | 2022-May-24 | M102 | v16.14 | ✅ |
|
||||
| 20.0.0 | 2022-May-26 | 2022-Jun-21 | 2022-Aug-02 | M104 | TBD | ✅ |
|
||||
|
||||
**Notes:**
|
||||
|
||||
|
||||
@@ -131,7 +131,6 @@ folder of your project:
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
@@ -427,7 +426,6 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
<meta charset="UTF-8">
|
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -177,6 +177,7 @@ template("electron_paks") {
|
||||
"${root_gen_dir}/third_party/blink/public/strings/blink_accessibility_strings_",
|
||||
"${root_gen_dir}/third_party/blink/public/strings/blink_strings_",
|
||||
"${root_gen_dir}/device/bluetooth/strings/bluetooth_strings_",
|
||||
"${root_gen_dir}/extensions/strings/extensions_strings_",
|
||||
"${root_gen_dir}/services/strings/services_strings_",
|
||||
"${root_gen_dir}/ui/strings/app_locale_settings_",
|
||||
"${root_gen_dir}/ui/strings/ax_strings_",
|
||||
@@ -186,6 +187,7 @@ template("electron_paks") {
|
||||
"//chrome/app/resources:platform_locale_settings",
|
||||
"//components/strings:components_strings",
|
||||
"//device/bluetooth/strings",
|
||||
"//extensions/strings",
|
||||
"//services/strings",
|
||||
"//third_party/blink/public/strings",
|
||||
"//third_party/blink/public/strings:accessibility_strings",
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
<message name="IDS_DEFAULT_PRINT_DOCUMENT_TITLE" desc="Default title for a print document">
|
||||
Untitled Document
|
||||
</message>
|
||||
<message name="IDS_UTILITY_PROCESS_PRINT_BACKEND_SERVICE_NAME" desc="The name of the utility process used for backend interactions with printer drivers.">
|
||||
Print Backend Service
|
||||
</message>
|
||||
|
||||
<!-- Desktop Capturer API -->
|
||||
<message name="IDS_DESKTOP_MEDIA_PICKER_SINGLE_SCREEN_NAME" desc="Name for screens in the desktop media picker UI when there is only one monitor.">
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -78,6 +78,19 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition
|
||||
...browserWindowOptions
|
||||
});
|
||||
|
||||
if (!guest) {
|
||||
// When we open a new window from a link (via OpenURLFromTab),
|
||||
// the browser process is responsible for initiating navigation
|
||||
// in the new window.
|
||||
window.loadURL(url, {
|
||||
httpReferrer: referrer,
|
||||
...(postData && {
|
||||
postData,
|
||||
extraHeaders: formatPostDataHeaders(postData as Electron.UploadRawData[])
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
handleWindowLifecycleEvents({ embedder, frameName, guest: window, outlivesOpener });
|
||||
|
||||
embedder.emit('did-create-window', window, { url, frameName, options: browserWindowOptions, disposition, referrer, postData });
|
||||
@@ -243,6 +256,15 @@ export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {
|
||||
};
|
||||
}
|
||||
|
||||
function formatPostDataHeaders (postData: PostData) {
|
||||
if (!postData) return;
|
||||
|
||||
const { contentType, boundary } = parseContentTypeFormat(postData);
|
||||
if (boundary != null) { return `content-type: ${contentType}; boundary=${boundary}`; }
|
||||
|
||||
return `content-type: ${contentType}`;
|
||||
}
|
||||
|
||||
const MULTIPART_CONTENT_TYPE = 'multipart/form-data';
|
||||
const URL_ENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded';
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { webFrame } from 'electron';
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
const { mainFrame: webFrame } = process._linkedBinding('electron_renderer_web_frame');
|
||||
|
||||
let shouldLog: boolean | null = null;
|
||||
|
||||
const { platform, execPath, env } = process;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "19.0.0-beta.5",
|
||||
"version": "19.0.7",
|
||||
"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 --"
|
||||
|
||||
@@ -102,12 +102,12 @@ chore_do_not_use_chrome_windows_in_cryptotoken_webrequestsender.patch
|
||||
process_singleton.patch
|
||||
fix_expose_decrementcapturercount_in_web_contents_impl.patch
|
||||
add_ui_scopedcliboardwriter_writeunsaferawdata.patch
|
||||
feat_add_data_parameter_to_processsingleton.patch
|
||||
mas_gate_private_enterprise_APIs.patch
|
||||
load_v8_snapshot_in_browser_process.patch
|
||||
fix_patch_out_permissions_checks_in_exclusive_access.patch
|
||||
fix_adapt_exclusive_access_for_electron_needs.patch
|
||||
fix_aspect_ratio_with_max_size.patch
|
||||
fix_dont_delete_SerialPortManager_on_main_thread.patch
|
||||
feat_add_data_transfer_to_requestsingleinstancelock.patch
|
||||
fix_crash_when_saving_edited_pdf_files.patch
|
||||
port_autofill_colors_to_the_color_pipeline.patch
|
||||
build_disable_partition_alloc_on_mac.patch
|
||||
@@ -117,3 +117,6 @@ introduce_ozoneplatform_electron_can_call_x11_property.patch
|
||||
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
|
||||
|
||||
@@ -33,10 +33,10 @@ index 3c40d999a9545051e91a9f0ad3bf7ca2a95d80c4..b5a20be5e22238e7e1969bdaf52c0b05
|
||||
"//base",
|
||||
"//build:branding_buildflags",
|
||||
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
|
||||
index 1d80724ace438fb9d2c20ff78d87a93053cf7420..840548c9685c2e3bd584ea8f48d02d21ac541c1b 100644
|
||||
index 948d05358b89895faa97a7d383bfac66a45464a8..43859d267010103daf80525a5af0f312a577411c 100644
|
||||
--- a/chrome/browser/BUILD.gn
|
||||
+++ b/chrome/browser/BUILD.gn
|
||||
@@ -4559,7 +4559,7 @@ static_library("browser") {
|
||||
@@ -4560,7 +4560,7 @@ static_library("browser") {
|
||||
|
||||
# On Windows, the hashes are embedded in //chrome:chrome_initial rather
|
||||
# than here in :chrome_dll.
|
||||
|
||||
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);
|
||||
@@ -7,10 +7,10 @@ spellchecker uses a few IDS_ resources. We need to load these from
|
||||
Electrons grit header instead of Chromes
|
||||
|
||||
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
|
||||
index 86bbdc45c6911c7249cb898f38d8e262fa1646c1..1d80724ace438fb9d2c20ff78d87a93053cf7420 100644
|
||||
index af2fd82cbc458b396107a161d9c3dd31077e57a7..948d05358b89895faa97a7d383bfac66a45464a8 100644
|
||||
--- a/chrome/browser/BUILD.gn
|
||||
+++ b/chrome/browser/BUILD.gn
|
||||
@@ -7181,6 +7181,7 @@ static_library("browser") {
|
||||
@@ -7182,6 +7182,7 @@ static_library("browser") {
|
||||
deps += [
|
||||
"//components/spellcheck/browser",
|
||||
"//components/spellcheck/common",
|
||||
|
||||
53
patches/chromium/custom_protocols_plzserviceworker.patch
Normal file
53
patches/chromium/custom_protocols_plzserviceworker.patch
Normal file
@@ -0,0 +1,53 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Fri, 20 May 2022 00:29:34 +0900
|
||||
Subject: custom_protocols_plzserviceworker.patch
|
||||
|
||||
Allow registering custom protocols to handle service worker main script fetching with PlzServiceWorker.
|
||||
|
||||
Refs https://bugs.chromium.org/p/chromium/issues/detail?id=996511
|
||||
|
||||
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
|
||||
index f827745b9800d4f11a17e405b0eb22601d7e4dd4..0cecbfb2a6fd3daa71338c94bda4b1f45eb51d5e 100644
|
||||
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
|
||||
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
|
||||
@@ -1591,6 +1591,28 @@ ServiceWorkerContextWrapper::GetLoaderFactoryForBrowserInitiatedRequest(
|
||||
loader_factory_bundle_info =
|
||||
context()->loader_factory_bundle_for_update_check()->Clone();
|
||||
|
||||
+ // Give the embedder a chance to register custom schemes that can
|
||||
+ // handle loading the service worker main script.
|
||||
+ // Previous registration triggered by
|
||||
+ // ServiceWorkerContextWrapper::CreateNonNetworkPendingURLLoaderFactoryBundleForUpdateCheck
|
||||
+ // happens early on browser startup before the JS in the main process
|
||||
+ // is run by the embedder.
|
||||
+ auto* factory_bundle = static_cast<blink::PendingURLLoaderFactoryBundle*>(
|
||||
+ loader_factory_bundle_info.get());
|
||||
+ ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories;
|
||||
+ GetContentClient()
|
||||
+ ->browser()
|
||||
+ ->RegisterNonNetworkServiceWorkerUpdateURLLoaderFactories(
|
||||
+ storage_partition_->browser_context(), &non_network_factories);
|
||||
+ for (auto& pair : non_network_factories) {
|
||||
+ const std::string& scheme = pair.first;
|
||||
+ mojo::PendingRemote<network::mojom::URLLoaderFactory>& factory_remote =
|
||||
+ pair.second;
|
||||
+
|
||||
+ factory_bundle->pending_scheme_specific_factories().emplace(
|
||||
+ scheme, std::move(factory_remote));
|
||||
+ }
|
||||
+
|
||||
if (base::FeatureList::IsEnabled(
|
||||
features::kEnableServiceWorkersForChromeUntrusted) &&
|
||||
scope.scheme_piece() == kChromeUIUntrustedScheme) {
|
||||
@@ -1611,9 +1633,7 @@ ServiceWorkerContextWrapper::GetLoaderFactoryForBrowserInitiatedRequest(
|
||||
browser_context(), scope_origin)) {
|
||||
config->RegisterURLDataSource(browser_context());
|
||||
|
||||
- static_cast<blink::PendingURLLoaderFactoryBundle*>(
|
||||
- loader_factory_bundle_info.get())
|
||||
- ->pending_scheme_specific_factories()
|
||||
+ factory_bundle->pending_scheme_specific_factories()
|
||||
.emplace(kChromeUIUntrustedScheme,
|
||||
CreateWebUIServiceWorkerLoaderFactory(
|
||||
browser_context(), kChromeUIUntrustedScheme,
|
||||
@@ -0,0 +1,348 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Raymond Zhao <raymondzhao@microsoft.com>
|
||||
Date: Tue, 7 Sep 2021 14:54:25 -0700
|
||||
Subject: feat: Add data parameter to ProcessSingleton
|
||||
|
||||
This patch adds an additional_data parameter to the constructor of
|
||||
ProcessSingleton, so that the second instance can send additional
|
||||
data over to the first instance while requesting the ProcessSingleton
|
||||
lock.
|
||||
|
||||
On the Electron side, we then expose an extra parameter to the
|
||||
app.requestSingleInstanceLock API so that users can pass in a JSON
|
||||
object for the second instance to send to the first instance.
|
||||
|
||||
diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
|
||||
index 5a64220aaf1309832dc0ad543e353de67fe0a779..e75c4f0d7cf1cac2e5862eb858800359e2001eb6 100644
|
||||
--- a/chrome/browser/process_singleton.h
|
||||
+++ b/chrome/browser/process_singleton.h
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/process/process.h"
|
||||
+#include "base/containers/span.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
|
||||
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
|
||||
@@ -99,22 +100,25 @@ class ProcessSingleton {
|
||||
// handled within the current browser instance or false if the remote process
|
||||
// should handle it (i.e., because the current process is shutting down).
|
||||
using NotificationCallback =
|
||||
- base::RepeatingCallback<bool(const base::CommandLine& command_line,
|
||||
- const base::FilePath& current_directory)>;
|
||||
+ base::RepeatingCallback<bool(const base::CommandLine& command_line,
|
||||
+ const base::FilePath& current_directory,
|
||||
+ const std::vector<const uint8_t> additional_data)>;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
ProcessSingleton(const std::string& program_name,
|
||||
const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
bool is_sandboxed,
|
||||
const NotificationCallback& notification_callback);
|
||||
#else
|
||||
ProcessSingleton(const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
const NotificationCallback& notification_callback);
|
||||
+#endif
|
||||
|
||||
ProcessSingleton(const ProcessSingleton&) = delete;
|
||||
ProcessSingleton& operator=(const ProcessSingleton&) = delete;
|
||||
|
||||
-#endif
|
||||
~ProcessSingleton();
|
||||
|
||||
// Notify another process, if available. Otherwise sets ourselves as the
|
||||
@@ -177,7 +181,10 @@ class ProcessSingleton {
|
||||
#endif
|
||||
|
||||
private:
|
||||
- NotificationCallback notification_callback_; // Handler for notifications.
|
||||
+ // A callback to run when the first instance receives data from the second.
|
||||
+ NotificationCallback notification_callback_;
|
||||
+ // Custom data to pass to the other instance during notify.
|
||||
+ base::span<const uint8_t> additional_data_;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
bool EscapeVirtualization(const base::FilePath& user_data_dir);
|
||||
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
|
||||
index be2c417c07a4206fac4a9a6c03e516fd0493c942..9a1f7b80e9ec538977f9b492829646a62d2a67cd 100644
|
||||
--- a/chrome/browser/process_singleton_posix.cc
|
||||
+++ b/chrome/browser/process_singleton_posix.cc
|
||||
@@ -612,6 +612,7 @@ class ProcessSingleton::LinuxWatcher
|
||||
// |reader| is for sending back ACK message.
|
||||
void HandleMessage(const std::string& current_dir,
|
||||
const std::vector<std::string>& argv,
|
||||
+ const std::vector<const uint8_t> additional_data,
|
||||
SocketReader* reader);
|
||||
|
||||
private:
|
||||
@@ -666,13 +667,16 @@ void ProcessSingleton::LinuxWatcher::StartListening(int socket) {
|
||||
}
|
||||
|
||||
void ProcessSingleton::LinuxWatcher::HandleMessage(
|
||||
- const std::string& current_dir, const std::vector<std::string>& argv,
|
||||
+ const std::string& current_dir,
|
||||
+ const std::vector<std::string>& argv,
|
||||
+ const std::vector<const uint8_t> additional_data,
|
||||
SocketReader* reader) {
|
||||
DCHECK(ui_task_runner_->BelongsToCurrentThread());
|
||||
DCHECK(reader);
|
||||
|
||||
if (parent_->notification_callback_.Run(base::CommandLine(argv),
|
||||
- base::FilePath(current_dir))) {
|
||||
+ base::FilePath(current_dir),
|
||||
+ std::move(additional_data))) {
|
||||
// Send back "ACK" message to prevent the client process from starting up.
|
||||
reader->FinishWithACK(kACKToken, std::size(kACKToken) - 1);
|
||||
} else {
|
||||
@@ -720,7 +724,8 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
|
||||
}
|
||||
}
|
||||
|
||||
- // Validate the message. The shortest message is kStartToken\0x\0x
|
||||
+ // Validate the message. The shortest message kStartToken\0\00
|
||||
+ // The shortest message with additional data is kStartToken\0\00\00\0.
|
||||
const size_t kMinMessageLength = std::size(kStartToken) + 4;
|
||||
if (bytes_read_ < kMinMessageLength) {
|
||||
buf_[bytes_read_] = 0;
|
||||
@@ -750,10 +755,28 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
|
||||
tokens.erase(tokens.begin());
|
||||
tokens.erase(tokens.begin());
|
||||
|
||||
+ size_t num_args;
|
||||
+ base::StringToSizeT(tokens[0], &num_args);
|
||||
+ std::vector<std::string> command_line(tokens.begin() + 1, tokens.begin() + 1 + num_args);
|
||||
+
|
||||
+ std::vector<const uint8_t> additional_data;
|
||||
+ if (tokens.size() >= 3 + num_args) {
|
||||
+ size_t additional_data_size;
|
||||
+ base::StringToSizeT(tokens[1 + num_args], &additional_data_size);
|
||||
+ std::string remaining_args = base::JoinString(
|
||||
+ base::make_span(tokens.begin() + 2 + num_args, tokens.end()),
|
||||
+ std::string(1, kTokenDelimiter));
|
||||
+ const uint8_t* additional_data_bits =
|
||||
+ reinterpret_cast<const uint8_t*>(remaining_args.c_str());
|
||||
+ additional_data = std::vector<const uint8_t>(
|
||||
+ additional_data_bits, additional_data_bits + additional_data_size);
|
||||
+ }
|
||||
+
|
||||
// Return to the UI thread to handle opening a new browser tab.
|
||||
ui_task_runner_->PostTask(
|
||||
FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::HandleMessage,
|
||||
- parent_, current_dir, tokens, this));
|
||||
+ parent_, current_dir, command_line,
|
||||
+ std::move(additional_data), this));
|
||||
fd_watch_controller_.reset();
|
||||
|
||||
// LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
|
||||
@@ -782,8 +805,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
|
||||
//
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
const NotificationCallback& notification_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
+ additional_data_(additional_data),
|
||||
current_pid_(base::GetCurrentProcId()),
|
||||
watcher_(new LinuxWatcher(this)) {
|
||||
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
|
||||
@@ -902,7 +927,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
sizeof(socket_timeout));
|
||||
|
||||
// Found another process, prepare our command line
|
||||
- // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>".
|
||||
+ // format is "START\0<current-dir>\0<n-args>\0<argv[0]>\0...\0<argv[n]>
|
||||
+ // \0<additional-data-length>\0<additional-data>".
|
||||
std::string to_send(kStartToken);
|
||||
to_send.push_back(kTokenDelimiter);
|
||||
|
||||
@@ -912,11 +938,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
to_send.append(current_dir.value());
|
||||
|
||||
const std::vector<std::string>& argv = cmd_line.argv();
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(base::NumberToString(argv.size()));
|
||||
for (auto it = argv.begin(); it != argv.end(); ++it) {
|
||||
to_send.push_back(kTokenDelimiter);
|
||||
to_send.append(*it);
|
||||
}
|
||||
|
||||
+ size_t data_to_send_size = additional_data_.size_bytes();
|
||||
+ if (data_to_send_size) {
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(base::NumberToString(data_to_send_size));
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(reinterpret_cast<const char*>(additional_data_.data()), data_to_send_size);
|
||||
+ }
|
||||
+
|
||||
// Send the message
|
||||
if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
|
||||
// Try to kill the other process, because it might have been dead.
|
||||
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
|
||||
index ec725b44296266bea1a51aea889463a0bba8449c..beb2925f2fd7a1a93435bf4b1a40b5c71b49e449 100644
|
||||
--- a/chrome/browser/process_singleton_win.cc
|
||||
+++ b/chrome/browser/process_singleton_win.cc
|
||||
@@ -80,10 +80,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
|
||||
|
||||
bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||
base::CommandLine* parsed_command_line,
|
||||
- base::FilePath* current_directory) {
|
||||
+ base::FilePath* current_directory,
|
||||
+ std::vector<const uint8_t>* parsed_additional_data) {
|
||||
// We should have enough room for the shortest command (min_message_size)
|
||||
// and also be a multiple of wchar_t bytes. The shortest command
|
||||
- // possible is L"START\0\0" (empty current directory and command line).
|
||||
+ // possible is L"START\0\0" (empty command line, current directory,
|
||||
+ // and additional data).
|
||||
static const int min_message_size = 7;
|
||||
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
|
||||
cds->cbData % sizeof(wchar_t) != 0) {
|
||||
@@ -133,6 +135,37 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||
const std::wstring cmd_line =
|
||||
msg.substr(second_null + 1, third_null - second_null);
|
||||
*parsed_command_line = base::CommandLine::FromString(cmd_line);
|
||||
+
|
||||
+ const std::wstring::size_type fourth_null =
|
||||
+ msg.find_first_of(L'\0', third_null + 1);
|
||||
+ if (fourth_null == std::wstring::npos ||
|
||||
+ fourth_null == msg.length()) {
|
||||
+ // No additional data was provided.
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // Get length of the additional data.
|
||||
+ const std::wstring additional_data_length_string =
|
||||
+ msg.substr(third_null + 1, fourth_null - third_null);
|
||||
+ size_t additional_data_length;
|
||||
+ base::StringToSizeT(additional_data_length_string, &additional_data_length);
|
||||
+
|
||||
+ const std::wstring::size_type fifth_null =
|
||||
+ msg.find_first_of(L'\0', fourth_null + 1);
|
||||
+ if (fifth_null == std::wstring::npos ||
|
||||
+ fifth_null == msg.length()) {
|
||||
+ LOG(WARNING) << "Invalid format for start command, we need a string in 6 "
|
||||
+ "parts separated by NULLs";
|
||||
+ }
|
||||
+
|
||||
+ // Get the actual additional data.
|
||||
+ const std::wstring additional_data =
|
||||
+ msg.substr(fourth_null + 1, fifth_null - fourth_null);
|
||||
+ const uint8_t* additional_data_bytes =
|
||||
+ reinterpret_cast<const uint8_t*>(additional_data.c_str());
|
||||
+ *parsed_additional_data = std::vector<const uint8_t>(additional_data_bytes,
|
||||
+ additional_data_bytes + additional_data_length);
|
||||
+
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -154,13 +187,14 @@ bool ProcessLaunchNotification(
|
||||
|
||||
base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
|
||||
base::FilePath current_directory;
|
||||
- if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) {
|
||||
+ std::vector<const uint8_t> additional_data;
|
||||
+ if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory, &additional_data)) {
|
||||
*result = TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
- *result = notification_callback.Run(parsed_command_line, current_directory) ?
|
||||
- TRUE : FALSE;
|
||||
+ *result = notification_callback.Run(parsed_command_line,
|
||||
+ current_directory, std::move(additional_data)) ? TRUE : FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -261,9 +295,11 @@ bool ProcessSingleton::EscapeVirtualization(
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const std::string& program_name,
|
||||
const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
bool is_app_sandboxed,
|
||||
const NotificationCallback& notification_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
+ additional_data_(additional_data),
|
||||
program_name_(program_name),
|
||||
is_app_sandboxed_(is_app_sandboxed),
|
||||
is_virtualized_(false),
|
||||
@@ -290,7 +326,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
||||
return PROCESS_NONE;
|
||||
}
|
||||
|
||||
- switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
|
||||
+ switch (chrome::AttemptToNotifyRunningChrome(remote_window_, additional_data_)) {
|
||||
case chrome::NOTIFY_SUCCESS:
|
||||
return PROCESS_NOTIFIED;
|
||||
case chrome::NOTIFY_FAILED:
|
||||
diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
|
||||
index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..cfdb2d75532d270e3dd548eb7475a6cdbddf1016 100644
|
||||
--- a/chrome/browser/win/chrome_process_finder.cc
|
||||
+++ b/chrome/browser/win/chrome_process_finder.cc
|
||||
@@ -36,7 +36,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
|
||||
return base::win::MessageWindow::FindWindow(user_data_dir.value());
|
||||
}
|
||||
|
||||
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
+NotifyChromeResult AttemptToNotifyRunningChrome(
|
||||
+ HWND remote_window,
|
||||
+ const base::span<const uint8_t> additional_data) {
|
||||
TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome");
|
||||
|
||||
DCHECK(remote_window);
|
||||
@@ -50,7 +52,8 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
}
|
||||
|
||||
// Send the command line to the remote chrome window.
|
||||
- // Format is "START\0<<<current directory>>>\0<<<commandline>>>".
|
||||
+ // Format is
|
||||
+ // "START\0<current-directory>\0<command-line>\0<additional-data-length>\0<additional-data>".
|
||||
std::wstring to_send(L"START\0", 6); // want the NULL in the string.
|
||||
base::FilePath cur_dir;
|
||||
if (!base::GetCurrentDirectory(&cur_dir)) {
|
||||
@@ -64,6 +67,22 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
base::CommandLine::ForCurrentProcess()->GetCommandLineString());
|
||||
to_send.append(L"\0", 1); // Null separator.
|
||||
|
||||
+ size_t additional_data_size = additional_data.size_bytes();
|
||||
+ if (additional_data_size) {
|
||||
+ // Send over the size, because the reinterpret cast to wchar_t could
|
||||
+ // add padding.
|
||||
+ to_send.append(base::UTF8ToWide(base::NumberToString(additional_data_size)));
|
||||
+ to_send.append(L"\0", 1); // Null separator.
|
||||
+
|
||||
+ size_t padded_size = additional_data_size / sizeof(wchar_t);
|
||||
+ if (additional_data_size % sizeof(wchar_t) != 0) {
|
||||
+ padded_size++;
|
||||
+ }
|
||||
+ to_send.append(reinterpret_cast<const wchar_t*>(additional_data.data()),
|
||||
+ padded_size);
|
||||
+ to_send.append(L"\0", 1); // Null separator.
|
||||
+ }
|
||||
+
|
||||
// Allow the current running browser window to make itself the foreground
|
||||
// window (otherwise it will just flash in the taskbar).
|
||||
::AllowSetForegroundWindow(process_id);
|
||||
diff --git a/chrome/browser/win/chrome_process_finder.h b/chrome/browser/win/chrome_process_finder.h
|
||||
index 5516673cee019f6060077091e59498bf9038cd6e..8edea5079b46c2cba67833114eb9c21d85cfc22d 100644
|
||||
--- a/chrome/browser/win/chrome_process_finder.h
|
||||
+++ b/chrome/browser/win/chrome_process_finder.h
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
+#include "base/containers/span.h"
|
||||
#include "base/time/time.h"
|
||||
|
||||
namespace base {
|
||||
@@ -27,7 +28,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir);
|
||||
// Attempts to send the current command line to an already running instance of
|
||||
// Chrome via a WM_COPYDATA message.
|
||||
// Returns true if a running Chrome is found and successfully notified.
|
||||
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window);
|
||||
+NotifyChromeResult AttemptToNotifyRunningChrome(
|
||||
+ HWND remote_window,
|
||||
+ const base::span<const uint8_t> additional_data);
|
||||
|
||||
// Changes the notification timeout to |new_timeout|, returns the old timeout.
|
||||
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout);
|
||||
@@ -1,612 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Raymond Zhao <raymondzhao@microsoft.com>
|
||||
Date: Tue, 7 Sep 2021 14:54:25 -0700
|
||||
Subject: feat: Add data transfer mechanism to requestSingleInstanceLock flow
|
||||
|
||||
This patch adds code that allows for the second instance to send
|
||||
additional data to the first instance, and for the first instance
|
||||
to send additional data back to the second instance, during the
|
||||
app.requestSingleInstanceLock call.
|
||||
|
||||
Firstly, this patch adds an additional_data parameter
|
||||
to the constructor of ProcessSingleton, so that the second instance
|
||||
can send additional data over to the first instance
|
||||
while requesting the ProcessSingleton lock.
|
||||
|
||||
Then, we add additional processing to the second-instance event, both
|
||||
so the first instance can receive additional data from the second
|
||||
instance, but also so the second instance can send back additional
|
||||
data to the first instance if needed.
|
||||
|
||||
diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
|
||||
index 5a64220aaf1309832dc0ad543e353de67fe0a779..5b701b1361707b610ed60c344e441e67ca701362 100644
|
||||
--- a/chrome/browser/process_singleton.h
|
||||
+++ b/chrome/browser/process_singleton.h
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/process/process.h"
|
||||
+#include "base/containers/span.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
|
||||
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
|
||||
@@ -93,6 +94,9 @@ class ProcessSingleton {
|
||||
|
||||
static constexpr int kNumNotifyResults = LAST_VALUE + 1;
|
||||
|
||||
+ using NotificationAckCallback =
|
||||
+ base::RepeatingCallback<void(const base::span<const uint8_t>* ack_data)>;
|
||||
+
|
||||
// Implement this callback to handle notifications from other processes. The
|
||||
// callback will receive the command line and directory with which the other
|
||||
// Chrome process was launched. Return true if the command line will be
|
||||
@@ -100,21 +104,27 @@ class ProcessSingleton {
|
||||
// should handle it (i.e., because the current process is shutting down).
|
||||
using NotificationCallback =
|
||||
base::RepeatingCallback<bool(const base::CommandLine& command_line,
|
||||
- const base::FilePath& current_directory)>;
|
||||
+ const base::FilePath& current_directory,
|
||||
+ const std::vector<uint8_t> additional_data,
|
||||
+ const NotificationAckCallback& ack_callback)>;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
ProcessSingleton(const std::string& program_name,
|
||||
const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
bool is_sandboxed,
|
||||
- const NotificationCallback& notification_callback);
|
||||
+ const NotificationCallback& notification_callback,
|
||||
+ const NotificationAckCallback& ack_notification_callback);
|
||||
#else
|
||||
ProcessSingleton(const base::FilePath& user_data_dir,
|
||||
- const NotificationCallback& notification_callback);
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
+ const NotificationCallback& notification_callback,
|
||||
+ const NotificationAckCallback& ack_notification_callback);
|
||||
+#endif
|
||||
|
||||
ProcessSingleton(const ProcessSingleton&) = delete;
|
||||
ProcessSingleton& operator=(const ProcessSingleton&) = delete;
|
||||
|
||||
-#endif
|
||||
~ProcessSingleton();
|
||||
|
||||
// Notify another process, if available. Otherwise sets ourselves as the
|
||||
@@ -177,7 +187,13 @@ class ProcessSingleton {
|
||||
#endif
|
||||
|
||||
private:
|
||||
- NotificationCallback notification_callback_; // Handler for notifications.
|
||||
+ // A callback to run when the first instance receives data from the second.
|
||||
+ NotificationCallback notification_callback_;
|
||||
+ // A callback to run when the second instance
|
||||
+ // receives an acknowledgement from the first.
|
||||
+ NotificationAckCallback notification_ack_callback_;
|
||||
+ // Custom data to pass to the other instance during notify.
|
||||
+ base::span<const uint8_t> additional_data_;
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
bool EscapeVirtualization(const base::FilePath& user_data_dir);
|
||||
@@ -190,6 +206,7 @@ class ProcessSingleton {
|
||||
HANDLE lock_file_;
|
||||
base::FilePath user_data_dir_;
|
||||
ShouldKillRemoteProcessCallback should_kill_remote_process_callback_;
|
||||
+ HANDLE ack_pipe_;
|
||||
#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
|
||||
// Return true if the given pid is one of our child processes.
|
||||
// Assumes that the current pid is the root of all pids of the current
|
||||
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
|
||||
index be2c417c07a4206fac4a9a6c03e516fd0493c942..78f74b0b21242553b6af98628dc48190f7d1137d 100644
|
||||
--- a/chrome/browser/process_singleton_posix.cc
|
||||
+++ b/chrome/browser/process_singleton_posix.cc
|
||||
@@ -146,7 +146,7 @@ const char kACKToken[] = "ACK";
|
||||
const char kShutdownToken[] = "SHUTDOWN";
|
||||
const char kTokenDelimiter = '\0';
|
||||
const int kMaxMessageLength = 32 * 1024;
|
||||
-const int kMaxACKMessageLength = std::size(kShutdownToken) - 1;
|
||||
+const int kMaxACKMessageLength = kMaxMessageLength;
|
||||
|
||||
bool g_disable_prompt = false;
|
||||
bool g_skip_is_chrome_process_check = false;
|
||||
@@ -612,6 +612,7 @@ class ProcessSingleton::LinuxWatcher
|
||||
// |reader| is for sending back ACK message.
|
||||
void HandleMessage(const std::string& current_dir,
|
||||
const std::vector<std::string>& argv,
|
||||
+ const std::vector<uint8_t> additional_data,
|
||||
SocketReader* reader);
|
||||
|
||||
private:
|
||||
@@ -636,6 +637,9 @@ class ProcessSingleton::LinuxWatcher
|
||||
// The ProcessSingleton that owns us.
|
||||
ProcessSingleton* const parent_;
|
||||
|
||||
+ bool ack_callback_called_ = false;
|
||||
+ void AckCallback(SocketReader* reader, const base::span<const uint8_t>* response);
|
||||
+
|
||||
std::set<std::unique_ptr<SocketReader>, base::UniquePtrComparator> readers_;
|
||||
};
|
||||
|
||||
@@ -666,16 +670,21 @@ void ProcessSingleton::LinuxWatcher::StartListening(int socket) {
|
||||
}
|
||||
|
||||
void ProcessSingleton::LinuxWatcher::HandleMessage(
|
||||
- const std::string& current_dir, const std::vector<std::string>& argv,
|
||||
+ const std::string& current_dir,
|
||||
+ const std::vector<std::string>& argv,
|
||||
+ const std::vector<uint8_t> additional_data,
|
||||
SocketReader* reader) {
|
||||
DCHECK(ui_task_runner_->BelongsToCurrentThread());
|
||||
DCHECK(reader);
|
||||
|
||||
- if (parent_->notification_callback_.Run(base::CommandLine(argv),
|
||||
- base::FilePath(current_dir))) {
|
||||
- // Send back "ACK" message to prevent the client process from starting up.
|
||||
- reader->FinishWithACK(kACKToken, std::size(kACKToken) - 1);
|
||||
- } else {
|
||||
+ auto wrapped_ack_callback =
|
||||
+ base::BindRepeating(&ProcessSingleton::LinuxWatcher::AckCallback,
|
||||
+ base::Unretained(this), reader);
|
||||
+ ack_callback_called_ = false;
|
||||
+ if (!parent_->notification_callback_.Run(base::CommandLine(argv),
|
||||
+ base::FilePath(current_dir),
|
||||
+ std::move(additional_data),
|
||||
+ wrapped_ack_callback)) {
|
||||
LOG(WARNING) << "Not handling interprocess notification as browser"
|
||||
" is shutting down";
|
||||
// Send back "SHUTDOWN" message, so that the client process can start up
|
||||
@@ -685,6 +694,22 @@ void ProcessSingleton::LinuxWatcher::HandleMessage(
|
||||
}
|
||||
}
|
||||
|
||||
+void ProcessSingleton::LinuxWatcher::AckCallback(
|
||||
+ SocketReader* reader,
|
||||
+ const base::span<const uint8_t>* response) {
|
||||
+ // Send back "ACK" message to prevent the client process from starting up.
|
||||
+ if (!ack_callback_called_) {
|
||||
+ ack_callback_called_ = true;
|
||||
+ std::string ack_message;
|
||||
+ ack_message.append(kACKToken, std::size(kACKToken) - 1);
|
||||
+ if (response && response->size_bytes()) {
|
||||
+ ack_message.append(reinterpret_cast<const char*>(response->data()),
|
||||
+ response->size_bytes());
|
||||
+ }
|
||||
+ reader->FinishWithACK(ack_message.c_str(), ack_message.size());
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void ProcessSingleton::LinuxWatcher::RemoveSocketReader(SocketReader* reader) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
DCHECK(reader);
|
||||
@@ -720,7 +745,8 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
|
||||
}
|
||||
}
|
||||
|
||||
- // Validate the message. The shortest message is kStartToken\0x\0x
|
||||
+ // Validate the message. The shortest message kStartToken\0\00
|
||||
+ // The shortest message with additional data is kStartToken\0\00\00\0.
|
||||
const size_t kMinMessageLength = std::size(kStartToken) + 4;
|
||||
if (bytes_read_ < kMinMessageLength) {
|
||||
buf_[bytes_read_] = 0;
|
||||
@@ -750,10 +776,28 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
|
||||
tokens.erase(tokens.begin());
|
||||
tokens.erase(tokens.begin());
|
||||
|
||||
+ size_t num_args;
|
||||
+ base::StringToSizeT(tokens[0], &num_args);
|
||||
+ std::vector<std::string> command_line(tokens.begin() + 1, tokens.begin() + 1 + num_args);
|
||||
+
|
||||
+ std::vector<uint8_t> additional_data;
|
||||
+ if (tokens.size() >= 3 + num_args) {
|
||||
+ size_t additional_data_size;
|
||||
+ base::StringToSizeT(tokens[1 + num_args], &additional_data_size);
|
||||
+ std::string remaining_args = base::JoinString(
|
||||
+ base::make_span(tokens.begin() + 2 + num_args, tokens.end()),
|
||||
+ std::string(1, kTokenDelimiter));
|
||||
+ const uint8_t* additional_data_bits =
|
||||
+ reinterpret_cast<const uint8_t*>(remaining_args.c_str());
|
||||
+ additional_data = std::vector<uint8_t>(additional_data_bits,
|
||||
+ additional_data_bits + additional_data_size);
|
||||
+ }
|
||||
+
|
||||
// Return to the UI thread to handle opening a new browser tab.
|
||||
ui_task_runner_->PostTask(
|
||||
FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::HandleMessage,
|
||||
- parent_, current_dir, tokens, this));
|
||||
+ parent_, current_dir, command_line,
|
||||
+ std::move(additional_data), this));
|
||||
fd_watch_controller_.reset();
|
||||
|
||||
// LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
|
||||
@@ -782,8 +826,12 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
|
||||
//
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const base::FilePath& user_data_dir,
|
||||
- const NotificationCallback& notification_callback)
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
+ const NotificationCallback& notification_callback,
|
||||
+ const NotificationAckCallback& notification_ack_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
+ notification_ack_callback_(notification_ack_callback),
|
||||
+ additional_data_(additional_data),
|
||||
current_pid_(base::GetCurrentProcId()),
|
||||
watcher_(new LinuxWatcher(this)) {
|
||||
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
|
||||
@@ -902,7 +950,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
sizeof(socket_timeout));
|
||||
|
||||
// Found another process, prepare our command line
|
||||
- // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>".
|
||||
+ // format is "START\0<current-dir>\0<n-args>\0<argv[0]>\0...\0<argv[n]>
|
||||
+ // \0<additional-data-length>\0<additional-data>".
|
||||
std::string to_send(kStartToken);
|
||||
to_send.push_back(kTokenDelimiter);
|
||||
|
||||
@@ -912,11 +961,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
to_send.append(current_dir.value());
|
||||
|
||||
const std::vector<std::string>& argv = cmd_line.argv();
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(base::NumberToString(argv.size()));
|
||||
for (auto it = argv.begin(); it != argv.end(); ++it) {
|
||||
to_send.push_back(kTokenDelimiter);
|
||||
to_send.append(*it);
|
||||
}
|
||||
|
||||
+ size_t data_to_send_size = additional_data_.size_bytes();
|
||||
+ if (data_to_send_size) {
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(base::NumberToString(data_to_send_size));
|
||||
+ to_send.push_back(kTokenDelimiter);
|
||||
+ to_send.append(reinterpret_cast<const char*>(additional_data_.data()), data_to_send_size);
|
||||
+ }
|
||||
+
|
||||
// Send the message
|
||||
if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
|
||||
// Try to kill the other process, because it might have been dead.
|
||||
@@ -958,6 +1017,17 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
|
||||
linux_ui->NotifyWindowManagerStartupComplete();
|
||||
#endif
|
||||
|
||||
+ size_t ack_data_len = len - (std::size(kACKToken) - 1);
|
||||
+ if (ack_data_len) {
|
||||
+ const uint8_t* raw_ack_data =
|
||||
+ reinterpret_cast<const uint8_t*>(buf + std::size(kACKToken) - 1);
|
||||
+ base::span<const uint8_t> ack_data =
|
||||
+ base::make_span(raw_ack_data, raw_ack_data + ack_data_len);
|
||||
+ notification_ack_callback_.Run(&ack_data);
|
||||
+ } else {
|
||||
+ notification_ack_callback_.Run(nullptr);
|
||||
+ }
|
||||
+
|
||||
// Assume the other process is handling the request.
|
||||
return PROCESS_NOTIFIED;
|
||||
}
|
||||
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
|
||||
index ec725b44296266bea1a51aea889463a0bba8449c..3bb74c08cd78b11cd9925a6bfafc62d05018b627 100644
|
||||
--- a/chrome/browser/process_singleton_win.cc
|
||||
+++ b/chrome/browser/process_singleton_win.cc
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
+#include "base/timer/timer.h"
|
||||
#include "base/trace_event/base_tracing.h"
|
||||
#include "base/win/registry.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
@@ -45,6 +46,14 @@
|
||||
namespace {
|
||||
|
||||
const char kLockfile[] = "lockfile";
|
||||
+const LPCWSTR kPipeName = L"\\\\.\\pipe\\electronAckPipe";
|
||||
+const DWORD kPipeTimeout = 10000;
|
||||
+const DWORD kMaxMessageLength = 32 * 1024;
|
||||
+
|
||||
+std::unique_ptr<std::vector<uint8_t>> g_ack_data;
|
||||
+base::OneShotTimer g_ack_timer;
|
||||
+HANDLE g_write_ack_pipe;
|
||||
+bool g_write_ack_callback_called = false;
|
||||
|
||||
// A helper class that acquires the given |mutex| while the AutoLockMutex is in
|
||||
// scope.
|
||||
@@ -80,10 +89,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
|
||||
|
||||
bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||
base::CommandLine* parsed_command_line,
|
||||
- base::FilePath* current_directory) {
|
||||
+ base::FilePath* current_directory,
|
||||
+ std::vector<uint8_t>* parsed_additional_data) {
|
||||
// We should have enough room for the shortest command (min_message_size)
|
||||
// and also be a multiple of wchar_t bytes. The shortest command
|
||||
- // possible is L"START\0\0" (empty current directory and command line).
|
||||
+ // possible is L"START\0\0" (empty command line, current directory,
|
||||
+ // and additional data).
|
||||
static const int min_message_size = 7;
|
||||
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
|
||||
cds->cbData % sizeof(wchar_t) != 0) {
|
||||
@@ -133,11 +144,82 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||
const std::wstring cmd_line =
|
||||
msg.substr(second_null + 1, third_null - second_null);
|
||||
*parsed_command_line = base::CommandLine::FromString(cmd_line);
|
||||
+
|
||||
+ const std::wstring::size_type fourth_null =
|
||||
+ msg.find_first_of(L'\0', third_null + 1);
|
||||
+ if (fourth_null == std::wstring::npos ||
|
||||
+ fourth_null == msg.length()) {
|
||||
+ // No additional data was provided.
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ // Get length of the additional data.
|
||||
+ const std::wstring additional_data_length_string =
|
||||
+ msg.substr(third_null + 1, fourth_null - third_null);
|
||||
+ size_t additional_data_length;
|
||||
+ base::StringToSizeT(additional_data_length_string, &additional_data_length);
|
||||
+
|
||||
+ const std::wstring::size_type fifth_null =
|
||||
+ msg.find_first_of(L'\0', fourth_null + 1);
|
||||
+ if (fifth_null == std::wstring::npos ||
|
||||
+ fifth_null == msg.length()) {
|
||||
+ LOG(WARNING) << "Invalid format for start command, we need a string in 6 "
|
||||
+ "parts separated by NULLs";
|
||||
+ }
|
||||
+
|
||||
+ // Get the actual additional data.
|
||||
+ const std::wstring additional_data =
|
||||
+ msg.substr(fourth_null + 1, fifth_null - fourth_null);
|
||||
+ const uint8_t* additional_data_bytes =
|
||||
+ reinterpret_cast<const uint8_t*>(additional_data.c_str());
|
||||
+ *parsed_additional_data = std::vector<uint8_t>(additional_data_bytes,
|
||||
+ additional_data_bytes + additional_data_length);
|
||||
+
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+void StoreAck(const base::span<const uint8_t>* ack_data) {
|
||||
+ if (ack_data) {
|
||||
+ g_ack_data = std::make_unique<std::vector<uint8_t>>(ack_data->begin(),
|
||||
+ ack_data->end());
|
||||
+ } else {
|
||||
+ g_ack_data = nullptr;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void SendBackAck() {
|
||||
+ // This is the first instance sending the ack back to the second instance.
|
||||
+ if (!g_write_ack_callback_called) {
|
||||
+ g_write_ack_callback_called = true;
|
||||
+ const uint8_t* data_buffer = nullptr;
|
||||
+ DWORD data_to_send_size = 0;
|
||||
+ if (g_ack_data) {
|
||||
+ data_buffer = g_ack_data->data();
|
||||
+ DWORD ack_data_size = g_ack_data->size() * sizeof(uint8_t);
|
||||
+ data_to_send_size = (ack_data_size < kMaxMessageLength) ? ack_data_size : kMaxMessageLength;
|
||||
+ }
|
||||
+
|
||||
+ ::ConnectNamedPipe(g_write_ack_pipe, NULL);
|
||||
+
|
||||
+ DWORD bytes_written = 0;
|
||||
+ ::WriteFile(g_write_ack_pipe,
|
||||
+ (LPCVOID)data_buffer,
|
||||
+ data_to_send_size,
|
||||
+ &bytes_written,
|
||||
+ NULL);
|
||||
+ DCHECK(bytes_written == data_to_send_size);
|
||||
+
|
||||
+ ::FlushFileBuffers(g_write_ack_pipe);
|
||||
+ ::DisconnectNamedPipe(g_write_ack_pipe);
|
||||
+
|
||||
+ if (g_ack_data) {
|
||||
+ g_ack_data.reset();
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
bool ProcessLaunchNotification(
|
||||
const ProcessSingleton::NotificationCallback& notification_callback,
|
||||
UINT message,
|
||||
@@ -151,16 +233,35 @@ bool ProcessLaunchNotification(
|
||||
|
||||
// Handle the WM_COPYDATA message from another process.
|
||||
const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
|
||||
-
|
||||
base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
|
||||
base::FilePath current_directory;
|
||||
- if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) {
|
||||
+ std::vector<uint8_t> additional_data;
|
||||
+ if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory,
|
||||
+ &additional_data)) {
|
||||
*result = TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
- *result = notification_callback.Run(parsed_command_line, current_directory) ?
|
||||
- TRUE : FALSE;
|
||||
+ // notification_callback.Run waits for StoreAck to
|
||||
+ // run to completion before moving onwards.
|
||||
+ // Therefore, we cannot directly send the SendBackAck
|
||||
+ // callback instead, as it would hang the program
|
||||
+ // during the ConnectNamedPipe call.
|
||||
+ g_write_ack_callback_called = false;
|
||||
+ *result = notification_callback.Run(parsed_command_line, current_directory,
|
||||
+ std::move(additional_data),
|
||||
+ base::BindRepeating(&StoreAck))
|
||||
+ ? TRUE
|
||||
+ : FALSE;
|
||||
+ if (*result) {
|
||||
+ // If *result is TRUE, we return NOTIFY_SUCCESS.
|
||||
+ // Only for that case does the second process read
|
||||
+ // the acknowledgement. Therefore, only send back
|
||||
+ // the acknowledgement if *result is TRUE,
|
||||
+ // otherwise the program hangs during the ConnectNamedPipe call.
|
||||
+ g_ack_timer.Start(FROM_HERE, base::Seconds(0),
|
||||
+ base::BindOnce(&SendBackAck));
|
||||
+ }
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -261,9 +362,13 @@ bool ProcessSingleton::EscapeVirtualization(
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const std::string& program_name,
|
||||
const base::FilePath& user_data_dir,
|
||||
+ const base::span<const uint8_t> additional_data,
|
||||
bool is_app_sandboxed,
|
||||
- const NotificationCallback& notification_callback)
|
||||
+ const NotificationCallback& notification_callback,
|
||||
+ const NotificationAckCallback& notification_ack_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
+ notification_ack_callback_(notification_ack_callback),
|
||||
+ additional_data_(additional_data),
|
||||
program_name_(program_name),
|
||||
is_app_sandboxed_(is_app_sandboxed),
|
||||
is_virtualized_(false),
|
||||
@@ -278,6 +383,37 @@ ProcessSingleton::~ProcessSingleton() {
|
||||
::CloseHandle(lock_file_);
|
||||
}
|
||||
|
||||
+void ReadAck(const ProcessSingleton::NotificationAckCallback& ack_callback) {
|
||||
+ // We are reading the ack from the first instance.
|
||||
+ // First, wait for the pipe.
|
||||
+ ::WaitNamedPipe(kPipeName, NMPWAIT_USE_DEFAULT_WAIT);
|
||||
+
|
||||
+ HANDLE read_ack_pipe = ::CreateFile(kPipeName,
|
||||
+ GENERIC_READ,
|
||||
+ FILE_SHARE_READ,
|
||||
+ NULL,
|
||||
+ OPEN_EXISTING,
|
||||
+ FILE_ATTRIBUTE_NORMAL,
|
||||
+ NULL);
|
||||
+ CHECK(read_ack_pipe != INVALID_HANDLE_VALUE);
|
||||
+
|
||||
+ DWORD bytes_read;
|
||||
+ uint8_t read_ack_buffer[kMaxMessageLength];
|
||||
+ ::ReadFile(read_ack_pipe,
|
||||
+ (LPVOID)read_ack_buffer,
|
||||
+ kMaxMessageLength,
|
||||
+ &bytes_read,
|
||||
+ NULL);
|
||||
+
|
||||
+ if (!bytes_read) {
|
||||
+ ack_callback.Run(nullptr);
|
||||
+ } else {
|
||||
+ base::span<const uint8_t> out_span(read_ack_buffer, read_ack_buffer + bytes_read);
|
||||
+ ack_callback.Run(&out_span);
|
||||
+ }
|
||||
+ ::CloseHandle(read_ack_pipe);
|
||||
+}
|
||||
+
|
||||
// Code roughly based on Mozilla.
|
||||
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
||||
TRACE_EVENT0("startup", "ProcessSingleton::NotifyOtherProcess");
|
||||
@@ -290,8 +426,9 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
||||
return PROCESS_NONE;
|
||||
}
|
||||
|
||||
- switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
|
||||
+ switch (chrome::AttemptToNotifyRunningChrome(remote_window_, additional_data_)) {
|
||||
case chrome::NOTIFY_SUCCESS:
|
||||
+ ReadAck(notification_ack_callback_);
|
||||
return PROCESS_NOTIFIED;
|
||||
case chrome::NOTIFY_FAILED:
|
||||
remote_window_ = NULL;
|
||||
@@ -429,6 +566,18 @@ bool ProcessSingleton::Create() {
|
||||
<< "Lock file can not be created! Error code: " << error;
|
||||
|
||||
if (lock_file_ != INVALID_HANDLE_VALUE) {
|
||||
+ // We are the first instance. Create a pipe to send out ack data.
|
||||
+ ack_pipe_ = ::CreateNamedPipe(kPipeName,
|
||||
+ PIPE_ACCESS_OUTBOUND,
|
||||
+ PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
|
||||
+ PIPE_UNLIMITED_INSTANCES,
|
||||
+ kMaxMessageLength,
|
||||
+ 0,
|
||||
+ kPipeTimeout,
|
||||
+ NULL);
|
||||
+ CHECK(ack_pipe_ != INVALID_HANDLE_VALUE);
|
||||
+ g_write_ack_pipe = ack_pipe_;
|
||||
+
|
||||
// Set the window's title to the path of our user data directory so
|
||||
// other Chrome instances can decide if they should forward to us.
|
||||
TRACE_EVENT0("startup", "ProcessSingleton::Create:CreateWindow");
|
||||
@@ -456,6 +605,7 @@ bool ProcessSingleton::Create() {
|
||||
}
|
||||
|
||||
void ProcessSingleton::Cleanup() {
|
||||
+ ::CloseHandle(ack_pipe_);
|
||||
}
|
||||
|
||||
void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
|
||||
diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
|
||||
index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..ce851d09d501ebcc6d6c4065e746e869d5275b2b 100644
|
||||
--- a/chrome/browser/win/chrome_process_finder.cc
|
||||
+++ b/chrome/browser/win/chrome_process_finder.cc
|
||||
@@ -36,9 +36,10 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
|
||||
return base::win::MessageWindow::FindWindow(user_data_dir.value());
|
||||
}
|
||||
|
||||
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
+NotifyChromeResult AttemptToNotifyRunningChrome(
|
||||
+ HWND remote_window,
|
||||
+ const base::span<const uint8_t> additional_data) {
|
||||
TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome");
|
||||
-
|
||||
DCHECK(remote_window);
|
||||
DWORD process_id = 0;
|
||||
DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
|
||||
@@ -50,7 +51,8 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
}
|
||||
|
||||
// Send the command line to the remote chrome window.
|
||||
- // Format is "START\0<<<current directory>>>\0<<<commandline>>>".
|
||||
+ // Format is
|
||||
+ // "START\0<current-directory>\0<command-line>\0<additional-data-length>\0<additional-data>".
|
||||
std::wstring to_send(L"START\0", 6); // want the NULL in the string.
|
||||
base::FilePath cur_dir;
|
||||
if (!base::GetCurrentDirectory(&cur_dir)) {
|
||||
@@ -64,6 +66,22 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
|
||||
base::CommandLine::ForCurrentProcess()->GetCommandLineString());
|
||||
to_send.append(L"\0", 1); // Null separator.
|
||||
|
||||
+ size_t additional_data_size = additional_data.size_bytes();
|
||||
+ if (additional_data_size) {
|
||||
+ // Send over the size, because the reinterpret cast to wchar_t could
|
||||
+ // add padding.
|
||||
+ to_send.append(base::UTF8ToWide(base::NumberToString(additional_data_size)));
|
||||
+ to_send.append(L"\0", 1); // Null separator.
|
||||
+
|
||||
+ size_t padded_size = additional_data_size / sizeof(wchar_t);
|
||||
+ if (additional_data_size % sizeof(wchar_t) != 0) {
|
||||
+ padded_size++;
|
||||
+ }
|
||||
+ to_send.append(reinterpret_cast<const wchar_t*>(additional_data.data()),
|
||||
+ padded_size);
|
||||
+ to_send.append(L"\0", 1); // Null separator.
|
||||
+ }
|
||||
+
|
||||
// Allow the current running browser window to make itself the foreground
|
||||
// window (otherwise it will just flash in the taskbar).
|
||||
::AllowSetForegroundWindow(process_id);
|
||||
diff --git a/chrome/browser/win/chrome_process_finder.h b/chrome/browser/win/chrome_process_finder.h
|
||||
index 5516673cee019f6060077091e59498bf9038cd6e..8edea5079b46c2cba67833114eb9c21d85cfc22d 100644
|
||||
--- a/chrome/browser/win/chrome_process_finder.h
|
||||
+++ b/chrome/browser/win/chrome_process_finder.h
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
+#include "base/containers/span.h"
|
||||
#include "base/time/time.h"
|
||||
|
||||
namespace base {
|
||||
@@ -27,7 +28,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir);
|
||||
// Attempts to send the current command line to an already running instance of
|
||||
// Chrome via a WM_COPYDATA message.
|
||||
// Returns true if a running Chrome is found and successfully notified.
|
||||
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window);
|
||||
+NotifyChromeResult AttemptToNotifyRunningChrome(
|
||||
+ HWND remote_window,
|
||||
+ const base::span<const uint8_t> additional_data);
|
||||
|
||||
// Changes the notification timeout to |new_timeout|, returns the old timeout.
|
||||
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout);
|
||||
@@ -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();
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shelley Vohr <shelley.vohr@gmail.com>
|
||||
Date: Mon, 25 Oct 2021 21:45:57 +0200
|
||||
Subject: fix: patch out permissions checks in exclusive_access
|
||||
Subject: fix: adapt exclusive_access for electron needs
|
||||
|
||||
This patch is necessary in order to properly enable
|
||||
navigator.keyboard.{(un)?lock}() functionality. We don't have a concept
|
||||
of PermissionManager nor of a Profile, so this would not affect usage of
|
||||
the API.
|
||||
|
||||
We might consider potentially using our own permissions handler,
|
||||
but it's not strictly necessary for this API to work to spec.
|
||||
|
||||
Profile check has been upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/3247196
|
||||
We also need to ensure that NotifyExclusiveTabAccessLost is called
|
||||
on all platforms in FullscreenController::ExitFullscreenModeInternal()
|
||||
and not just macOS, since Electron's native window impls report state
|
||||
change fairly instantly as well, and so pressing escape won't work on
|
||||
Linux or Windows to un-fullscreen in some circumstances without this
|
||||
change.
|
||||
|
||||
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
|
||||
index 467fed776d390e1a10075d62dab75fd8a6789627..5992117c90fbff008cf2f6a914f85c90ea117e47 100644
|
||||
index 467fed776d390e1a10075d62dab75fd8a6789627..d6f3c20fabee755373add6ee1e5722c106204e42 100644
|
||||
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
|
||||
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
|
||||
@@ -16,12 +16,16 @@
|
||||
@@ -112,6 +114,22 @@ index 467fed776d390e1a10075d62dab75fd8a6789627..5992117c90fbff008cf2f6a914f85c90
|
||||
|
||||
if (option == BROWSER)
|
||||
base::RecordAction(base::UserMetricsAction("ToggleFullscreen"));
|
||||
@@ -492,12 +502,12 @@ void FullscreenController::ExitFullscreenModeInternal() {
|
||||
|
||||
RecordExitingUMA();
|
||||
toggled_into_fullscreen_ = false;
|
||||
-#if BUILDFLAG(IS_MAC)
|
||||
- // Mac windows report a state change instantly, and so we must also clear
|
||||
+
|
||||
+ // Electron native windows report a state change instantly, and so we must also clear
|
||||
// state_prior_to_tab_fullscreen_ to match them else other logic using
|
||||
// state_prior_to_tab_fullscreen_ will be incorrect.
|
||||
NotifyTabExclusiveAccessLost();
|
||||
-#endif
|
||||
+
|
||||
exclusive_access_manager()->context()->ExitFullscreen();
|
||||
extension_caused_fullscreen_ = GURL();
|
||||
|
||||
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller.h b/chrome/browser/ui/exclusive_access/fullscreen_controller.h
|
||||
index 58a1ff5fb9fecd8c85905045ba43f43f0c63c2bb..15f868ac651c05eae6505adb80f2b4573a1166ef 100644
|
||||
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller.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();
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
@@ -10,6 +10,19 @@ majority of changes originally come from these PRs:
|
||||
|
||||
This patch also fixes callback for manual user cancellation and success.
|
||||
|
||||
diff --git a/chrome/browser/printing/print_backend_service_manager.cc b/chrome/browser/printing/print_backend_service_manager.cc
|
||||
index 27eb7cf1480e7a56ab6b7d010cdee65c72e62c10..76d265b8de17244ed9d3727f1bebe730a224cd56 100644
|
||||
--- a/chrome/browser/printing/print_backend_service_manager.cc
|
||||
+++ b/chrome/browser/printing/print_backend_service_manager.cc
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "base/unguessable_token.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
-#include "chrome/grit/generated_resources.h"
|
||||
+#include "electron/grit/electron_resources.h"
|
||||
#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
|
||||
#include "components/crash/core/common/crash_keys.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
diff --git a/chrome/browser/printing/print_job.cc b/chrome/browser/printing/print_job.cc
|
||||
index 6408cbee8b5a8e45c4276ed966b57c1e61ad1137..e951c41a1bd92a33e32f6835841d3935a05dde53 100644
|
||||
--- a/chrome/browser/printing/print_job.cc
|
||||
@@ -113,7 +126,7 @@ index dd27bbf387718d6abda5080e7d2c609cd0eaff17..8837cf2aeaa2f87d51be8d00aa356c8a
|
||||
|
||||
void PrintJobWorkerOop::UnregisterServiceManagerClient() {
|
||||
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
|
||||
index fb69efbfa8e355f8389abf9490cf338e70f16c7b..3949a0c01e2d305d9560791a1dd9ed8cfda8d6b5 100644
|
||||
index fb69efbfa8e355f8389abf9490cf338e70f16c7b..52099b2312b042b0afdd5ee3d2df5c66ea25df8b 100644
|
||||
--- a/chrome/browser/printing/print_view_manager_base.cc
|
||||
+++ b/chrome/browser/printing/print_view_manager_base.cc
|
||||
@@ -30,10 +30,10 @@
|
||||
@@ -206,7 +219,7 @@ index fb69efbfa8e355f8389abf9490cf338e70f16c7b..3949a0c01e2d305d9560791a1dd9ed8c
|
||||
-bool PrintViewManagerBase::PrintNow(content::RenderFrameHost* rfh) {
|
||||
+bool PrintViewManagerBase::PrintNow(content::RenderFrameHost* rfh,
|
||||
+ bool silent,
|
||||
+ base::Value settings,
|
||||
+ base::Value::Dict settings,
|
||||
+ CompletionCallback callback) {
|
||||
// Remember the ID for `rfh`, to enable checking that the `RenderFrameHost`
|
||||
// is still valid after a possible inner message loop runs in
|
||||
@@ -402,12 +415,12 @@ index fb69efbfa8e355f8389abf9490cf338e70f16c7b..3949a0c01e2d305d9560791a1dd9ed8c
|
||||
|
||||
void PrintViewManagerBase::CompletePrintNow(content::RenderFrameHost* rfh) {
|
||||
- GetPrintRenderFrame(rfh)->PrintRequestedPages();
|
||||
+ GetPrintRenderFrame(rfh)->PrintRequestedPages(true/*silent*/, base::Value{}/*job_settings*/);
|
||||
+ GetPrintRenderFrame(rfh)->PrintRequestedPages(/*silent=*/true, /*job_settings=*/base::Value::Dict());
|
||||
|
||||
for (auto& observer : GetObservers())
|
||||
observer.OnPrintNow(rfh);
|
||||
diff --git a/chrome/browser/printing/print_view_manager_base.h b/chrome/browser/printing/print_view_manager_base.h
|
||||
index 48895e0f8a846aeb725e306c7f22ac159d5d96a7..7ae57ba04cedd9d4a428c140e48bd9a8ecb57c21 100644
|
||||
index 48895e0f8a846aeb725e306c7f22ac159d5d96a7..027794e38f86ab1905c5cdd95140b2cb1e5c5653 100644
|
||||
--- a/chrome/browser/printing/print_view_manager_base.h
|
||||
+++ b/chrome/browser/printing/print_view_manager_base.h
|
||||
@@ -41,6 +41,8 @@ namespace printing {
|
||||
@@ -426,7 +439,7 @@ index 48895e0f8a846aeb725e306c7f22ac159d5d96a7..7ae57ba04cedd9d4a428c140e48bd9a8
|
||||
- virtual bool PrintNow(content::RenderFrameHost* rfh);
|
||||
+ virtual bool PrintNow(content::RenderFrameHost* rfh,
|
||||
+ bool silent = true,
|
||||
+ base::Value settings = {},
|
||||
+ base::Value::Dict settings = {},
|
||||
+ CompletionCallback callback = {});
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
|
||||
@@ -466,7 +479,7 @@ index 48895e0f8a846aeb725e306c7f22ac159d5d96a7..7ae57ba04cedd9d4a428c140e48bd9a8
|
||||
// This means we are _blocking_ until all the necessary pages have been
|
||||
// rendered or the print settings are being loaded.
|
||||
diff --git a/chrome/browser/ui/webui/print_preview/fake_print_render_frame.cc b/chrome/browser/ui/webui/print_preview/fake_print_render_frame.cc
|
||||
index 016e0d8fcc575bfa77323d8600d5daba152abbc6..3a9b68a88c71c6f9760ed305f8aefd9fa5e71650 100644
|
||||
index 016e0d8fcc575bfa77323d8600d5daba152abbc6..dfb856f57767e24c0ec495c94e515afbb83e19ea 100644
|
||||
--- a/chrome/browser/ui/webui/print_preview/fake_print_render_frame.cc
|
||||
+++ b/chrome/browser/ui/webui/print_preview/fake_print_render_frame.cc
|
||||
@@ -20,7 +20,7 @@ FakePrintRenderFrame::FakePrintRenderFrame(
|
||||
@@ -474,12 +487,12 @@ index 016e0d8fcc575bfa77323d8600d5daba152abbc6..3a9b68a88c71c6f9760ed305f8aefd9f
|
||||
FakePrintRenderFrame::~FakePrintRenderFrame() = default;
|
||||
|
||||
-void FakePrintRenderFrame::PrintRequestedPages() {}
|
||||
+void FakePrintRenderFrame::PrintRequestedPages(bool /*silent*/, ::base::Value /*settings*/) {}
|
||||
+void FakePrintRenderFrame::PrintRequestedPages(bool /*silent*/, ::base::Value::Dict /*settings*/) {}
|
||||
|
||||
void FakePrintRenderFrame::PrintForSystemDialog() {}
|
||||
|
||||
diff --git a/chrome/browser/ui/webui/print_preview/fake_print_render_frame.h b/chrome/browser/ui/webui/print_preview/fake_print_render_frame.h
|
||||
index 3c95ee5c0d169f677947427a6ace55b4bfe80277..9e9d3f327b0033229cdd9b78159a1abdad5d3a80 100644
|
||||
index 3c95ee5c0d169f677947427a6ace55b4bfe80277..63ba8aca503968d0409b63b991a9276228d60d4a 100644
|
||||
--- a/chrome/browser/ui/webui/print_preview/fake_print_render_frame.h
|
||||
+++ b/chrome/browser/ui/webui/print_preview/fake_print_render_frame.h
|
||||
@@ -25,7 +25,7 @@ class FakePrintRenderFrame : public mojom::PrintRenderFrame {
|
||||
@@ -487,7 +500,7 @@ index 3c95ee5c0d169f677947427a6ace55b4bfe80277..9e9d3f327b0033229cdd9b78159a1abd
|
||||
private:
|
||||
// printing::mojom::PrintRenderFrame:
|
||||
- void PrintRequestedPages() override;
|
||||
+ void PrintRequestedPages(bool silent, ::base::Value settings) override;
|
||||
+ void PrintRequestedPages(bool silent, ::base::Value::Dict settings) override;
|
||||
void PrintForSystemDialog() override;
|
||||
void SetPrintPreviewUI(
|
||||
mojo::PendingAssociatedRemote<mojom::PrintPreviewUI> preview) override;
|
||||
@@ -541,7 +554,7 @@ index 3ba45e77f66531bc6b81717eb9c7d3faca1e9fa9..41271d3fe390dd16d1c2b9839ac32013
|
||||
|
||||
void PdfPrintManager::ShowInvalidPrinterSettingsError() {
|
||||
diff --git a/components/printing/common/print.mojom b/components/printing/common/print.mojom
|
||||
index 156b987aa9cafb69c04bed483aa78f26baa9cd97..fd50e8ca937878d73f9ae4c22aec204e7851e95b 100644
|
||||
index 156b987aa9cafb69c04bed483aa78f26baa9cd97..3d3f2cb97b59a6e46bf0c16dd2355aa8e21ec5da 100644
|
||||
--- a/components/printing/common/print.mojom
|
||||
+++ b/components/printing/common/print.mojom
|
||||
@@ -275,7 +275,7 @@ interface PrintPreviewUI {
|
||||
@@ -549,7 +562,7 @@ index 156b987aa9cafb69c04bed483aa78f26baa9cd97..fd50e8ca937878d73f9ae4c22aec204e
|
||||
// Tells the RenderFrame to switch the CSS to print media type, render every
|
||||
// requested page, and then switch back the CSS to display media type.
|
||||
- PrintRequestedPages();
|
||||
+ PrintRequestedPages(bool silent, mojo_base.mojom.DeprecatedDictionaryValue settings);
|
||||
+ PrintRequestedPages(bool silent, mojo_base.mojom.DictionaryValue settings);
|
||||
|
||||
// Tells the RenderFrame to switch the CSS to print media type, render every
|
||||
// requested page using the print preview document's frame/node, and then
|
||||
@@ -563,7 +576,7 @@ index 156b987aa9cafb69c04bed483aa78f26baa9cd97..fd50e8ca937878d73f9ae4c22aec204e
|
||||
// Tells the browser that there are invalid printer settings.
|
||||
ShowInvalidPrinterSettingsError();
|
||||
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
|
||||
index e0ecf8f3f09dafe44a5220117281a08e38a61bef..01059e34e4c90c681dd347ad4876c0fb208b0d21 100644
|
||||
index e0ecf8f3f09dafe44a5220117281a08e38a61bef..edbaa28ec4ffd22b6afa332365c5dc51c33de31e 100644
|
||||
--- a/components/printing/renderer/print_render_frame_helper.cc
|
||||
+++ b/components/printing/renderer/print_render_frame_helper.cc
|
||||
@@ -41,6 +41,7 @@
|
||||
@@ -580,7 +593,7 @@ index e0ecf8f3f09dafe44a5220117281a08e38a61bef..01059e34e4c90c681dd347ad4876c0fb
|
||||
|
||||
- Print(web_frame, blink::WebNode(), PrintRequestType::kScripted);
|
||||
+ Print(web_frame, blink::WebNode(), PrintRequestType::kScripted,
|
||||
+ false /* silent */, base::DictionaryValue() /* new_settings */);
|
||||
+ false /* silent */, base::Value::Dict() /* new_settings */);
|
||||
if (!weak_this)
|
||||
return;
|
||||
|
||||
@@ -589,7 +602,7 @@ index e0ecf8f3f09dafe44a5220117281a08e38a61bef..01059e34e4c90c681dd347ad4876c0fb
|
||||
}
|
||||
|
||||
-void PrintRenderFrameHelper::PrintRequestedPages() {
|
||||
+void PrintRenderFrameHelper::PrintRequestedPages(bool silent, base::Value settings) {
|
||||
+void PrintRenderFrameHelper::PrintRequestedPages(bool silent, base::Value::Dict settings) {
|
||||
ScopedIPC scoped_ipc(weak_ptr_factory_.GetWeakPtr());
|
||||
if (ipc_nesting_level_ > kAllowedIpcDepthForPrint)
|
||||
return;
|
||||
@@ -608,7 +621,7 @@ index e0ecf8f3f09dafe44a5220117281a08e38a61bef..01059e34e4c90c681dd347ad4876c0fb
|
||||
Print(frame, print_preview_context_.source_node(),
|
||||
- PrintRequestType::kRegular);
|
||||
+ PrintRequestType::kRegular, false,
|
||||
+ base::DictionaryValue());
|
||||
+ base::Value::Dict());
|
||||
if (!render_frame_gone_)
|
||||
print_preview_context_.DispatchAfterPrintEvent();
|
||||
// WARNING: |this| may be gone at this point. Do not do any more work here and
|
||||
@@ -627,7 +640,7 @@ index e0ecf8f3f09dafe44a5220117281a08e38a61bef..01059e34e4c90c681dd347ad4876c0fb
|
||||
Print(duplicate_node.GetDocument().GetFrame(), duplicate_node,
|
||||
- PrintRequestType::kRegular);
|
||||
+ PrintRequestType::kRegular, false /* silent */,
|
||||
+ base::DictionaryValue() /* new_settings */);
|
||||
+ base::Value::Dict() /* new_settings */);
|
||||
// Check if |this| is still valid.
|
||||
if (!weak_this)
|
||||
return;
|
||||
@@ -638,7 +651,7 @@ index e0ecf8f3f09dafe44a5220117281a08e38a61bef..01059e34e4c90c681dd347ad4876c0fb
|
||||
- PrintRequestType print_request_type) {
|
||||
+ PrintRequestType print_request_type,
|
||||
+ bool silent,
|
||||
+ base::Value settings) {
|
||||
+ base::Value::Dict settings) {
|
||||
// If still not finished with earlier print request simply ignore.
|
||||
if (prep_frame_view_)
|
||||
return;
|
||||
@@ -647,7 +660,7 @@ index e0ecf8f3f09dafe44a5220117281a08e38a61bef..01059e34e4c90c681dd347ad4876c0fb
|
||||
|
||||
uint32_t expected_page_count = 0;
|
||||
- if (!CalculateNumberOfPages(frame, node, &expected_page_count)) {
|
||||
+ if (!CalculateNumberOfPages(frame, node, &expected_page_count, base::Value::AsDictionaryValue(settings))) {
|
||||
+ if (!CalculateNumberOfPages(frame, node, &expected_page_count, std::move(settings))) {
|
||||
DidFinishPrinting(FAIL_PRINT_INIT);
|
||||
return; // Failed to init print page settings.
|
||||
}
|
||||
@@ -678,10 +691,10 @@ index e0ecf8f3f09dafe44a5220117281a08e38a61bef..01059e34e4c90c681dd347ad4876c0fb
|
||||
- GetPrintManagerHost()->GetDefaultPrintSettings(&settings.params);
|
||||
+bool PrintRenderFrameHelper::InitPrintSettings(
|
||||
+ bool fit_to_paper_size,
|
||||
+ const base::DictionaryValue& new_settings) {
|
||||
+ base::Value::Dict new_settings) {
|
||||
+ mojom::PrintPagesParamsPtr settings;
|
||||
+
|
||||
+ if (new_settings.DictEmpty()) {
|
||||
+ if (new_settings.empty()) {
|
||||
+ settings = mojom::PrintPagesParams::New();
|
||||
+ settings->params = mojom::PrintParams::New();
|
||||
+ GetPrintManagerHost()->GetDefaultPrintSettings(&settings->params);
|
||||
@@ -690,7 +703,7 @@ index e0ecf8f3f09dafe44a5220117281a08e38a61bef..01059e34e4c90c681dd347ad4876c0fb
|
||||
+ int cookie =
|
||||
+ print_pages_params_ ? print_pages_params_->params->document_cookie : 0;
|
||||
+ GetPrintManagerHost()->UpdatePrintSettings(
|
||||
+ cookie, new_settings.GetDict().Clone(), &settings, &canceled);
|
||||
+ cookie, std::move(new_settings), &settings, &canceled);
|
||||
+ if (canceled)
|
||||
+ return false;
|
||||
+ }
|
||||
@@ -725,11 +738,11 @@ index e0ecf8f3f09dafe44a5220117281a08e38a61bef..01059e34e4c90c681dd347ad4876c0fb
|
||||
+ blink::WebLocalFrame* frame,
|
||||
+ const blink::WebNode& node,
|
||||
+ uint32_t* number_of_pages,
|
||||
+ const base::DictionaryValue& settings) {
|
||||
+ base::Value::Dict settings) {
|
||||
DCHECK(frame);
|
||||
bool fit_to_paper_size = !IsPrintingPdfFrame(frame, node);
|
||||
- if (!InitPrintSettings(fit_to_paper_size)) {
|
||||
+ if (!InitPrintSettings(fit_to_paper_size, settings)) {
|
||||
+ if (!InitPrintSettings(fit_to_paper_size, std::move(settings))) {
|
||||
notify_browser_of_print_failure_ = false;
|
||||
GetPrintManagerHost()->ShowInvalidPrinterSettingsError();
|
||||
return false;
|
||||
@@ -763,7 +776,7 @@ index e0ecf8f3f09dafe44a5220117281a08e38a61bef..01059e34e4c90c681dd347ad4876c0fb
|
||||
|
||||
bool PrintRenderFrameHelper::PreviewPageRendered(
|
||||
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
|
||||
index 15c367bd66706c915fdb95faf483429a8b31eb4c..628e113b861b6ea5157ff2179ea29dbb063ad992 100644
|
||||
index 15c367bd66706c915fdb95faf483429a8b31eb4c..ba2d5decbad2195c63efa4a5e23c60df7de69441 100644
|
||||
--- a/components/printing/renderer/print_render_frame_helper.h
|
||||
+++ b/components/printing/renderer/print_render_frame_helper.h
|
||||
@@ -254,7 +254,7 @@ class PrintRenderFrameHelper
|
||||
@@ -771,7 +784,7 @@ index 15c367bd66706c915fdb95faf483429a8b31eb4c..628e113b861b6ea5157ff2179ea29dbb
|
||||
|
||||
// printing::mojom::PrintRenderFrame:
|
||||
- void PrintRequestedPages() override;
|
||||
+ void PrintRequestedPages(bool silent, base::Value settings) override;
|
||||
+ void PrintRequestedPages(bool silent, base::Value::Dict settings) override;
|
||||
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
|
||||
void PrintForSystemDialog() override;
|
||||
void SetPrintPreviewUI(
|
||||
@@ -782,7 +795,7 @@ index 15c367bd66706c915fdb95faf483429a8b31eb4c..628e113b861b6ea5157ff2179ea29dbb
|
||||
- PrintRequestType print_request_type);
|
||||
+ PrintRequestType print_request_type,
|
||||
+ bool silent,
|
||||
+ base::Value settings);
|
||||
+ base::Value::Dict settings);
|
||||
|
||||
// Notification when printing is done - signal tear-down/free resources.
|
||||
void DidFinishPrinting(PrintingResult result);
|
||||
@@ -792,14 +805,14 @@ index 15c367bd66706c915fdb95faf483429a8b31eb4c..628e113b861b6ea5157ff2179ea29dbb
|
||||
// Used only for native printing workflow.
|
||||
- bool InitPrintSettings(bool fit_to_paper_size);
|
||||
+ bool InitPrintSettings(bool fit_to_paper_size,
|
||||
+ const base::DictionaryValue& settings);
|
||||
+ base::Value::Dict new_settings);
|
||||
|
||||
// Calculate number of pages in source document.
|
||||
bool CalculateNumberOfPages(blink::WebLocalFrame* frame,
|
||||
const blink::WebNode& node,
|
||||
- uint32_t* number_of_pages);
|
||||
+ uint32_t* number_of_pages,
|
||||
+ const base::DictionaryValue& settings);
|
||||
+ base::Value::Dict settings);
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
|
||||
// Set options for print preset from source PDF document.
|
||||
|
||||
@@ -4,6 +4,6 @@ dcheck.patch
|
||||
export_symbols_needed_for_windows_build.patch
|
||||
workaround_an_undefined_symbol_error.patch
|
||||
do_not_export_private_v8_symbols_on_windows.patch
|
||||
fix_build_deprecated_attirbute_for_older_msvc_versions.patch
|
||||
fix_build_deprecated_attribute_for_older_msvc_versions.patch
|
||||
fix_disable_implies_dcheck_for_node_stream_array_buffers.patch
|
||||
revert_fix_cppgc_removed_deleted_cstors_in_cppheapcreateparams.patch
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Deepak Mohan <hop2deep@gmail.com>
|
||||
Date: Tue, 28 Jan 2020 15:48:03 -0800
|
||||
Subject: fix: usage of c++ [[deprecated]] attirbute for older msvc versions
|
||||
Subject: fix: usage of c++ [[deprecated]] attribute for older msvc versions
|
||||
|
||||
VS 2015 update 3 has a bug where [[deprecated]] attribute cannot
|
||||
be applied to constructor declarations, this is fixed in 2017 and
|
||||
higher versions, but native module compiling with this version
|
||||
will have an issue.
|
||||
This attribute can only be used in all contexts in Visual Studio 2019
|
||||
|
||||
diff --git a/include/v8config.h b/include/v8config.h
|
||||
index 77fd65c6c5b7d8c0a7fe7a37c40e17ce66f49ce6..010b3633546601ba70a55aeb8e8fc503ef79e2f5 100644
|
||||
index 77fd65c6c5b7d8c0a7fe7a37c40e17ce66f49ce6..644f921f970d214b4d93b1e4c384e7475740b485 100644
|
||||
--- a/include/v8config.h
|
||||
+++ b/include/v8config.h
|
||||
@@ -454,10 +454,13 @@ path. Add it with -I<path> to the command line
|
||||
@@ -20,7 +17,7 @@ index 77fd65c6c5b7d8c0a7fe7a37c40e17ce66f49ce6..010b3633546601ba70a55aeb8e8fc503
|
||||
// A macro (V8_DEPRECATED) to mark classes or functions as deprecated.
|
||||
#if defined(V8_DEPRECATION_WARNINGS)
|
||||
-# define V8_DEPRECATED(message) [[deprecated(message)]]
|
||||
+# if defined(_MSC_VER) && _MSC_VER <= 1900
|
||||
+# if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER < 1920
|
||||
+# define V8_DEPRECATED(message) __declspec(deprecated(message))
|
||||
+# else
|
||||
+# define V8_DEPRECATED(message) [[deprecated(message)]]
|
||||
@@ -28,12 +25,12 @@ index 77fd65c6c5b7d8c0a7fe7a37c40e17ce66f49ce6..010b3633546601ba70a55aeb8e8fc503
|
||||
#else
|
||||
# define V8_DEPRECATED(message)
|
||||
#endif
|
||||
@@ -465,7 +468,11 @@ path. Add it with -I<path> to the command line
|
||||
@@ -465,13 +468,17 @@ path. Add it with -I<path> to the command line
|
||||
|
||||
// A macro (V8_DEPRECATE_SOON) to make it easier to see what will be deprecated.
|
||||
#if defined(V8_IMMINENT_DEPRECATION_WARNINGS)
|
||||
-# define V8_DEPRECATE_SOON(message) [[deprecated(message)]]
|
||||
+# if defined(_MSC_VER) && _MSC_VER <= 1900
|
||||
+# if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER < 1920
|
||||
+# define V8_DEPRECATE_SOON(message) __declspec(deprecated(message))
|
||||
+# else
|
||||
+# define V8_DEPRECATE_SOON(message) [[deprecated(message)]]
|
||||
@@ -41,3 +38,10 @@ index 77fd65c6c5b7d8c0a7fe7a37c40e17ce66f49ce6..010b3633546601ba70a55aeb8e8fc503
|
||||
#else
|
||||
# define V8_DEPRECATE_SOON(message)
|
||||
#endif
|
||||
|
||||
|
||||
-#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 6)
|
||||
+#if !defined(__clang__) && (defined(__GNUC__) && __GNUC__ < 6) || (defined(_MSC_VER) && _MSC_VER < 1920)
|
||||
# define V8_ENUM_DEPRECATED(message)
|
||||
# define V8_ENUM_DEPRECATE_SOON(message)
|
||||
#else
|
||||
@@ -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'
|
||||
@@ -55,7 +56,7 @@ const getCustomOptions = () => {
|
||||
async function main () {
|
||||
const options = args.default ? defaultOptions : getCustomOptions();
|
||||
|
||||
const testChild = cp.spawn('python', options, {
|
||||
const testChild = cp.spawn('python3', options, {
|
||||
env: {
|
||||
...process.env,
|
||||
ELECTRON_RUN_AS_NODE: 'true',
|
||||
|
||||
@@ -62,9 +62,9 @@ async function circleCIcall (targetBranch, workflowName, options) {
|
||||
parameters: {}
|
||||
};
|
||||
if (options.ghRelease) {
|
||||
buildRequest.parameters['upload-to-s3'] = '0';
|
||||
buildRequest.parameters['upload-to-storage'] = '0';
|
||||
} else {
|
||||
buildRequest.parameters['upload-to-s3'] = '1';
|
||||
buildRequest.parameters['upload-to-storage'] = '1';
|
||||
}
|
||||
buildRequest.parameters[`run-${workflowName}`] = true;
|
||||
if (options.arch) {
|
||||
@@ -205,7 +205,7 @@ async function callAppVeyor (targetBranch, job, options) {
|
||||
};
|
||||
|
||||
if (!options.ghRelease) {
|
||||
environmentVariables.UPLOAD_TO_S3 = 1;
|
||||
environmentVariables.UPLOAD_TO_STORAGE = 1;
|
||||
}
|
||||
|
||||
const requestOpts = {
|
||||
|
||||
@@ -128,9 +128,8 @@ function assetsForVersion (version, validatingRelease) {
|
||||
`electron-${version}-mas-arm64-dsym-snapshot.zip`,
|
||||
`electron-${version}-mas-arm64-symbols.zip`,
|
||||
`electron-${version}-mas-arm64.zip`,
|
||||
// TODO(jkleinsc) Symbol generation on 32-bit Windows is temporarily disabled due to failures
|
||||
// `electron-${version}-win32-ia32-pdb.zip`,
|
||||
// `electron-${version}-win32-ia32-symbols.zip`,
|
||||
`electron-${version}-win32-ia32-pdb.zip`,
|
||||
`electron-${version}-win32-ia32-symbols.zip`,
|
||||
`electron-${version}-win32-ia32.zip`,
|
||||
`electron-${version}-win32-x64-pdb.zip`,
|
||||
`electron-${version}-win32-x64-symbols.zip`,
|
||||
|
||||
@@ -47,7 +47,7 @@ def main():
|
||||
args = parse_args()
|
||||
if args.verbose:
|
||||
enable_verbose_mode()
|
||||
if args.upload_to_s3:
|
||||
if args.upload_to_storage:
|
||||
utcnow = datetime.datetime.utcnow()
|
||||
args.upload_timestamp = utcnow.strftime('%Y%m%d')
|
||||
|
||||
@@ -64,7 +64,7 @@ def main():
|
||||
if not release['draft']:
|
||||
tag_exists = True
|
||||
|
||||
if not args.upload_to_s3:
|
||||
if not args.upload_to_storage:
|
||||
assert release['exists'], \
|
||||
'Release does not exist; cannot upload to GitHub!'
|
||||
assert tag_exists == args.overwrite, \
|
||||
@@ -76,10 +76,9 @@ def main():
|
||||
shutil.copy2(os.path.join(OUT_DIR, 'dist.zip'), electron_zip)
|
||||
upload_electron(release, electron_zip, args)
|
||||
if get_target_arch() != 'mips64el':
|
||||
if get_target_arch() != 'ia32' or PLATFORM != 'win32':
|
||||
symbols_zip = os.path.join(OUT_DIR, SYMBOLS_NAME)
|
||||
shutil.copy2(os.path.join(OUT_DIR, 'symbols.zip'), symbols_zip)
|
||||
upload_electron(release, symbols_zip, args)
|
||||
symbols_zip = os.path.join(OUT_DIR, SYMBOLS_NAME)
|
||||
shutil.copy2(os.path.join(OUT_DIR, 'symbols.zip'), symbols_zip)
|
||||
upload_electron(release, symbols_zip, args)
|
||||
if PLATFORM == 'darwin':
|
||||
if get_platform_key() == 'darwin' and get_target_arch() == 'x64':
|
||||
api_path = os.path.join(ELECTRON_DIR, 'electron-api.json')
|
||||
@@ -96,10 +95,9 @@ def main():
|
||||
shutil.copy2(os.path.join(OUT_DIR, 'dsym-snapshot.zip'), dsym_snaphot_zip)
|
||||
upload_electron(release, dsym_snaphot_zip, args)
|
||||
elif PLATFORM == 'win32':
|
||||
if get_target_arch() != 'ia32':
|
||||
pdb_zip = os.path.join(OUT_DIR, PDB_NAME)
|
||||
shutil.copy2(os.path.join(OUT_DIR, 'pdb.zip'), pdb_zip)
|
||||
upload_electron(release, pdb_zip, args)
|
||||
pdb_zip = os.path.join(OUT_DIR, PDB_NAME)
|
||||
shutil.copy2(os.path.join(OUT_DIR, 'pdb.zip'), pdb_zip)
|
||||
upload_electron(release, pdb_zip, args)
|
||||
elif PLATFORM == 'linux':
|
||||
debug_zip = os.path.join(OUT_DIR, DEBUG_NAME)
|
||||
shutil.copy2(os.path.join(OUT_DIR, 'debug.zip'), debug_zip)
|
||||
@@ -148,7 +146,7 @@ def main():
|
||||
OUT_DIR, 'hunspell_dictionaries.zip')
|
||||
upload_electron(release, hunspell_dictionaries_zip, args)
|
||||
|
||||
if not tag_exists and not args.upload_to_s3:
|
||||
if not tag_exists and not args.upload_to_storage:
|
||||
# Upload symbols to symbol server.
|
||||
run_python_upload_script('upload-symbols.py')
|
||||
if PLATFORM == 'win32':
|
||||
@@ -174,9 +172,9 @@ def parse_args():
|
||||
parser.add_argument('-p', '--publish-release',
|
||||
help='Publish the release',
|
||||
action='store_true')
|
||||
parser.add_argument('-s', '--upload_to_s3',
|
||||
help='Upload assets to s3 bucket',
|
||||
dest='upload_to_s3',
|
||||
parser.add_argument('-s', '--upload_to_storage',
|
||||
help='Upload assets to azure bucket',
|
||||
dest='upload_to_storage',
|
||||
action='store_true',
|
||||
default=False,
|
||||
required=False)
|
||||
@@ -342,9 +340,9 @@ def upload_electron(release, file_path, args):
|
||||
except NonZipFileError:
|
||||
pass
|
||||
|
||||
# if upload_to_s3 is set, skip github upload.
|
||||
# todo (vertedinde): migrate this variable to upload_to_az
|
||||
if args.upload_to_s3:
|
||||
# if upload_to_storage is set, skip github upload.
|
||||
# todo (vertedinde): migrate this variable to upload_to_storage
|
||||
if args.upload_to_storage:
|
||||
key_prefix = 'release-builds/{0}_{1}'.format(args.version,
|
||||
args.upload_timestamp)
|
||||
store_artifact(os.path.dirname(file_path), key_prefix, [file_path])
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -519,24 +519,21 @@ bool NotificationCallbackWrapper(
|
||||
const base::RepeatingCallback<
|
||||
void(const base::CommandLine& command_line,
|
||||
const base::FilePath& current_directory,
|
||||
const std::vector<uint8_t> additional_data,
|
||||
const ProcessSingleton::NotificationAckCallback& ack_callback)>&
|
||||
callback,
|
||||
const std::vector<const uint8_t> additional_data)>& callback,
|
||||
const base::CommandLine& cmd,
|
||||
const base::FilePath& cwd,
|
||||
const std::vector<uint8_t> additional_data,
|
||||
const ProcessSingleton::NotificationAckCallback& ack_callback) {
|
||||
const std::vector<const uint8_t> additional_data) {
|
||||
// Make sure the callback is called after app gets ready.
|
||||
if (Browser::Get()->is_ready()) {
|
||||
callback.Run(cmd, cwd, std::move(additional_data), ack_callback);
|
||||
callback.Run(cmd, cwd, std::move(additional_data));
|
||||
} else {
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner(
|
||||
base::ThreadTaskRunnerHandle::Get());
|
||||
|
||||
// Make a copy of the span so that the data isn't lost.
|
||||
task_runner->PostTask(
|
||||
FROM_HERE, base::BindOnce(base::IgnoreResult(callback), cmd, cwd,
|
||||
std::move(additional_data), ack_callback));
|
||||
task_runner->PostTask(FROM_HERE,
|
||||
base::BindOnce(base::IgnoreResult(callback), cmd, cwd,
|
||||
std::move(additional_data)));
|
||||
}
|
||||
// ProcessSingleton needs to know whether current process is quiting.
|
||||
return !Browser::Get()->is_shutting_down();
|
||||
@@ -1083,54 +1080,15 @@ std::string App::GetLocaleCountryCode() {
|
||||
return region.size() == 2 ? region : std::string();
|
||||
}
|
||||
|
||||
void App::OnFirstInstanceAck(
|
||||
const base::span<const uint8_t>* first_instance_data) {
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::Locker locker(isolate);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
base::Value data_to_send;
|
||||
if (first_instance_data) {
|
||||
// Don't send back the local directly, because it might be empty.
|
||||
v8::Local<v8::Value> data_local;
|
||||
data_local = DeserializeV8Value(isolate, *first_instance_data);
|
||||
if (!data_local.IsEmpty()) {
|
||||
gin::ConvertFromV8(isolate, data_local, &data_to_send);
|
||||
}
|
||||
}
|
||||
Emit("first-instance-ack", data_to_send);
|
||||
}
|
||||
|
||||
// This function handles the user calling
|
||||
// the callback parameter sent out by the second-instance event.
|
||||
static void AckCallbackWrapper(
|
||||
const ProcessSingleton::NotificationAckCallback& ack_callback,
|
||||
gin::Arguments* args) {
|
||||
blink::CloneableMessage ack_message;
|
||||
args->GetNext(&ack_message);
|
||||
if (!ack_message.encoded_message.empty()) {
|
||||
ack_callback.Run(&ack_message.encoded_message);
|
||||
} else {
|
||||
ack_callback.Run(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void App::OnSecondInstance(
|
||||
const base::CommandLine& cmd,
|
||||
const base::FilePath& cwd,
|
||||
const std::vector<uint8_t> additional_data,
|
||||
const ProcessSingleton::NotificationAckCallback& ack_callback) {
|
||||
void App::OnSecondInstance(const base::CommandLine& cmd,
|
||||
const base::FilePath& cwd,
|
||||
const std::vector<const uint8_t> additional_data) {
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::Locker locker(isolate);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Value> data_value =
|
||||
DeserializeV8Value(isolate, std::move(additional_data));
|
||||
auto cb = base::BindRepeating(&AckCallbackWrapper, ack_callback);
|
||||
bool prevent_default =
|
||||
Emit("second-instance", cmd.argv(), cwd, data_value, cb);
|
||||
if (!prevent_default) {
|
||||
// Call the callback ourselves, and send back nothing.
|
||||
ack_callback.Run(nullptr);
|
||||
}
|
||||
Emit("second-instance", cmd.argv(), cwd, data_value);
|
||||
}
|
||||
|
||||
bool App::HasSingleInstanceLock() const {
|
||||
@@ -1151,9 +1109,6 @@ bool App::RequestSingleInstanceLock(gin::Arguments* args) {
|
||||
base::CreateDirectoryAndGetError(user_dir, nullptr);
|
||||
|
||||
auto cb = base::BindRepeating(&App::OnSecondInstance, base::Unretained(this));
|
||||
auto wrapped_cb = base::BindRepeating(NotificationCallbackWrapper, cb);
|
||||
auto ack_cb =
|
||||
base::BindRepeating(&App::OnFirstInstanceAck, base::Unretained(this));
|
||||
|
||||
blink::CloneableMessage additional_data_message;
|
||||
args->GetNext(&additional_data_message);
|
||||
@@ -1162,10 +1117,11 @@ bool App::RequestSingleInstanceLock(gin::Arguments* args) {
|
||||
IsSandboxEnabled(base::CommandLine::ForCurrentProcess());
|
||||
process_singleton_ = std::make_unique<ProcessSingleton>(
|
||||
program_name, user_dir, additional_data_message.encoded_message,
|
||||
app_is_sandboxed, wrapped_cb, ack_cb);
|
||||
app_is_sandboxed, base::BindRepeating(NotificationCallbackWrapper, cb));
|
||||
#else
|
||||
process_singleton_ = std::make_unique<ProcessSingleton>(
|
||||
user_dir, additional_data_message.encoded_message, wrapped_cb, ack_cb);
|
||||
user_dir, additional_data_message.encoded_message,
|
||||
base::BindRepeating(NotificationCallbackWrapper, cb));
|
||||
#endif
|
||||
|
||||
switch (process_singleton_->NotifyOtherProcessOrCreate()) {
|
||||
|
||||
@@ -193,12 +193,9 @@ class App : public ElectronBrowserClient::Delegate,
|
||||
void SetDesktopName(const std::string& desktop_name);
|
||||
std::string GetLocale();
|
||||
std::string GetLocaleCountryCode();
|
||||
void OnFirstInstanceAck(const base::span<const uint8_t>* first_instance_data);
|
||||
void OnSecondInstance(
|
||||
const base::CommandLine& cmd,
|
||||
const base::FilePath& cwd,
|
||||
const std::vector<uint8_t> additional_data,
|
||||
const ProcessSingleton::NotificationAckCallback& ack_callback);
|
||||
void OnSecondInstance(const base::CommandLine& cmd,
|
||||
const base::FilePath& cwd,
|
||||
const std::vector<const uint8_t> additional_data);
|
||||
bool HasSingleInstanceLock() const;
|
||||
bool RequestSingleInstanceLock(gin::Arguments* args);
|
||||
void ReleaseSingleInstanceLock();
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "base/path_service.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "components/upload_list/crash_upload_list.h"
|
||||
#include "components/upload_list/text_log_upload_list.h"
|
||||
@@ -135,6 +136,7 @@ void Start(const std::string& submit_url,
|
||||
const std::map<std::string, std::string>& global_extra,
|
||||
const std::map<std::string, std::string>& extra,
|
||||
bool is_node_process) {
|
||||
TRACE_EVENT0("electron", "crash_reporter::Start");
|
||||
#if !defined(MAS_BUILD)
|
||||
if (g_crash_reporter_initialized)
|
||||
return;
|
||||
|
||||
@@ -31,12 +31,24 @@ void SetElectronCryptoReady(bool ready) {
|
||||
#endif
|
||||
|
||||
bool IsEncryptionAvailable() {
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
// Calling IsEncryptionAvailable() before the app is ready results in a crash
|
||||
// on Linux.
|
||||
// Refs: https://github.com/electron/electron/issues/32206.
|
||||
if (!Browser::Get()->is_ready())
|
||||
return false;
|
||||
#endif
|
||||
return OSCrypt::IsEncryptionAvailable();
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> EncryptString(v8::Isolate* isolate,
|
||||
const std::string& plaintext) {
|
||||
if (!OSCrypt::IsEncryptionAvailable()) {
|
||||
if (!IsEncryptionAvailable()) {
|
||||
if (!Browser::Get()->is_ready()) {
|
||||
gin_helper::ErrorThrower(isolate).ThrowError(
|
||||
"safeStorage cannot be used before app is ready");
|
||||
return v8::Local<v8::Value>();
|
||||
}
|
||||
gin_helper::ErrorThrower(isolate).ThrowError(
|
||||
"Error while decrypting the ciphertext provided to "
|
||||
"safeStorage.decryptString. "
|
||||
@@ -59,7 +71,12 @@ v8::Local<v8::Value> EncryptString(v8::Isolate* isolate,
|
||||
}
|
||||
|
||||
std::string DecryptString(v8::Isolate* isolate, v8::Local<v8::Value> buffer) {
|
||||
if (!OSCrypt::IsEncryptionAvailable()) {
|
||||
if (!IsEncryptionAvailable()) {
|
||||
if (!Browser::Get()->is_ready()) {
|
||||
gin_helper::ErrorThrower(isolate).ThrowError(
|
||||
"safeStorage cannot be used before app is ready");
|
||||
return "";
|
||||
}
|
||||
gin_helper::ErrorThrower(isolate).ThrowError(
|
||||
"Error while decrypting the ciphertext provided to "
|
||||
"safeStorage.decryptString. "
|
||||
|
||||
@@ -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"
|
||||
@@ -660,9 +661,10 @@ 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);
|
||||
absl::optional<std::string> user_agent_override =
|
||||
GetBrowserContext()->GetUserAgentOverride();
|
||||
if (user_agent_override)
|
||||
SetUserAgent(*user_agent_override);
|
||||
web_contents->SetUserData(kElectronApiWebContentsKey,
|
||||
std::make_unique<UserDataLink>(GetWeakPtr()));
|
||||
InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate));
|
||||
@@ -868,9 +870,10 @@ void WebContents::InitWithSessionAndOptions(
|
||||
|
||||
AutofillDriverFactory::CreateForWebContents(web_contents());
|
||||
|
||||
web_contents()->SetUserAgentOverride(blink::UserAgentOverride::UserAgentOnly(
|
||||
GetBrowserContext()->GetUserAgent()),
|
||||
false);
|
||||
absl::optional<std::string> user_agent_override =
|
||||
GetBrowserContext()->GetUserAgentOverride();
|
||||
if (user_agent_override)
|
||||
SetUserAgent(*user_agent_override);
|
||||
|
||||
if (IsGuest()) {
|
||||
NativeWindow* owner_window = nullptr;
|
||||
@@ -1312,8 +1315,6 @@ void WebContents::EnterFullscreenModeForTab(
|
||||
base::BindRepeating(&WebContents::OnEnterFullscreenModeForTab,
|
||||
base::Unretained(this), requesting_frame, options);
|
||||
permission_helper->RequestFullscreenPermission(callback);
|
||||
exclusive_access_manager_->fullscreen_controller()->EnterFullscreenModeForTab(
|
||||
requesting_frame, options.display_id);
|
||||
}
|
||||
|
||||
void WebContents::OnEnterFullscreenModeForTab(
|
||||
@@ -1329,6 +1330,9 @@ void WebContents::OnEnterFullscreenModeForTab(
|
||||
return;
|
||||
}
|
||||
|
||||
exclusive_access_manager_->fullscreen_controller()->EnterFullscreenModeForTab(
|
||||
requesting_frame, options.display_id);
|
||||
|
||||
SetHtmlApiFullscreen(true);
|
||||
|
||||
if (native_fullscreen_) {
|
||||
@@ -1510,7 +1514,7 @@ void WebContents::HandleNewRenderFrame(
|
||||
|
||||
auto* web_frame = WebFrameMain::FromRenderFrameHost(render_frame_host);
|
||||
if (web_frame)
|
||||
web_frame->Connect();
|
||||
web_frame->MaybeSetupMojoConnection();
|
||||
}
|
||||
|
||||
void WebContents::OnBackgroundColorChanged() {
|
||||
@@ -2156,8 +2160,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))
|
||||
@@ -2372,8 +2375,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,7 +2600,7 @@ bool WebContents::IsCurrentlyAudible() {
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
void WebContents::OnGetDefaultPrinter(
|
||||
base::Value print_settings,
|
||||
base::Value::Dict print_settings,
|
||||
printing::CompletionCallback print_callback,
|
||||
std::u16string device_name,
|
||||
bool silent,
|
||||
@@ -2623,7 +2630,7 @@ void WebContents::OnGetDefaultPrinter(
|
||||
return;
|
||||
}
|
||||
|
||||
print_settings.SetStringKey(printing::kSettingDeviceName, printer_name);
|
||||
print_settings.Set(printing::kSettingDeviceName, printer_name);
|
||||
|
||||
auto* print_view_manager =
|
||||
PrintViewManagerElectron::FromWebContents(web_contents());
|
||||
@@ -2642,7 +2649,7 @@ void WebContents::OnGetDefaultPrinter(
|
||||
void WebContents::Print(gin::Arguments* args) {
|
||||
gin_helper::Dictionary options =
|
||||
gin::Dictionary::CreateEmpty(args->isolate());
|
||||
base::Value settings(base::Value::Type::DICTIONARY);
|
||||
base::Value::Dict settings;
|
||||
|
||||
if (args->Length() >= 1 && !args->GetNext(&options)) {
|
||||
gin_helper::ErrorThrower(args->isolate())
|
||||
@@ -2663,8 +2670,7 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
|
||||
bool print_background = false;
|
||||
options.Get("printBackground", &print_background);
|
||||
settings.SetBoolKey(printing::kSettingShouldPrintBackgrounds,
|
||||
print_background);
|
||||
settings.Set(printing::kSettingShouldPrintBackgrounds, print_background);
|
||||
|
||||
// Set custom margin settings
|
||||
gin_helper::Dictionary margins =
|
||||
@@ -2673,28 +2679,26 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
printing::mojom::MarginType margin_type =
|
||||
printing::mojom::MarginType::kDefaultMargins;
|
||||
margins.Get("marginType", &margin_type);
|
||||
settings.SetIntKey(printing::kSettingMarginsType,
|
||||
static_cast<int>(margin_type));
|
||||
settings.Set(printing::kSettingMarginsType, static_cast<int>(margin_type));
|
||||
|
||||
if (margin_type == printing::mojom::MarginType::kCustomMargins) {
|
||||
base::Value custom_margins(base::Value::Type::DICTIONARY);
|
||||
base::Value::Dict custom_margins;
|
||||
int top = 0;
|
||||
margins.Get("top", &top);
|
||||
custom_margins.SetIntKey(printing::kSettingMarginTop, top);
|
||||
custom_margins.Set(printing::kSettingMarginTop, top);
|
||||
int bottom = 0;
|
||||
margins.Get("bottom", &bottom);
|
||||
custom_margins.SetIntKey(printing::kSettingMarginBottom, bottom);
|
||||
custom_margins.Set(printing::kSettingMarginBottom, bottom);
|
||||
int left = 0;
|
||||
margins.Get("left", &left);
|
||||
custom_margins.SetIntKey(printing::kSettingMarginLeft, left);
|
||||
custom_margins.Set(printing::kSettingMarginLeft, left);
|
||||
int right = 0;
|
||||
margins.Get("right", &right);
|
||||
custom_margins.SetIntKey(printing::kSettingMarginRight, right);
|
||||
settings.SetPath(printing::kSettingMarginsCustom,
|
||||
std::move(custom_margins));
|
||||
custom_margins.Set(printing::kSettingMarginRight, right);
|
||||
settings.Set(printing::kSettingMarginsCustom, std::move(custom_margins));
|
||||
}
|
||||
} else {
|
||||
settings.SetIntKey(
|
||||
settings.Set(
|
||||
printing::kSettingMarginsType,
|
||||
static_cast<int>(printing::mojom::MarginType::kDefaultMargins));
|
||||
}
|
||||
@@ -2704,12 +2708,12 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
options.Get("color", &print_color);
|
||||
auto const color_model = print_color ? printing::mojom::ColorModel::kColor
|
||||
: printing::mojom::ColorModel::kGray;
|
||||
settings.SetIntKey(printing::kSettingColor, static_cast<int>(color_model));
|
||||
settings.Set(printing::kSettingColor, static_cast<int>(color_model));
|
||||
|
||||
// Is the orientation landscape or portrait.
|
||||
bool landscape = false;
|
||||
options.Get("landscape", &landscape);
|
||||
settings.SetBoolKey(printing::kSettingLandscape, landscape);
|
||||
settings.Set(printing::kSettingLandscape, landscape);
|
||||
|
||||
// We set the default to the system's default printer and only update
|
||||
// if at the Chromium level if the user overrides.
|
||||
@@ -2724,21 +2728,21 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
|
||||
int scale_factor = 100;
|
||||
options.Get("scaleFactor", &scale_factor);
|
||||
settings.SetIntKey(printing::kSettingScaleFactor, scale_factor);
|
||||
settings.Set(printing::kSettingScaleFactor, scale_factor);
|
||||
|
||||
int pages_per_sheet = 1;
|
||||
options.Get("pagesPerSheet", &pages_per_sheet);
|
||||
settings.SetIntKey(printing::kSettingPagesPerSheet, pages_per_sheet);
|
||||
settings.Set(printing::kSettingPagesPerSheet, pages_per_sheet);
|
||||
|
||||
// True if the user wants to print with collate.
|
||||
bool collate = true;
|
||||
options.Get("collate", &collate);
|
||||
settings.SetBoolKey(printing::kSettingCollate, collate);
|
||||
settings.Set(printing::kSettingCollate, collate);
|
||||
|
||||
// The number of individual copies to print
|
||||
int copies = 1;
|
||||
options.Get("copies", &copies);
|
||||
settings.SetIntKey(printing::kSettingCopies, copies);
|
||||
settings.Set(printing::kSettingCopies, copies);
|
||||
|
||||
// Strings to be printed as headers and footers if requested by the user.
|
||||
std::string header;
|
||||
@@ -2747,53 +2751,52 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
options.Get("footer", &footer);
|
||||
|
||||
if (!(header.empty() && footer.empty())) {
|
||||
settings.SetBoolKey(printing::kSettingHeaderFooterEnabled, true);
|
||||
settings.Set(printing::kSettingHeaderFooterEnabled, true);
|
||||
|
||||
settings.SetStringKey(printing::kSettingHeaderFooterTitle, header);
|
||||
settings.SetStringKey(printing::kSettingHeaderFooterURL, footer);
|
||||
settings.Set(printing::kSettingHeaderFooterTitle, header);
|
||||
settings.Set(printing::kSettingHeaderFooterURL, footer);
|
||||
} else {
|
||||
settings.SetBoolKey(printing::kSettingHeaderFooterEnabled, false);
|
||||
settings.Set(printing::kSettingHeaderFooterEnabled, false);
|
||||
}
|
||||
|
||||
// We don't want to allow the user to enable these settings
|
||||
// but we need to set them or a CHECK is hit.
|
||||
settings.SetIntKey(printing::kSettingPrinterType,
|
||||
static_cast<int>(printing::mojom::PrinterType::kLocal));
|
||||
settings.SetBoolKey(printing::kSettingShouldPrintSelectionOnly, false);
|
||||
settings.SetBoolKey(printing::kSettingRasterizePdf, false);
|
||||
settings.Set(printing::kSettingPrinterType,
|
||||
static_cast<int>(printing::mojom::PrinterType::kLocal));
|
||||
settings.Set(printing::kSettingShouldPrintSelectionOnly, false);
|
||||
settings.Set(printing::kSettingRasterizePdf, false);
|
||||
|
||||
// Set custom page ranges to print
|
||||
std::vector<gin_helper::Dictionary> page_ranges;
|
||||
if (options.Get("pageRanges", &page_ranges)) {
|
||||
base::Value page_range_list(base::Value::Type::LIST);
|
||||
base::Value::List page_range_list;
|
||||
for (auto& range : page_ranges) {
|
||||
int from, to;
|
||||
if (range.Get("from", &from) && range.Get("to", &to)) {
|
||||
base::Value range(base::Value::Type::DICTIONARY);
|
||||
base::Value::Dict range;
|
||||
// Chromium uses 1-based page ranges, so increment each by 1.
|
||||
range.SetIntKey(printing::kSettingPageRangeFrom, from + 1);
|
||||
range.SetIntKey(printing::kSettingPageRangeTo, to + 1);
|
||||
range.Set(printing::kSettingPageRangeFrom, from + 1);
|
||||
range.Set(printing::kSettingPageRangeTo, to + 1);
|
||||
page_range_list.Append(std::move(range));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!page_range_list.GetListDeprecated().empty())
|
||||
settings.SetPath(printing::kSettingPageRange, std::move(page_range_list));
|
||||
if (!page_range_list.empty())
|
||||
settings.Set(printing::kSettingPageRange, std::move(page_range_list));
|
||||
}
|
||||
|
||||
// Duplex type user wants to use.
|
||||
printing::mojom::DuplexMode duplex_mode =
|
||||
printing::mojom::DuplexMode::kSimplex;
|
||||
options.Get("duplexMode", &duplex_mode);
|
||||
settings.SetIntKey(printing::kSettingDuplexMode,
|
||||
static_cast<int>(duplex_mode));
|
||||
settings.Set(printing::kSettingDuplexMode, static_cast<int>(duplex_mode));
|
||||
|
||||
// We've already done necessary parameter sanitization at the
|
||||
// JS level, so we can simply pass this through.
|
||||
base::Value media_size(base::Value::Type::DICTIONARY);
|
||||
if (options.Get("mediaSize", &media_size))
|
||||
settings.SetKey(printing::kSettingMediaSize, std::move(media_size));
|
||||
settings.Set(printing::kSettingMediaSize, std::move(media_size));
|
||||
|
||||
// Set custom dots per inch (dpi)
|
||||
gin_helper::Dictionary dpi_settings;
|
||||
@@ -2801,13 +2804,13 @@ void WebContents::Print(gin::Arguments* args) {
|
||||
if (options.Get("dpi", &dpi_settings)) {
|
||||
int horizontal = 72;
|
||||
dpi_settings.Get("horizontal", &horizontal);
|
||||
settings.SetIntKey(printing::kSettingDpiHorizontal, horizontal);
|
||||
settings.Set(printing::kSettingDpiHorizontal, horizontal);
|
||||
int vertical = 72;
|
||||
dpi_settings.Get("vertical", &vertical);
|
||||
settings.SetIntKey(printing::kSettingDpiVertical, vertical);
|
||||
settings.Set(printing::kSettingDpiVertical, vertical);
|
||||
} else {
|
||||
settings.SetIntKey(printing::kSettingDpiHorizontal, dpi);
|
||||
settings.SetIntKey(printing::kSettingDpiVertical, dpi);
|
||||
settings.Set(printing::kSettingDpiHorizontal, dpi);
|
||||
settings.Set(printing::kSettingDpiVertical, dpi);
|
||||
}
|
||||
|
||||
print_task_runner_->PostTaskAndReplyWithResult(
|
||||
|
||||
@@ -222,7 +222,7 @@ class WebContents : public ExclusiveAccessContext,
|
||||
void HandleNewRenderFrame(content::RenderFrameHost* render_frame_host);
|
||||
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
void OnGetDefaultPrinter(base::Value print_settings,
|
||||
void OnGetDefaultPrinter(base::Value::Dict print_settings,
|
||||
printing::CompletionCallback print_callback,
|
||||
std::u16string device_name,
|
||||
bool silent,
|
||||
|
||||
@@ -96,13 +96,15 @@ void WebFrameMain::Destroyed() {
|
||||
void WebFrameMain::MarkRenderFrameDisposed() {
|
||||
render_frame_ = nullptr;
|
||||
render_frame_disposed_ = true;
|
||||
TeardownMojoConnection();
|
||||
}
|
||||
|
||||
void WebFrameMain::UpdateRenderFrameHost(content::RenderFrameHost* rfh) {
|
||||
// Should only be called when swapping frames.
|
||||
render_frame_disposed_ = false;
|
||||
render_frame_ = rfh;
|
||||
renderer_api_.reset();
|
||||
TeardownMojoConnection();
|
||||
MaybeSetupMojoConnection();
|
||||
}
|
||||
|
||||
bool WebFrameMain::CheckRenderFrame() const {
|
||||
@@ -182,20 +184,42 @@ void WebFrameMain::Send(v8::Isolate* isolate,
|
||||
}
|
||||
|
||||
const mojo::Remote<mojom::ElectronRenderer>& WebFrameMain::GetRendererApi() {
|
||||
if (!renderer_api_) {
|
||||
pending_receiver_ = renderer_api_.BindNewPipeAndPassReceiver();
|
||||
if (render_frame_->IsRenderFrameCreated()) {
|
||||
render_frame_->GetRemoteInterfaces()->GetInterface(
|
||||
std::move(pending_receiver_));
|
||||
}
|
||||
renderer_api_.set_disconnect_handler(base::BindOnce(
|
||||
&WebFrameMain::OnRendererConnectionError, weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
MaybeSetupMojoConnection();
|
||||
return renderer_api_;
|
||||
}
|
||||
|
||||
void WebFrameMain::OnRendererConnectionError() {
|
||||
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_ &&
|
||||
render_frame_->IsRenderFrameCreated()) {
|
||||
render_frame_->GetRemoteInterfaces()->GetInterface(
|
||||
std::move(pending_receiver_));
|
||||
}
|
||||
}
|
||||
|
||||
void WebFrameMain::TeardownMojoConnection() {
|
||||
renderer_api_.reset();
|
||||
pending_receiver_.reset();
|
||||
}
|
||||
|
||||
void WebFrameMain::OnRendererConnectionError() {
|
||||
TeardownMojoConnection();
|
||||
}
|
||||
|
||||
void WebFrameMain::PostMessage(v8::Isolate* isolate,
|
||||
@@ -315,13 +339,6 @@ std::vector<content::RenderFrameHost*> WebFrameMain::FramesInSubtree() const {
|
||||
return frame_hosts;
|
||||
}
|
||||
|
||||
void WebFrameMain::Connect() {
|
||||
if (pending_receiver_) {
|
||||
render_frame_->GetRemoteInterfaces()->GetInterface(
|
||||
std::move(pending_receiver_));
|
||||
}
|
||||
}
|
||||
|
||||
void WebFrameMain::DOMContentLoaded() {
|
||||
Emit("dom-ready");
|
||||
}
|
||||
|
||||
@@ -82,6 +82,9 @@ class WebFrameMain : public gin::Wrappable<WebFrameMain>,
|
||||
void UpdateRenderFrameHost(content::RenderFrameHost* rfh);
|
||||
|
||||
const mojo::Remote<mojom::ElectronRenderer>& GetRendererApi();
|
||||
void MaybeSetupMojoConnection();
|
||||
void TeardownMojoConnection();
|
||||
void OnRendererConnectionError();
|
||||
|
||||
// WebFrameMain can outlive its RenderFrameHost pointer so we need to check
|
||||
// whether its been disposed of prior to accessing it.
|
||||
@@ -112,8 +115,6 @@ class WebFrameMain : public gin::Wrappable<WebFrameMain>,
|
||||
std::vector<content::RenderFrameHost*> Frames() const;
|
||||
std::vector<content::RenderFrameHost*> FramesInSubtree() const;
|
||||
|
||||
void OnRendererConnectionError();
|
||||
void Connect();
|
||||
void DOMContentLoaded();
|
||||
|
||||
mojo::Remote<mojom::ElectronRenderer> renderer_api_;
|
||||
|
||||
@@ -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,
|
||||
@@ -1333,6 +1341,11 @@ void ElectronBrowserClient::
|
||||
DCHECK(browser_context);
|
||||
DCHECK(factories);
|
||||
|
||||
auto* protocol_registry =
|
||||
ProtocolRegistry::FromBrowserContext(browser_context);
|
||||
protocol_registry->RegisterURLLoaderFactories(factories,
|
||||
false /* allow_file_access */);
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
factories->emplace(
|
||||
extensions::kExtensionScheme,
|
||||
|
||||
@@ -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,6 +305,11 @@ ElectronBrowserContext::GetSpecialStoragePolicy() {
|
||||
}
|
||||
|
||||
std::string ElectronBrowserContext::GetUserAgent() const {
|
||||
return user_agent_.value_or(ElectronBrowserClient::Get()->GetUserAgent());
|
||||
}
|
||||
|
||||
absl::optional<std::string> ElectronBrowserContext::GetUserAgentOverride()
|
||||
const {
|
||||
return user_agent_;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ class ElectronBrowserContext : public content::BrowserContext {
|
||||
|
||||
void SetUserAgent(const std::string& user_agent);
|
||||
std::string GetUserAgent() const;
|
||||
absl::optional<std::string> GetUserAgentOverride() const;
|
||||
bool CanUseHttpCache() const;
|
||||
int GetMaxCacheSize() const;
|
||||
ResolveProxyHelper* GetResolveProxyHelper();
|
||||
@@ -170,7 +171,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;
|
||||
|
||||
@@ -376,6 +376,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:
|
||||
|
||||
@@ -335,22 +335,32 @@ bool ElectronPermissionManager::CheckDevicePermission(
|
||||
static_cast<content::PermissionType>(
|
||||
WebContentsPermissionHelper::PermissionType::SERIAL)) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
if (device->FindStringKey(kDeviceInstanceIdKey) ==
|
||||
granted_device.FindStringKey(kDeviceInstanceIdKey))
|
||||
const auto* instance_id = device->FindStringKey(kDeviceInstanceIdKey);
|
||||
const auto* port_instance_id =
|
||||
granted_device.FindStringKey(kDeviceInstanceIdKey);
|
||||
if (instance_id && port_instance_id &&
|
||||
*instance_id == *port_instance_id)
|
||||
return true;
|
||||
#else
|
||||
const auto* serial_number =
|
||||
granted_device.FindStringKey(kSerialNumberKey);
|
||||
const auto* port_serial_number =
|
||||
device->FindStringKey(kSerialNumberKey);
|
||||
if (device->FindIntKey(kVendorIdKey) !=
|
||||
granted_device.FindIntKey(kVendorIdKey) ||
|
||||
device->FindIntKey(kProductIdKey) !=
|
||||
granted_device.FindIntKey(kProductIdKey) ||
|
||||
*device->FindStringKey(kSerialNumberKey) !=
|
||||
*granted_device.FindStringKey(kSerialNumberKey)) {
|
||||
(serial_number && port_serial_number &&
|
||||
*port_serial_number != *serial_number)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
if (*device->FindStringKey(kUsbDriverKey) !=
|
||||
*granted_device.FindStringKey(kUsbDriverKey)) {
|
||||
const auto* usb_driver_key = device->FindStringKey(kUsbDriverKey);
|
||||
const auto* port_usb_driver_key =
|
||||
granted_device.FindStringKey(kUsbDriverKey);
|
||||
if (usb_driver_key && port_usb_driver_key &&
|
||||
*usb_driver_key != *port_usb_driver_key) {
|
||||
continue;
|
||||
}
|
||||
#endif // BUILDFLAG(IS_MAC)
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -166,6 +166,8 @@ class NativeWindowMac : public NativeWindow,
|
||||
|
||||
void UpdateVibrancyRadii(bool fullscreen);
|
||||
|
||||
void UpdateWindowOriginalFrame();
|
||||
|
||||
// Set the attribute of NSWindow while work around a bug of zoom button.
|
||||
void SetStyleMask(bool on, NSUInteger flag);
|
||||
void SetCollectionBehavior(bool on, NSUInteger flag);
|
||||
@@ -176,6 +178,10 @@ class NativeWindowMac : public NativeWindow,
|
||||
// Handle fullscreen transitions.
|
||||
void SetFullScreenTransitionState(FullScreenTransitionState state);
|
||||
void HandlePendingFullscreenTransitions();
|
||||
bool HandleDeferredClose();
|
||||
void SetHasDeferredWindowClose(bool defer_close) {
|
||||
has_deferred_window_close_ = defer_close;
|
||||
}
|
||||
|
||||
enum class VisualEffectState {
|
||||
kFollowWindow,
|
||||
@@ -249,6 +255,12 @@ class NativeWindowMac : public NativeWindow,
|
||||
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
|
||||
// transition is complete.
|
||||
bool has_deferred_window_close_ = false;
|
||||
|
||||
NSInteger attention_request_id_ = 0; // identifier from requestUserAttention
|
||||
|
||||
// The presentation options before entering kiosk mode.
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -474,6 +474,11 @@ void NativeWindowMac::Close() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fullscreen_transition_state() != FullScreenTransitionState::NONE) {
|
||||
SetHasDeferredWindowClose(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// If a sheet is attached to the window when we call
|
||||
// [window_ performClose:nil], the window won't close properly
|
||||
// even after the user has ended the sheet.
|
||||
@@ -603,13 +608,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() {
|
||||
@@ -647,7 +662,7 @@ void NativeWindowMac::Minimize() {
|
||||
|
||||
// Take note of the current window size
|
||||
if (IsNormal())
|
||||
original_frame_ = [window_ frame];
|
||||
UpdateWindowOriginalFrame();
|
||||
[window_ miniaturize:nil];
|
||||
}
|
||||
|
||||
@@ -668,6 +683,15 @@ void NativeWindowMac::HandlePendingFullscreenTransitions() {
|
||||
SetFullScreen(next_transition);
|
||||
}
|
||||
|
||||
bool NativeWindowMac::HandleDeferredClose() {
|
||||
if (has_deferred_window_close_) {
|
||||
SetHasDeferredWindowClose(false);
|
||||
Close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetFullScreen(bool fullscreen) {
|
||||
// [NSWindow -toggleFullScreen] is an asynchronous operation, which means
|
||||
// that it's possible to call it while a fullscreen transition is currently
|
||||
@@ -691,7 +715,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
|
||||
@@ -727,6 +751,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() {
|
||||
@@ -1001,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];
|
||||
}
|
||||
|
||||
@@ -1399,6 +1424,10 @@ void NativeWindowMac::UpdateVibrancyRadii(bool fullscreen) {
|
||||
}
|
||||
}
|
||||
|
||||
void NativeWindowMac::UpdateWindowOriginalFrame() {
|
||||
original_frame_ = [window_ frame];
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetVibrancy(const std::string& type) {
|
||||
NSVisualEffectView* vibrantView = [window_ vibrantView];
|
||||
|
||||
@@ -1503,12 +1532,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();
|
||||
}
|
||||
|
||||
|
||||
@@ -876,6 +876,11 @@ bool NativeWindowViews::IsMovable() {
|
||||
void NativeWindowViews::SetMinimizable(bool minimizable) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
FlipWindowStyle(GetAcceleratedWidget(), minimizable, WS_MINIMIZEBOX);
|
||||
if (titlebar_overlay_enabled()) {
|
||||
auto* frame_view =
|
||||
static_cast<WinFrameView*>(widget()->non_client_view()->frame_view());
|
||||
frame_view->caption_button_container()->UpdateButtons();
|
||||
}
|
||||
#endif
|
||||
minimizable_ = minimizable;
|
||||
}
|
||||
@@ -891,6 +896,11 @@ bool NativeWindowViews::IsMinimizable() {
|
||||
void NativeWindowViews::SetMaximizable(bool maximizable) {
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
FlipWindowStyle(GetAcceleratedWidget(), maximizable, WS_MAXIMIZEBOX);
|
||||
if (titlebar_overlay_enabled()) {
|
||||
auto* frame_view =
|
||||
static_cast<WinFrameView*>(widget()->non_client_view()->frame_view());
|
||||
frame_view->caption_button_container()->UpdateButtons();
|
||||
}
|
||||
#endif
|
||||
maximizable_ = maximizable;
|
||||
}
|
||||
@@ -926,6 +936,11 @@ void NativeWindowViews::SetClosable(bool closable) {
|
||||
} else {
|
||||
EnableMenuItem(menu, SC_CLOSE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
|
||||
}
|
||||
if (titlebar_overlay_enabled()) {
|
||||
auto* frame_view =
|
||||
static_cast<WinFrameView*>(widget()->non_client_view()->frame_view());
|
||||
frame_view->caption_button_container()->UpdateButtons();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -296,6 +296,7 @@ bool NativeWindowViews::PreHandleMSG(UINT message,
|
||||
&prevent_default);
|
||||
if (prevent_default) {
|
||||
::GetWindowRect(hwnd, reinterpret_cast<RECT*>(l_param));
|
||||
pending_bounds_change_.reset();
|
||||
return true; // Tells Windows that the Sizing is handled.
|
||||
}
|
||||
return false;
|
||||
@@ -334,6 +335,7 @@ bool NativeWindowViews::PreHandleMSG(UINT message,
|
||||
NotifyWindowWillMove(dpi_bounds, &prevent_default);
|
||||
if (!movable_ || prevent_default) {
|
||||
::GetWindowRect(hwnd, reinterpret_cast<RECT*>(l_param));
|
||||
pending_bounds_change_.reset();
|
||||
return true; // Tells Windows that the Move is handled. If not true,
|
||||
// frameless windows can be moved using
|
||||
// -webkit-app-region: drag elements.
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 19,0,0,5
|
||||
PRODUCTVERSION 19,0,0,5
|
||||
FILEVERSION 19,0,7,0
|
||||
PRODUCTVERSION 19,0,7,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -68,12 +68,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "19.0.0"
|
||||
VALUE "FileVersion", "19.0.7"
|
||||
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.0"
|
||||
VALUE "ProductVersion", "19.0.7"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
||||
@@ -203,6 +203,7 @@ using FullScreenTransitionState =
|
||||
// windowDidDeminiaturize
|
||||
level_ = [window level];
|
||||
shell_->SetWindowLevel(NSNormalWindowLevel);
|
||||
shell_->UpdateWindowOriginalFrame();
|
||||
}
|
||||
|
||||
- (void)windowDidMiniaturize:(NSNotification*)notification {
|
||||
@@ -248,6 +249,9 @@ using FullScreenTransitionState =
|
||||
|
||||
shell_->NotifyWindowEnterFullScreen();
|
||||
|
||||
if (shell_->HandleDeferredClose())
|
||||
return;
|
||||
|
||||
shell_->HandlePendingFullscreenTransitions();
|
||||
}
|
||||
|
||||
@@ -263,6 +267,9 @@ using FullScreenTransitionState =
|
||||
shell_->SetResizable(is_resizable_);
|
||||
shell_->NotifyWindowLeaveFullScreen();
|
||||
|
||||
if (shell_->HandleDeferredClose())
|
||||
return;
|
||||
|
||||
shell_->HandlePendingFullscreenTransitions();
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -3,4 +3,5 @@ void gtk_native_dialog_set_modal(GtkNativeDialog* self, gboolean modal);
|
||||
void gtk_native_dialog_show(GtkNativeDialog* self);
|
||||
void gtk_native_dialog_hide(GtkNativeDialog* self);
|
||||
gint gtk_native_dialog_run(GtkNativeDialog* self);
|
||||
void gtk_native_dialog_destroy(GtkNativeDialog* self);
|
||||
void gtk_native_dialog_destroy(GtkNativeDialog* self);
|
||||
GType gtk_native_dialog_get_type(void);
|
||||
|
||||
@@ -32,8 +32,11 @@ static const int kPreviewWidth = 256;
|
||||
static const int kPreviewHeight = 512;
|
||||
|
||||
std::string MakeCaseInsensitivePattern(const std::string& extension) {
|
||||
std::string pattern("*.");
|
||||
// If the extension is the "all files" extension, no change needed.
|
||||
if (extension == "*")
|
||||
return extension;
|
||||
|
||||
std::string pattern("*.");
|
||||
for (std::size_t i = 0, n = extension.size(); i < n; i++) {
|
||||
char ch = extension[i];
|
||||
if (!base::IsAsciiAlpha(ch)) {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "electron/electron_gtk_stubs.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
#include "third_party/skia/include/core/SkUnPreMultiply.h"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "electron/electron_gtk_stubs.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/native_window_observer.h"
|
||||
#include "shell/browser/native_window_views.h"
|
||||
|
||||
@@ -190,9 +190,9 @@
|
||||
gfx::ScreenPointFromNSPoint([event locationInWindow]),
|
||||
ui::EventFlagsFromModifiers([event modifierFlags]));
|
||||
|
||||
// Pass click to superclass to show menu. Custom mouseUp handler won't be
|
||||
// invoked.
|
||||
if (menuController_) {
|
||||
// Pass click to superclass to show menu if one exists and has a non-zero
|
||||
// number of items. Custom mouseUp handler won't be invoked in this case.
|
||||
if (menuController_ && [[menuController_ menu] numberOfItems] > 0) {
|
||||
[self handleClickNotifications:event];
|
||||
[super mouseDown:event];
|
||||
} else {
|
||||
|
||||
@@ -148,11 +148,30 @@ void WinCaptionButtonContainer::UpdateButtons() {
|
||||
restore_button_->SetVisible(is_maximized);
|
||||
maximize_button_->SetVisible(!is_maximized);
|
||||
|
||||
const bool minimizable = frame_view_->window()->IsMinimizable();
|
||||
minimize_button_->SetEnabled(minimizable);
|
||||
|
||||
// In touch mode, windows cannot be taken out of fullscreen or tiled mode, so
|
||||
// the maximize/restore button should be disabled.
|
||||
const bool is_touch = ui::TouchUiController::Get()->touch_ui();
|
||||
restore_button_->SetEnabled(!is_touch);
|
||||
maximize_button_->SetEnabled(!is_touch);
|
||||
|
||||
// The maximize button should only be enabled if the window is
|
||||
// maximizable *and* touch mode is disabled.
|
||||
const bool maximizable = frame_view_->window()->IsMaximizable();
|
||||
maximize_button_->SetEnabled(!is_touch && maximizable);
|
||||
|
||||
const bool closable = frame_view_->window()->IsClosable();
|
||||
close_button_->SetEnabled(closable);
|
||||
|
||||
// If all three of closable, maximizable, and minimizable are disabled,
|
||||
// Windows natively only shows the disabled closable button. Copy that
|
||||
// behavior here.
|
||||
if (!maximizable && !closable && !minimizable) {
|
||||
minimize_button_->SetVisible(false);
|
||||
maximize_button_->SetVisible(false);
|
||||
}
|
||||
|
||||
InvalidateLayout();
|
||||
}
|
||||
} // namespace electron
|
||||
|
||||
@@ -41,6 +41,11 @@ class WinCaptionButtonContainer : public views::View,
|
||||
gfx::Size GetButtonSize() const;
|
||||
void SetButtonSize(gfx::Size size);
|
||||
|
||||
// Sets caption button visibility and enabled state based on window state.
|
||||
// Only one of maximize or restore button should ever be visible at the same
|
||||
// time, and both are disabled in tablet UI mode.
|
||||
void UpdateButtons();
|
||||
|
||||
private:
|
||||
// views::View:
|
||||
void AddedToWidget() override;
|
||||
@@ -52,11 +57,6 @@ class WinCaptionButtonContainer : public views::View,
|
||||
|
||||
void ResetWindowControls();
|
||||
|
||||
// Sets caption button visibility and enabled state based on window state.
|
||||
// Only one of maximize or restore button should ever be visible at the same
|
||||
// time, and both are disabled in tablet UI mode.
|
||||
void UpdateButtons();
|
||||
|
||||
WinFrameView* const frame_view_;
|
||||
WinCaptionButton* const minimize_button_;
|
||||
WinCaptionButton* const maximize_button_;
|
||||
|
||||
@@ -55,8 +55,8 @@ SkColor WinFrameView::GetReadableFeatureColor(SkColor background_color) {
|
||||
}
|
||||
|
||||
void WinFrameView::InvalidateCaptionButtons() {
|
||||
// Ensure that the caption buttons container exists
|
||||
DCHECK(caption_button_container_);
|
||||
if (!caption_button_container_)
|
||||
return;
|
||||
|
||||
caption_button_container_->InvalidateLayout();
|
||||
caption_button_container_->SchedulePaint();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "shell/browser/native_window_views.h"
|
||||
#include "shell/browser/ui/views/frameless_view.h"
|
||||
#include "shell/browser/ui/views/win_caption_button.h"
|
||||
#include "shell/browser/ui/views/win_caption_button_container.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
@@ -43,6 +44,9 @@ class WinFrameView : public FramelessView {
|
||||
|
||||
NativeWindowViews* window() const { return window_; }
|
||||
views::Widget* frame() const { return frame_; }
|
||||
WinCaptionButtonContainer* caption_button_container() {
|
||||
return caption_button_container_;
|
||||
}
|
||||
|
||||
bool IsMaximized() const;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/bundle_locations.h"
|
||||
#include "base/mac/foundation_util.h"
|
||||
@@ -21,7 +22,8 @@
|
||||
namespace asar {
|
||||
|
||||
absl::optional<base::FilePath> Archive::RelativePath() const {
|
||||
base::FilePath bundle_path = base::mac::MainBundlePath().Append("Contents");
|
||||
base::FilePath bundle_path = base::MakeAbsoluteFilePath(
|
||||
base::mac::MainBundlePath().Append("Contents"));
|
||||
|
||||
base::FilePath relative_path;
|
||||
if (!bundle_path.AppendRelativePath(path_, &relative_path))
|
||||
|
||||
@@ -24,15 +24,6 @@
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsDevToolsExtension(content::RenderFrame* render_frame) {
|
||||
return static_cast<GURL>(render_frame->GetWebFrame()->GetDocument().Url())
|
||||
.SchemeIs("chrome-extension");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ElectronRendererClient::ElectronRendererClient()
|
||||
: node_bindings_(
|
||||
NodeBindings::Create(NodeBindings::BrowserEnvironment::kRenderer)),
|
||||
@@ -77,15 +68,7 @@ void ElectronRendererClient::DidCreateScriptContext(
|
||||
|
||||
// Only load Node.js if we are a main frame or a devtools extension
|
||||
// unless Node.js support has been explicitly enabled for subframes.
|
||||
auto prefs = render_frame->GetBlinkPreferences();
|
||||
bool is_main_frame = render_frame->IsMainFrame();
|
||||
bool is_devtools = IsDevToolsExtension(render_frame);
|
||||
bool allow_node_in_subframes = prefs.node_integration_in_sub_frames;
|
||||
|
||||
bool should_load_node =
|
||||
(is_main_frame || is_devtools || allow_node_in_subframes) &&
|
||||
!IsWebViewFrame(renderer_context, render_frame);
|
||||
if (!should_load_node)
|
||||
if (!ShouldLoadPreload(renderer_context, render_frame))
|
||||
return;
|
||||
|
||||
injected_frames_.insert(render_frame);
|
||||
|
||||
@@ -36,16 +36,6 @@ namespace {
|
||||
const char kLifecycleKey[] = "lifecycle";
|
||||
const char kModuleCacheKey[] = "native-module-cache";
|
||||
|
||||
bool IsDevTools(content::RenderFrame* render_frame) {
|
||||
return render_frame->GetWebFrame()->GetDocument().Url().ProtocolIs(
|
||||
"devtools");
|
||||
}
|
||||
|
||||
bool IsDevToolsExtension(content::RenderFrame* render_frame) {
|
||||
return render_frame->GetWebFrame()->GetDocument().Url().ProtocolIs(
|
||||
"chrome-extension");
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> GetModuleCache(v8::Isolate* isolate) {
|
||||
auto context = isolate->GetCurrentContext();
|
||||
gin_helper::Dictionary global(isolate, context->Global());
|
||||
@@ -207,17 +197,7 @@ void ElectronSandboxedRendererClient::DidCreateScriptContext(
|
||||
// Only allow preload for the main frame or
|
||||
// For devtools we still want to run the preload_bundle script
|
||||
// Or when nodeSupport is explicitly enabled in sub frames
|
||||
bool is_main_frame = render_frame->IsMainFrame();
|
||||
bool is_devtools =
|
||||
IsDevTools(render_frame) || IsDevToolsExtension(render_frame);
|
||||
|
||||
bool allow_node_in_sub_frames =
|
||||
render_frame->GetBlinkPreferences().node_integration_in_sub_frames;
|
||||
|
||||
bool should_load_preload =
|
||||
(is_main_frame || is_devtools || allow_node_in_sub_frames) &&
|
||||
!IsWebViewFrame(context, render_frame);
|
||||
if (!should_load_preload)
|
||||
if (!ShouldLoadPreload(context, render_frame))
|
||||
return;
|
||||
|
||||
injected_frames_.insert(render_frame);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "third_party/blink/public/web/web_local_frame.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
#include "chrome/common/pdf_util.h"
|
||||
#include "extensions/common/constants.h"
|
||||
#include "extensions/renderer/guest_view/mime_handler_view/post_message_support.h"
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
@@ -24,22 +25,11 @@ PrintRenderFrameHelperDelegate::~PrintRenderFrameHelperDelegate() = default;
|
||||
blink::WebElement PrintRenderFrameHelperDelegate::GetPdfElement(
|
||||
blink::WebLocalFrame* frame) {
|
||||
#if BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
GURL url = frame->GetDocument().Url();
|
||||
bool inside_pdf_extension =
|
||||
url.SchemeIs(extensions::kExtensionScheme) &&
|
||||
url.host_piece() == extension_misc::kPdfExtensionId;
|
||||
if (inside_pdf_extension) {
|
||||
// <object> with id="plugin" is created in
|
||||
// chrome/browser/resources/pdf/pdf_viewer_base.js.
|
||||
auto viewer_element = frame->GetDocument().GetElementById("viewer");
|
||||
if (!viewer_element.IsNull() && !viewer_element.ShadowRoot().IsNull()) {
|
||||
auto plugin_element =
|
||||
viewer_element.ShadowRoot().QuerySelector("#plugin");
|
||||
if (!plugin_element.IsNull()) {
|
||||
return plugin_element;
|
||||
}
|
||||
}
|
||||
NOTREACHED();
|
||||
if (frame->Parent() &&
|
||||
IsPdfInternalPluginAllowedOrigin(frame->Parent()->GetSecurityOrigin())) {
|
||||
auto plugin_element = frame->GetDocument().QuerySelector("embed");
|
||||
DCHECK(!plugin_element.IsNull());
|
||||
return plugin_element;
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
return blink::WebElement();
|
||||
|
||||
@@ -134,6 +134,16 @@ class ChromePdfInternalPluginDelegate final
|
||||
// static
|
||||
RendererClientBase* g_renderer_client_base = nullptr;
|
||||
|
||||
bool IsDevTools(content::RenderFrame* render_frame) {
|
||||
return render_frame->GetWebFrame()->GetDocument().Url().ProtocolIs(
|
||||
"devtools");
|
||||
}
|
||||
|
||||
bool IsDevToolsExtension(content::RenderFrame* render_frame) {
|
||||
return render_frame->GetWebFrame()->GetDocument().Url().ProtocolIs(
|
||||
"chrome-extension");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RendererClientBase::RendererClientBase() {
|
||||
@@ -196,6 +206,19 @@ void RendererClientBase::BindProcess(v8::Isolate* isolate,
|
||||
process->SetReadOnly("contextId", context_id);
|
||||
}
|
||||
|
||||
bool RendererClientBase::ShouldLoadPreload(
|
||||
v8::Handle<v8::Context> context,
|
||||
content::RenderFrame* render_frame) const {
|
||||
auto prefs = render_frame->GetBlinkPreferences();
|
||||
bool is_main_frame = render_frame->IsMainFrame();
|
||||
bool is_devtools =
|
||||
IsDevTools(render_frame) || IsDevToolsExtension(render_frame);
|
||||
bool allow_node_in_sub_frames = prefs.node_integration_in_sub_frames;
|
||||
|
||||
return (is_main_frame || is_devtools || allow_node_in_sub_frames) &&
|
||||
!IsWebViewFrame(context, render_frame);
|
||||
}
|
||||
|
||||
void RendererClientBase::RenderThreadStarted() {
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
|
||||
|
||||
@@ -93,6 +93,9 @@ class RendererClientBase : public content::ContentRendererClient
|
||||
gin_helper::Dictionary* process,
|
||||
content::RenderFrame* render_frame);
|
||||
|
||||
bool ShouldLoadPreload(v8::Handle<v8::Context> context,
|
||||
content::RenderFrame* render_frame) const;
|
||||
|
||||
// content::ContentRendererClient:
|
||||
void RenderThreadStarted() override;
|
||||
void ExposeInterfacesToBrowser(mojo::BinderMap* binders) override;
|
||||
|
||||
@@ -208,23 +208,18 @@ describe('app module', () => {
|
||||
interface SingleInstanceLockTestArgs {
|
||||
args: string[];
|
||||
expectedAdditionalData: unknown;
|
||||
expectedAck: unknown;
|
||||
}
|
||||
|
||||
it('prevents the second launch of app', async function () {
|
||||
this.timeout(60000);
|
||||
const appPath = path.join(fixturesPath, 'api', 'singleton');
|
||||
this.timeout(120000);
|
||||
const appPath = path.join(fixturesPath, 'api', 'singleton-data');
|
||||
const first = cp.spawn(process.execPath, [appPath]);
|
||||
const firstExited = emittedOnce(first, 'exit');
|
||||
await emittedOnce(first.stdout, 'data');
|
||||
|
||||
// Start second app when received output.
|
||||
const second = cp.spawn(process.execPath, [appPath]);
|
||||
const secondExited = emittedOnce(second, 'exit');
|
||||
|
||||
const [code2] = await secondExited;
|
||||
const [code2] = await emittedOnce(second, 'exit');
|
||||
expect(code2).to.equal(1);
|
||||
const [code1] = await firstExited;
|
||||
const [code1] = await emittedOnce(first, 'exit');
|
||||
expect(code1).to.equal(0);
|
||||
});
|
||||
|
||||
@@ -250,11 +245,6 @@ describe('app module', () => {
|
||||
const secondInstanceArgs = [process.execPath, appPath, ...testArgs.args, '--some-switch', 'some-arg'];
|
||||
const second = cp.spawn(secondInstanceArgs[0], secondInstanceArgs.slice(1));
|
||||
const secondExited = emittedOnce(second, 'exit');
|
||||
const secondStdoutLines = second.stdout.pipe(split());
|
||||
let ackData;
|
||||
while ((ackData = await emittedOnce(secondStdoutLines, 'data'))[0].toString().length === 0) {
|
||||
// This isn't valid data.
|
||||
}
|
||||
|
||||
const [code2] = await secondExited;
|
||||
expect(code2).to.equal(1);
|
||||
@@ -264,7 +254,6 @@ describe('app module', () => {
|
||||
const [args, additionalData] = dataFromSecondInstance[0].toString('ascii').split('||');
|
||||
const secondInstanceArgsReceived: string[] = JSON.parse(args.toString('ascii'));
|
||||
const secondInstanceDataReceived = JSON.parse(additionalData.toString('ascii'));
|
||||
const dataAckReceived = JSON.parse(ackData[0].toString('ascii'));
|
||||
|
||||
// Ensure secondInstanceArgs is a subset of secondInstanceArgsReceived
|
||||
for (const arg of secondInstanceArgs) {
|
||||
@@ -273,113 +262,69 @@ describe('app module', () => {
|
||||
}
|
||||
expect(secondInstanceDataReceived).to.be.deep.equal(testArgs.expectedAdditionalData,
|
||||
`received data ${JSON.stringify(secondInstanceDataReceived)} is not equal to expected data ${JSON.stringify(testArgs.expectedAdditionalData)}.`);
|
||||
expect(dataAckReceived).to.be.deep.equal(testArgs.expectedAck,
|
||||
`received data ${JSON.stringify(dataAckReceived)} is not equal to expected data ${JSON.stringify(testArgs.expectedAck)}.`);
|
||||
}
|
||||
|
||||
const expectedAdditionalData = {
|
||||
level: 1,
|
||||
testkey: 'testvalue1',
|
||||
inner: {
|
||||
level: 2,
|
||||
testkey: 'testvalue2'
|
||||
}
|
||||
};
|
||||
|
||||
const expectedAck = {
|
||||
level: 1,
|
||||
testkey: 'acktestvalue1',
|
||||
inner: {
|
||||
level: 2,
|
||||
testkey: 'acktestvalue2'
|
||||
}
|
||||
};
|
||||
|
||||
it('passes arguments to the second-instance event with no additional data', async () => {
|
||||
it('passes arguments to the second-instance event no additional data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: [],
|
||||
expectedAdditionalData: null,
|
||||
expectedAck: null
|
||||
expectedAdditionalData: null
|
||||
});
|
||||
});
|
||||
|
||||
it('passes arguments to the second-instance event', async () => {
|
||||
it('sends and receives JSON object data', async () => {
|
||||
const expectedAdditionalData = {
|
||||
level: 1,
|
||||
testkey: 'testvalue1',
|
||||
inner: {
|
||||
level: 2,
|
||||
testkey: 'testvalue2'
|
||||
}
|
||||
};
|
||||
await testArgumentPassing({
|
||||
args: ['--send-data'],
|
||||
expectedAdditionalData,
|
||||
expectedAck: null
|
||||
});
|
||||
});
|
||||
|
||||
it('gets back an ack after preventing default', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-ack', '--prevent-default'],
|
||||
expectedAdditionalData: null,
|
||||
expectedAck
|
||||
});
|
||||
});
|
||||
|
||||
it('is able to send back empty ack after preventing default', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--prevent-default'],
|
||||
expectedAdditionalData: null,
|
||||
expectedAck: null
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-ack', '--prevent-default', '--send-data'],
|
||||
expectedAdditionalData,
|
||||
expectedAck
|
||||
expectedAdditionalData
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives numerical data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-ack', '--ack-content=1', '--prevent-default', '--send-data', '--data-content=2'],
|
||||
expectedAdditionalData: 2,
|
||||
expectedAck: 1
|
||||
args: ['--send-data', '--data-content=2'],
|
||||
expectedAdditionalData: 2
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives string data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-ack', '--ack-content="ack"', '--prevent-default', '--send-data', '--data-content="data"'],
|
||||
expectedAdditionalData: 'data',
|
||||
expectedAck: 'ack'
|
||||
args: ['--send-data', '--data-content="data"'],
|
||||
expectedAdditionalData: 'data'
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives boolean data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-ack', '--ack-content=true', '--prevent-default', '--send-data', '--data-content=false'],
|
||||
expectedAdditionalData: false,
|
||||
expectedAck: true
|
||||
args: ['--send-data', '--data-content=false'],
|
||||
expectedAdditionalData: false
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives array data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-ack', '--ack-content=[1, 2, 3]', '--prevent-default', '--send-data', '--data-content=[2, 3, 4]'],
|
||||
expectedAdditionalData: [2, 3, 4],
|
||||
expectedAck: [1, 2, 3]
|
||||
args: ['--send-data', '--data-content=[2, 3, 4]'],
|
||||
expectedAdditionalData: [2, 3, 4]
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives mixed array data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-ack', '--ack-content=["1", true, 3]', '--prevent-default', '--send-data', '--data-content=["2", false, 4]'],
|
||||
expectedAdditionalData: ['2', false, 4],
|
||||
expectedAck: ['1', true, 3]
|
||||
args: ['--send-data', '--data-content=["2", true, 4]'],
|
||||
expectedAdditionalData: ['2', true, 4]
|
||||
});
|
||||
});
|
||||
|
||||
it('sends and receives null data', async () => {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-ack', '--ack-content=null', '--prevent-default', '--send-data', '--data-content=null'],
|
||||
expectedAdditionalData: null,
|
||||
expectedAck: null
|
||||
args: ['--send-data', '--data-content=null'],
|
||||
expectedAdditionalData: null
|
||||
});
|
||||
});
|
||||
|
||||
@@ -387,8 +332,7 @@ describe('app module', () => {
|
||||
try {
|
||||
await testArgumentPassing({
|
||||
args: ['--send-ack', '--ack-content="undefined"', '--prevent-default', '--send-data', '--data-content="undefined"'],
|
||||
expectedAdditionalData: undefined,
|
||||
expectedAck: undefined
|
||||
expectedAdditionalData: undefined
|
||||
});
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
|
||||
@@ -2,10 +2,8 @@ import { expect } from 'chai';
|
||||
import * as childProcess from 'child_process';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as qs from 'querystring';
|
||||
import * as http from 'http';
|
||||
import * as semver from 'semver';
|
||||
import { AddressInfo } from 'net';
|
||||
import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents, BrowserWindowConstructorOptions } from 'electron/main';
|
||||
|
||||
@@ -1230,6 +1228,7 @@ describe('BrowserWindow module', () => {
|
||||
await resize;
|
||||
expectBoundsEqual(w.getNormalBounds(), w.getBounds());
|
||||
});
|
||||
|
||||
it('checks normal bounds after move', async () => {
|
||||
const pos = [10, 10];
|
||||
const move = emittedOnce(w, 'move');
|
||||
@@ -1248,6 +1247,51 @@ describe('BrowserWindow module', () => {
|
||||
await maximize;
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('updates normal bounds after resize and maximize', async () => {
|
||||
const size = [300, 400];
|
||||
const resize = emittedOnce(w, 'resize');
|
||||
w.setSize(size[0], size[1]);
|
||||
await resize;
|
||||
const original = w.getBounds();
|
||||
|
||||
const maximize = emittedOnce(w, 'maximize');
|
||||
w.maximize();
|
||||
await maximize;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('updates normal bounds after move and maximize', async () => {
|
||||
const pos = [10, 10];
|
||||
const move = emittedOnce(w, 'move');
|
||||
w.setPosition(pos[0], pos[1]);
|
||||
await move;
|
||||
const original = w.getBounds();
|
||||
|
||||
const maximize = emittedOnce(w, 'maximize');
|
||||
w.maximize();
|
||||
await maximize;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('checks normal bounds when unmaximized', async () => {
|
||||
const bounds = w.getBounds();
|
||||
w.once('maximize', () => {
|
||||
@@ -1259,6 +1303,7 @@ describe('BrowserWindow module', () => {
|
||||
await unmaximize;
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('does not change size for a frameless window with min size', async () => {
|
||||
w.destroy();
|
||||
w = new BrowserWindow({
|
||||
@@ -1279,6 +1324,7 @@ describe('BrowserWindow module', () => {
|
||||
await unmaximize;
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('correctly checks transparent window maximization state', async () => {
|
||||
w.destroy();
|
||||
w = new BrowserWindow({
|
||||
@@ -1298,6 +1344,7 @@ describe('BrowserWindow module', () => {
|
||||
await unmaximize;
|
||||
expect(w.isMaximized()).to.equal(false);
|
||||
});
|
||||
|
||||
it('returns the correct value for windows with an aspect ratio', async () => {
|
||||
w.destroy();
|
||||
w = new BrowserWindow({
|
||||
@@ -1327,6 +1374,41 @@ describe('BrowserWindow module', () => {
|
||||
await minimize;
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('updates normal bounds after move and minimize', async () => {
|
||||
const pos = [10, 10];
|
||||
const move = emittedOnce(w, 'move');
|
||||
w.setPosition(pos[0], pos[1]);
|
||||
await move;
|
||||
const original = w.getBounds();
|
||||
|
||||
const minimize = emittedOnce(w, 'minimize');
|
||||
w.minimize();
|
||||
await minimize;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
|
||||
expect(original).to.deep.equal(normal);
|
||||
expectBoundsEqual(normal, w.getBounds());
|
||||
});
|
||||
|
||||
it('updates normal bounds after resize and minimize', async () => {
|
||||
const size = [300, 400];
|
||||
const resize = emittedOnce(w, 'resize');
|
||||
w.setSize(size[0], size[1]);
|
||||
await resize;
|
||||
const original = w.getBounds();
|
||||
|
||||
const minimize = emittedOnce(w, 'minimize');
|
||||
w.minimize();
|
||||
await minimize;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
|
||||
expect(original).to.deep.equal(normal);
|
||||
expectBoundsEqual(normal, w.getBounds());
|
||||
});
|
||||
|
||||
it('checks normal bounds when restored', async () => {
|
||||
const bounds = w.getBounds();
|
||||
w.once('minimize', () => {
|
||||
@@ -1338,6 +1420,7 @@ describe('BrowserWindow module', () => {
|
||||
await restore;
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('does not change size for a frameless window with min size', async () => {
|
||||
w.destroy();
|
||||
w = new BrowserWindow({
|
||||
@@ -1383,6 +1466,50 @@ describe('BrowserWindow module', () => {
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('updates normal bounds after resize and fullscreen', async () => {
|
||||
const size = [300, 400];
|
||||
const resize = emittedOnce(w, 'resize');
|
||||
w.setSize(size[0], size[1]);
|
||||
await resize;
|
||||
const original = w.getBounds();
|
||||
|
||||
const fsc = emittedOnce(w, 'enter-full-screen');
|
||||
w.fullScreen = true;
|
||||
await fsc;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('updates normal bounds after move and fullscreen', async () => {
|
||||
const pos = [10, 10];
|
||||
const move = emittedOnce(w, 'move');
|
||||
w.setPosition(pos[0], pos[1]);
|
||||
await move;
|
||||
const original = w.getBounds();
|
||||
|
||||
const fsc = emittedOnce(w, 'enter-full-screen');
|
||||
w.fullScreen = true;
|
||||
await fsc;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('checks normal bounds when unfullscreen\'ed', async () => {
|
||||
const bounds = w.getBounds();
|
||||
w.once('enter-full-screen', () => {
|
||||
@@ -1420,6 +1547,50 @@ describe('BrowserWindow module', () => {
|
||||
expectBoundsEqual(w.getNormalBounds(), bounds);
|
||||
});
|
||||
|
||||
it('updates normal bounds after resize and fullscreen', async () => {
|
||||
const size = [300, 400];
|
||||
const resize = emittedOnce(w, 'resize');
|
||||
w.setSize(size[0], size[1]);
|
||||
await resize;
|
||||
const original = w.getBounds();
|
||||
|
||||
const fsc = emittedOnce(w, 'enter-full-screen');
|
||||
w.setFullScreen(true);
|
||||
await fsc;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('updates normal bounds after move and fullscreen', async () => {
|
||||
const pos = [10, 10];
|
||||
const move = emittedOnce(w, 'move');
|
||||
w.setPosition(pos[0], pos[1]);
|
||||
await move;
|
||||
const original = w.getBounds();
|
||||
|
||||
const fsc = emittedOnce(w, 'enter-full-screen');
|
||||
w.setFullScreen(true);
|
||||
await fsc;
|
||||
|
||||
const normal = w.getNormalBounds();
|
||||
const bounds = w.getBounds();
|
||||
|
||||
expect(normal).to.deep.equal(original);
|
||||
expect(normal).to.not.deep.equal(bounds);
|
||||
|
||||
const close = emittedOnce(w, 'close');
|
||||
w.close();
|
||||
await close;
|
||||
});
|
||||
|
||||
it('checks normal bounds when unfullscreen\'ed', async () => {
|
||||
const bounds = w.getBounds();
|
||||
w.show();
|
||||
@@ -1825,6 +1996,42 @@ describe('BrowserWindow module', () => {
|
||||
w.setWindowButtonVisibility(false);
|
||||
expect(w._getWindowButtonVisibility()).to.equal(false);
|
||||
});
|
||||
|
||||
it('correctly updates when entering/exiting fullscreen for hidden style', async () => {
|
||||
const w = new BrowserWindow({ show: false, frame: false, titleBarStyle: 'hidden' });
|
||||
expect(w._getWindowButtonVisibility()).to.equal(true);
|
||||
w.setWindowButtonVisibility(false);
|
||||
expect(w._getWindowButtonVisibility()).to.equal(false);
|
||||
|
||||
const enterFS = emittedOnce(w, 'enter-full-screen');
|
||||
w.setFullScreen(true);
|
||||
await enterFS;
|
||||
|
||||
const leaveFS = emittedOnce(w, 'leave-full-screen');
|
||||
w.setFullScreen(false);
|
||||
await leaveFS;
|
||||
|
||||
w.setWindowButtonVisibility(true);
|
||||
expect(w._getWindowButtonVisibility()).to.equal(true);
|
||||
});
|
||||
|
||||
it('correctly updates when entering/exiting fullscreen for hiddenInset style', async () => {
|
||||
const w = new BrowserWindow({ show: false, frame: false, titleBarStyle: 'hiddenInset' });
|
||||
expect(w._getWindowButtonVisibility()).to.equal(true);
|
||||
w.setWindowButtonVisibility(false);
|
||||
expect(w._getWindowButtonVisibility()).to.equal(false);
|
||||
|
||||
const enterFS = emittedOnce(w, 'enter-full-screen');
|
||||
w.setFullScreen(true);
|
||||
await enterFS;
|
||||
|
||||
const leaveFS = emittedOnce(w, 'leave-full-screen');
|
||||
w.setFullScreen(false);
|
||||
await leaveFS;
|
||||
|
||||
w.setWindowButtonVisibility(true);
|
||||
expect(w._getWindowButtonVisibility()).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'darwin')('BrowserWindow.setVibrancy(type)', () => {
|
||||
@@ -1937,6 +2144,26 @@ describe('BrowserWindow module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Opening a BrowserWindow from a link', () => {
|
||||
let appProcess: childProcess.ChildProcessWithoutNullStreams | undefined;
|
||||
|
||||
afterEach(() => {
|
||||
if (appProcess && !appProcess.killed) {
|
||||
appProcess.kill();
|
||||
appProcess = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
it('can properly open and load a new window from a link', async () => {
|
||||
const appPath = path.join(__dirname, 'fixtures', 'apps', 'open-new-window-from-link');
|
||||
|
||||
appProcess = childProcess.spawn(process.execPath, [appPath]);
|
||||
|
||||
const [code] = await emittedOnce(appProcess, 'exit');
|
||||
expect(code).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserWindow.fromWebContents(webContents)', () => {
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
@@ -2128,7 +2355,7 @@ describe('BrowserWindow module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'win32' || (process.platform === 'darwin' && semver.gte(os.release(), '14.0.0')))('"titleBarStyle" option', () => {
|
||||
ifdescribe(['win32', 'darwin'].includes(process.platform))('"titleBarStyle" option', () => {
|
||||
const testWindowsOverlay = async (style: any) => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
@@ -2167,8 +2394,10 @@ describe('BrowserWindow module', () => {
|
||||
const [, newOverlayRect] = await geometryChange;
|
||||
expect(newOverlayRect.width).to.equal(overlayRect.width + 400);
|
||||
};
|
||||
afterEach(closeAllWindows);
|
||||
afterEach(() => { ipcMain.removeAllListeners('geometrychange'); });
|
||||
afterEach(async () => {
|
||||
await closeAllWindows();
|
||||
ipcMain.removeAllListeners('geometrychange');
|
||||
});
|
||||
it('creates browser window with hidden title bar', () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
@@ -2197,7 +2426,7 @@ describe('BrowserWindow module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'win32' || (process.platform === 'darwin' && semver.gte(os.release(), '14.0.0')))('"titleBarOverlay" option', () => {
|
||||
ifdescribe(['win32', 'darwin'].includes(process.platform))('"titleBarOverlay" option', () => {
|
||||
const testWindowsOverlayHeight = async (size: any) => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
@@ -2223,9 +2452,15 @@ describe('BrowserWindow module', () => {
|
||||
const overlayEnabled = await w.webContents.executeJavaScript('navigator.windowControlsOverlay.visible');
|
||||
expect(overlayEnabled).to.be.true('overlayEnabled');
|
||||
const overlayRectPreMax = await w.webContents.executeJavaScript('getJSOverlayProperties()');
|
||||
await w.maximize();
|
||||
const max = await w.isMaximized();
|
||||
expect(max).to.equal(true);
|
||||
|
||||
if (!w.isMaximized()) {
|
||||
const maximize = emittedOnce(w, 'maximize');
|
||||
w.show();
|
||||
w.maximize();
|
||||
await maximize;
|
||||
}
|
||||
|
||||
expect(w.isMaximized()).to.be.true('not maximized');
|
||||
const overlayRectPostMax = await w.webContents.executeJavaScript('getJSOverlayProperties()');
|
||||
|
||||
expect(overlayRectPreMax.y).to.equal(0);
|
||||
@@ -2240,13 +2475,96 @@ describe('BrowserWindow module', () => {
|
||||
// Confirm that maximization only affected the height of the buttons and not the title bar
|
||||
expect(overlayRectPostMax.height).to.equal(size);
|
||||
};
|
||||
afterEach(closeAllWindows);
|
||||
afterEach(() => { ipcMain.removeAllListeners('geometrychange'); });
|
||||
afterEach(async () => {
|
||||
await closeAllWindows();
|
||||
ipcMain.removeAllListeners('geometrychange');
|
||||
});
|
||||
it('sets Window Control Overlay with title bar height of 40', async () => {
|
||||
await testWindowsOverlayHeight(40);
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'win32')('BrowserWindow.setTitlebarOverlay', () => {
|
||||
afterEach(async () => {
|
||||
await closeAllWindows();
|
||||
ipcMain.removeAllListeners('geometrychange');
|
||||
});
|
||||
|
||||
it('does not crash when an invalid titleBarStyle was initially set', () => {
|
||||
const win = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false
|
||||
},
|
||||
titleBarOverlay: {
|
||||
color: '#0000f0',
|
||||
symbolColor: '#ffffff'
|
||||
},
|
||||
titleBarStyle: 'hiddenInset'
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
win.setTitleBarOverlay({
|
||||
color: '#000000'
|
||||
});
|
||||
}).to.not.throw();
|
||||
});
|
||||
|
||||
it('correctly updates the height of the overlay', async () => {
|
||||
const testOverlay = async (w: BrowserWindow, size: Number, firstRun: boolean) => {
|
||||
const overlayHTML = path.join(__dirname, 'fixtures', 'pages', 'overlay.html');
|
||||
const overlayReady = emittedOnce(ipcMain, 'geometrychange');
|
||||
await w.loadFile(overlayHTML);
|
||||
if (firstRun) {
|
||||
await overlayReady;
|
||||
}
|
||||
|
||||
const overlayEnabled = await w.webContents.executeJavaScript('navigator.windowControlsOverlay.visible');
|
||||
expect(overlayEnabled).to.be.true('overlayEnabled');
|
||||
const { height: preMaxHeight } = await w.webContents.executeJavaScript('getJSOverlayProperties()');
|
||||
|
||||
if (!w.isMaximized()) {
|
||||
const maximize = emittedOnce(w, 'maximize');
|
||||
w.show();
|
||||
w.maximize();
|
||||
await maximize;
|
||||
}
|
||||
|
||||
expect(w.isMaximized()).to.be.true('not maximized');
|
||||
const { x, y, width, height } = await w.webContents.executeJavaScript('getJSOverlayProperties()');
|
||||
expect(x).to.equal(0);
|
||||
expect(y).to.equal(0);
|
||||
expect(width).to.be.greaterThan(0);
|
||||
expect(height).to.equal(size);
|
||||
expect(preMaxHeight).to.equal(size);
|
||||
};
|
||||
|
||||
const INITIAL_SIZE = 40;
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
width: 400,
|
||||
height: 400,
|
||||
titleBarStyle: 'hidden',
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false
|
||||
},
|
||||
titleBarOverlay: {
|
||||
height: INITIAL_SIZE
|
||||
}
|
||||
});
|
||||
|
||||
await testOverlay(w, INITIAL_SIZE, true);
|
||||
|
||||
w.setTitleBarOverlay({
|
||||
height: INITIAL_SIZE + 10
|
||||
});
|
||||
|
||||
await testOverlay(w, INITIAL_SIZE + 10, false);
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'darwin')('"enableLargerThanScreen" option', () => {
|
||||
afterEach(closeAllWindows);
|
||||
it('can move the window out of screen', () => {
|
||||
@@ -3516,9 +3834,14 @@ describe('BrowserWindow module', () => {
|
||||
describe('beginFrameSubscription method', () => {
|
||||
it('does not crash when callback returns nothing', (done) => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
let called = false;
|
||||
w.loadFile(path.join(fixtures, 'api', 'frame-subscriber.html'));
|
||||
w.webContents.on('dom-ready', () => {
|
||||
w.webContents.beginFrameSubscription(function () {
|
||||
// This callback might be called twice.
|
||||
if (called) return;
|
||||
called = true;
|
||||
|
||||
// Pending endFrameSubscription to next tick can reliably reproduce
|
||||
// a crash which happens when nothing is returned in the callback.
|
||||
setTimeout(() => {
|
||||
@@ -3703,22 +4026,24 @@ describe('BrowserWindow module', () => {
|
||||
// TODO(dsanders11): Enable once maximize event works on Linux again on CI
|
||||
ifdescribe(process.platform !== 'linux')('BrowserWindow.maximize()', () => {
|
||||
afterEach(closeAllWindows);
|
||||
// TODO(dsanders11): Disabled on macOS, see https://github.com/electron/electron/issues/32947
|
||||
ifit(process.platform !== 'darwin')('should show the window if it is not currently shown', async () => {
|
||||
it('should show the window if it is not currently shown', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
const hidden = emittedOnce(w, 'hide');
|
||||
const shown = emittedOnce(w, 'show');
|
||||
let shown = emittedOnce(w, 'show');
|
||||
const maximize = emittedOnce(w, 'maximize');
|
||||
expect(w.isVisible()).to.be.false('visible');
|
||||
w.maximize();
|
||||
await maximize;
|
||||
await shown;
|
||||
expect(w.isMaximized()).to.be.true('maximized');
|
||||
expect(w.isVisible()).to.be.true('visible');
|
||||
// Even if the window is already maximized
|
||||
w.hide();
|
||||
await hidden;
|
||||
expect(w.isVisible()).to.be.false('visible');
|
||||
shown = emittedOnce(w, 'show');
|
||||
w.maximize();
|
||||
await shown; // Ensure a 'show' event happens when it becomes visible
|
||||
await shown;
|
||||
expect(w.isVisible()).to.be.true('visible');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -764,10 +764,7 @@ describe('protocol module', () => {
|
||||
await expect(contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.notjs', {scope: './'})`)).to.be.rejected();
|
||||
});
|
||||
|
||||
// TODO(nornagon): I'm not sure why this isn't working, but I'm choosing to
|
||||
// disable this test for now to land the roll. See
|
||||
// https://github.com/electron/electron/issues/32664.
|
||||
it.skip('should be able to register service worker for custom scheme', async () => {
|
||||
it('should be able to register service worker for custom scheme', async () => {
|
||||
await contents.loadURL(`${serviceWorkerScheme}://${v4()}.com`);
|
||||
await contents.executeJavaScript(`navigator.serviceWorker.register('${v4()}.js', {scope: './'})`);
|
||||
});
|
||||
|
||||
@@ -12,8 +12,27 @@ import * as fs from 'fs';
|
||||
*
|
||||
* Because all encryption methods are gated by isEncryptionAvailable, the methods will never return the correct values
|
||||
* when run on CI and linux.
|
||||
* Refs: https://github.com/electron/electron/issues/30424.
|
||||
*/
|
||||
|
||||
describe('safeStorage module', () => {
|
||||
it('safeStorage before and after app is ready', async () => {
|
||||
const appPath = path.join(__dirname, 'fixtures', 'crash-cases', 'safe-storage');
|
||||
const appProcess = cp.spawn(process.execPath, [appPath]);
|
||||
|
||||
let output = '';
|
||||
appProcess.stdout.on('data', data => { output += data; });
|
||||
appProcess.stderr.on('data', data => { output += data; });
|
||||
|
||||
const code = (await emittedOnce(appProcess, 'exit'))[0] ?? 1;
|
||||
|
||||
if (code !== 0 && output) {
|
||||
console.log(output);
|
||||
}
|
||||
expect(code).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform !== 'linux')('safeStorage module', () => {
|
||||
after(async () => {
|
||||
const pathToEncryptedString = path.resolve(__dirname, 'fixtures', 'api', 'safe-storage', 'encrypted.txt');
|
||||
|
||||
@@ -224,6 +224,37 @@ describe('webFrameMain module', () => {
|
||||
expect(w.webContents.mainFrame).to.equal(mainFrame);
|
||||
expect(mainFrame.url).to.equal(crossOriginUrl);
|
||||
});
|
||||
|
||||
it('recovers from renderer crash on same-origin', async () => {
|
||||
const server = await createServer();
|
||||
// Keep reference to mainFrame alive throughout crash and recovery.
|
||||
const { mainFrame } = w.webContents;
|
||||
await w.webContents.loadURL(server.url);
|
||||
const crashEvent = emittedOnce(w.webContents, 'render-process-gone');
|
||||
w.webContents.forcefullyCrashRenderer();
|
||||
await crashEvent;
|
||||
await w.webContents.loadURL(server.url);
|
||||
// Log just to keep mainFrame in scope.
|
||||
console.log('mainFrame.url', mainFrame.url);
|
||||
});
|
||||
|
||||
// Fixed by #34411
|
||||
it('recovers from renderer crash on cross-origin', async () => {
|
||||
const server = await createServer();
|
||||
// 'localhost' is treated as a separate origin.
|
||||
const crossOriginUrl = server.url.replace('127.0.0.1', 'localhost');
|
||||
// Keep reference to mainFrame alive throughout crash and recovery.
|
||||
const { mainFrame } = w.webContents;
|
||||
await w.webContents.loadURL(server.url);
|
||||
const crashEvent = emittedOnce(w.webContents, 'render-process-gone');
|
||||
w.webContents.forcefullyCrashRenderer();
|
||||
await crashEvent;
|
||||
// A short wait seems to be required to reproduce the crash.
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
await w.webContents.loadURL(crossOriginUrl);
|
||||
// Log just to keep mainFrame in scope.
|
||||
console.log('mainFrame.url', mainFrame.url);
|
||||
});
|
||||
});
|
||||
|
||||
describe('webFrameMain.fromId', () => {
|
||||
|
||||
@@ -1509,6 +1509,68 @@ describe('chromium features', () => {
|
||||
expect(focus).to.be.false();
|
||||
});
|
||||
});
|
||||
|
||||
describe('navigator.userAgentData', () => {
|
||||
// These tests are done on an http server because navigator.userAgentData
|
||||
// requires a secure context.
|
||||
let server: http.Server;
|
||||
let serverUrl: string;
|
||||
before(async () => {
|
||||
server = http.createServer((req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end('');
|
||||
});
|
||||
await new Promise<void>(resolve => server.listen(0, '127.0.0.1', resolve));
|
||||
serverUrl = `http://localhost:${(server.address() as any).port}`;
|
||||
});
|
||||
after(() => {
|
||||
server.close();
|
||||
});
|
||||
|
||||
describe('is not empty', () => {
|
||||
it('by default', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL(serverUrl);
|
||||
const platform = await w.webContents.executeJavaScript('navigator.userAgentData.platform');
|
||||
expect(platform).not.to.be.empty();
|
||||
});
|
||||
|
||||
it('when there is a session-wide UA override', async () => {
|
||||
const ses = session.fromPartition(`${Math.random()}`);
|
||||
ses.setUserAgent('foobar');
|
||||
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
|
||||
await w.loadURL(serverUrl);
|
||||
const platform = await w.webContents.executeJavaScript('navigator.userAgentData.platform');
|
||||
expect(platform).not.to.be.empty();
|
||||
});
|
||||
|
||||
it('when there is a WebContents-specific UA override', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
w.webContents.setUserAgent('foo');
|
||||
await w.loadURL(serverUrl);
|
||||
const platform = await w.webContents.executeJavaScript('navigator.userAgentData.platform');
|
||||
expect(platform).not.to.be.empty();
|
||||
});
|
||||
|
||||
it('when there is a WebContents-specific UA override at load time', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL(serverUrl, {
|
||||
userAgent: 'foo'
|
||||
});
|
||||
const platform = await w.webContents.executeJavaScript('navigator.userAgentData.platform');
|
||||
expect(platform).not.to.be.empty();
|
||||
});
|
||||
});
|
||||
|
||||
describe('brand list', () => {
|
||||
it('contains chromium', async () => {
|
||||
const w = new BrowserWindow({ show: false });
|
||||
await w.loadURL(serverUrl);
|
||||
const brands = await w.webContents.executeJavaScript('navigator.userAgentData.brands');
|
||||
expect(brands.map((b: any) => b.brand)).to.include('Chromium');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('font fallback', () => {
|
||||
@@ -1679,11 +1741,39 @@ describe('navigator.serial', () => {
|
||||
});
|
||||
|
||||
it('returns a port when select-serial-port event is defined', async () => {
|
||||
let havePorts = false;
|
||||
w.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
|
||||
callback(portList[0].portId);
|
||||
if (portList.length > 0) {
|
||||
havePorts = true;
|
||||
callback(portList[0].portId);
|
||||
} else {
|
||||
callback('');
|
||||
}
|
||||
});
|
||||
const port = await getPorts();
|
||||
expect(port).to.equal('[object SerialPort]');
|
||||
if (havePorts) {
|
||||
expect(port).to.equal('[object SerialPort]');
|
||||
} else {
|
||||
expect(port).to.equal('NotFoundError: No port selected by the user.');
|
||||
}
|
||||
});
|
||||
|
||||
it('navigator.serial.getPorts() returns values', async () => {
|
||||
let havePorts = false;
|
||||
|
||||
w.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
|
||||
if (portList.length > 0) {
|
||||
havePorts = true;
|
||||
callback(portList[0].portId);
|
||||
} else {
|
||||
callback('');
|
||||
}
|
||||
});
|
||||
await getPorts();
|
||||
if (havePorts) {
|
||||
const grantedPorts = await w.webContents.executeJavaScript('navigator.serial.getPorts()');
|
||||
expect(grantedPorts).to.not.be.empty();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2006,7 +2096,6 @@ describe('navigator.hid', () => {
|
||||
if (details.deviceList.length > 0) {
|
||||
details.deviceList.find((device) => {
|
||||
if (device.name && device.name !== '' && device.serialNumber && device.serialNumber !== '') {
|
||||
console.log('device is: ', device);
|
||||
if (checkForExcludedDevice) {
|
||||
const compareDevice = {
|
||||
vendorId: device.vendorId,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { expect } from 'chai';
|
||||
import * as cp from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { ifit } from './spec-helpers';
|
||||
|
||||
const fixturePath = path.resolve(__dirname, 'fixtures', 'crash-cases');
|
||||
|
||||
@@ -41,7 +42,8 @@ describe('crash cases', () => {
|
||||
const cases = fs.readdirSync(fixturePath);
|
||||
|
||||
for (const crashCase of cases) {
|
||||
it(`the "${crashCase}" case should not crash`, () => {
|
||||
// TODO(jkleinsc) fix this flaky test on Windows 32-bit
|
||||
ifit(process.platform !== 'win32' || process.arch !== 'ia32' || crashCase !== 'quit-on-crashed-event')(`the "${crashCase}" case should not crash`, () => {
|
||||
const fixture = path.resolve(fixturePath, crashCase);
|
||||
const argsFile = path.resolve(fixture, 'electron.args');
|
||||
const args = [fixture];
|
||||
|
||||
@@ -110,6 +110,12 @@ describe('chrome extensions', () => {
|
||||
expect(bg).to.equal('red');
|
||||
});
|
||||
|
||||
it('does not crash when loading an extension with missing manifest', async () => {
|
||||
const customSession = session.fromPartition(`persist:${uuid.v4()}`);
|
||||
const promise = customSession.loadExtension(path.join(fixtures, 'extensions', 'missing-manifest'));
|
||||
await expect(promise).to.eventually.be.rejectedWith(/Manifest file is missing or unreadable/);
|
||||
});
|
||||
|
||||
it('does not crash when failing to load an extension', async () => {
|
||||
const customSession = session.fromPartition(`persist:${uuid.v4()}`);
|
||||
const promise = customSession.loadExtension(path.join(fixtures, 'extensions', 'load-error'));
|
||||
|
||||
11
spec-main/fixtures/apps/open-new-window-from-link/index.html
Normal file
11
spec-main/fixtures/apps/open-new-window-from-link/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
<a href="./new-window-page.html">Open New Window</a>
|
||||
</body>
|
||||
</html>
|
||||
64
spec-main/fixtures/apps/open-new-window-from-link/main.js
Normal file
64
spec-main/fixtures/apps/open-new-window-from-link/main.js
Normal file
@@ -0,0 +1,64 @@
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
const path = require('path');
|
||||
|
||||
async function createWindow () {
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
x: 100,
|
||||
y: 100,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
contextIsolation: false,
|
||||
nodeIntegration: true
|
||||
}
|
||||
});
|
||||
|
||||
await mainWindow.loadFile('index.html');
|
||||
|
||||
const rect = await mainWindow.webContents.executeJavaScript('JSON.parse(JSON.stringify(document.querySelector("a").getBoundingClientRect()))');
|
||||
const x = rect.x + rect.width / 2;
|
||||
const y = rect.y + rect.height / 2;
|
||||
|
||||
function click (x, y, options) {
|
||||
x = Math.floor(x);
|
||||
y = Math.floor(y);
|
||||
mainWindow.webContents.sendInputEvent({
|
||||
type: 'mouseDown',
|
||||
button: 'left',
|
||||
x,
|
||||
y,
|
||||
clickCount: 1,
|
||||
...options
|
||||
});
|
||||
|
||||
mainWindow.webContents.sendInputEvent({
|
||||
type: 'mouseUp',
|
||||
button: 'left',
|
||||
x,
|
||||
y,
|
||||
clickCount: 1,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
click(x, y, { modifiers: ['shift'] });
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
app.on('web-contents-created', (e, wc) => {
|
||||
wc.on('render-process-gone', (e, details) => {
|
||||
console.error(details);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
wc.on('did-finish-load', () => {
|
||||
const title = wc.getTitle();
|
||||
if (title === 'Window From Link') {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
createWindow();
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
|
||||
<title>Window From Link</title>
|
||||
</head>
|
||||
<body>
|
||||
I'm a window opened from a link!
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "electron-test-open-new-window-from-link",
|
||||
"main": "main.js"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
window.addEventListener('click', e => {
|
||||
console.log('click', e);
|
||||
});
|
||||
39
spec-main/fixtures/crash-cases/safe-storage/index.js
Normal file
39
spec-main/fixtures/crash-cases/safe-storage/index.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const { app, safeStorage } = require('electron');
|
||||
const { expect } = require('chai');
|
||||
|
||||
(async () => {
|
||||
if (!app.isReady()) {
|
||||
// isEncryptionAvailable() returns false before the app is ready on
|
||||
// Linux: https://github.com/electron/electron/issues/32206
|
||||
// and
|
||||
// Windows: https://github.com/electron/electron/issues/33640.
|
||||
expect(safeStorage.isEncryptionAvailable()).to.equal(process.platform === 'darwin');
|
||||
if (safeStorage.isEncryptionAvailable()) {
|
||||
const plaintext = 'plaintext';
|
||||
const ciphertext = safeStorage.encryptString(plaintext);
|
||||
expect(Buffer.isBuffer(ciphertext)).to.equal(true);
|
||||
expect(safeStorage.decryptString(ciphertext)).to.equal(plaintext);
|
||||
} else {
|
||||
expect(() => safeStorage.encryptString('plaintext')).to.throw(/safeStorage cannot be used before app is ready/);
|
||||
expect(() => safeStorage.decryptString(Buffer.from(''))).to.throw(/safeStorage cannot be used before app is ready/);
|
||||
}
|
||||
}
|
||||
await app.whenReady();
|
||||
// isEncryptionAvailable() will always return false on CI due to a mocked
|
||||
// dbus as mentioned above.
|
||||
expect(safeStorage.isEncryptionAvailable()).to.equal(process.platform !== 'linux');
|
||||
if (safeStorage.isEncryptionAvailable()) {
|
||||
const plaintext = 'plaintext';
|
||||
const ciphertext = safeStorage.encryptString(plaintext);
|
||||
expect(Buffer.isBuffer(ciphertext)).to.equal(true);
|
||||
expect(safeStorage.decryptString(ciphertext)).to.equal(plaintext);
|
||||
} else {
|
||||
expect(() => safeStorage.encryptString('plaintext')).to.throw(/Encryption is not available/);
|
||||
expect(() => safeStorage.decryptString(Buffer.from(''))).to.throw(/Decryption is not available/);
|
||||
}
|
||||
})()
|
||||
.then(app.quit)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
app.exit(1);
|
||||
});
|
||||
1
spec-main/fixtures/extensions/missing-manifest/main.js
Normal file
1
spec-main/fixtures/extensions/missing-manifest/main.js
Normal file
@@ -0,0 +1 @@
|
||||
console.log('oh no where is my manifest');
|
||||
@@ -79,6 +79,58 @@ describe('webContents.setWindowOpenHandler', () => {
|
||||
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
it('does not fire window creation events if the handler callback throws an error', (done) => {
|
||||
const error = new Error('oh no');
|
||||
const listeners = process.listeners('uncaughtException');
|
||||
process.removeAllListeners('uncaughtException');
|
||||
process.on('uncaughtException', (thrown) => {
|
||||
try {
|
||||
expect(thrown).to.equal(error);
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
} finally {
|
||||
process.removeAllListeners('uncaughtException');
|
||||
listeners.forEach((listener) => process.on('uncaughtException', listener));
|
||||
}
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('new-window', () => {
|
||||
assert.fail('new-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('did-create-window', () => {
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
|
||||
|
||||
browserWindow.webContents.setWindowOpenHandler(() => {
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
|
||||
it('does not fire window creation events if the handler callback returns a bad result', async () => {
|
||||
const bad = new Promise((resolve) => {
|
||||
browserWindow.webContents.setWindowOpenHandler(() => {
|
||||
setTimeout(resolve);
|
||||
return [1, 2, 3] as any;
|
||||
});
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('new-window', () => {
|
||||
assert.fail('new-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('did-create-window', () => {
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
|
||||
|
||||
await bad;
|
||||
});
|
||||
|
||||
it('does not fire window creation events if an override returns action: deny', async () => {
|
||||
const denied = new Promise((resolve) => {
|
||||
browserWindow.webContents.setWindowOpenHandler(() => {
|
||||
@@ -87,11 +139,11 @@ describe('webContents.setWindowOpenHandler', () => {
|
||||
});
|
||||
});
|
||||
browserWindow.webContents.on('new-window', () => {
|
||||
assert.fail('new-window should not to be called with an overridden window.open');
|
||||
assert.fail('new-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('did-create-window', () => {
|
||||
assert.fail('did-create-window should not to be called with an overridden window.open');
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
|
||||
@@ -107,11 +159,11 @@ describe('webContents.setWindowOpenHandler', () => {
|
||||
});
|
||||
});
|
||||
browserWindow.webContents.on('new-window', () => {
|
||||
assert.fail('new-window should not to be called with an overridden window.open');
|
||||
assert.fail('new-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('did-create-window', () => {
|
||||
assert.fail('did-create-window should not to be called with an overridden window.open');
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
await browserWindow.webContents.loadURL('data:text/html,<a target="_blank" href="http://example.com" style="display: block; width: 100%; height: 100%; position: fixed; left: 0; top: 0;">link</a>');
|
||||
@@ -129,11 +181,11 @@ describe('webContents.setWindowOpenHandler', () => {
|
||||
});
|
||||
});
|
||||
browserWindow.webContents.on('new-window', () => {
|
||||
assert.fail('new-window should not to be called with an overridden window.open');
|
||||
assert.fail('new-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
browserWindow.webContents.on('did-create-window', () => {
|
||||
assert.fail('did-create-window should not to be called with an overridden window.open');
|
||||
assert.fail('did-create-window should not be called with an overridden window.open');
|
||||
});
|
||||
|
||||
await browserWindow.webContents.loadURL('data:text/html,<a href="http://example.com" style="display: block; width: 100%; height: 100%; position: fixed; left: 0; top: 0;">link</a>');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user