Compare commits

..

85 Commits

Author SHA1 Message Date
Sudowoodo Release Bot
2f4e7679f8 Bump v19.0.7 2022-06-30 08:53:29 -07:00
Sudowoodo Release Bot
50d45d07ac Revert "Bump v19.0.7"
This reverts commit 8fa0cbe27a.
2022-06-29 13:07:14 -07:00
trop[bot]
39df0bdb74 build: remove appveyor hook to defunct service (#34791)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-29 19:33:11 +02:00
Sudowoodo Release Bot
8fa0cbe27a Bump v19.0.7 2022-06-29 08:31:37 -07:00
trop[bot]
addf23c579 fix: resolve symlinks when computing relative asar paths for integrity (#34781)
Co-authored-by: Samuel Attard <sattard@salesforce.com>
2022-06-29 17:17:42 +02:00
trop[bot]
6b67219dbf docs: replace broken Windows taskbar images (#34753)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2022-06-28 10:08:20 +02:00
trop[bot]
2298908a1f fix: make navigator.userAgentData non-empty (#34757)
fix: make navigator.userAgentData non-empty (#34481)

Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2022-06-28 10:07:50 +02:00
trop[bot]
b8af801f82 refactor: load webFrame via process._linkedBinding in security-warnings.ts (#34747)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2022-06-27 14:12:37 +02:00
trop[bot]
c38f15eb03 fix: WCO respects maximizable/closable/minimizable (#34722)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-23 20:46:37 -04:00
Pedro Pontes
6d65180819 chore: cherry-pick 22c61cfae5d1 from chromium (#34712)
* chore: cherry-pick 22c61cfae5d1 from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-06-23 21:07:19 +02:00
electron-roller[bot]
6ba162de48 chore: bump chromium to 102.0.5005.134 (19-x-y) (#34702)
chore: bump chromium in DEPS to 102.0.5005.134

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2022-06-22 15:41:55 -04:00
Sudowoodo Release Bot
e9e2b6dfda Bump v19.0.6 2022-06-22 08:31:07 -07:00
trop[bot]
88632cbdc7 fix: window button visibility fullscreen interaction (#34674)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-21 13:23:32 +02:00
trop[bot]
d6b2e757f8 chore: let result: ReturnType<typeof this._callWindowOpenHandler>; (#34668)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2022-06-21 16:31:48 +09:00
trop[bot]
b91ab0ebf8 build: mark existing Node.js flakes as dontcare (#34658)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-21 10:48:52 +09:00
Sudowoodo Release Bot
c0588770ea Bump v19.0.5 2022-06-20 10:16:20 -07:00
trop[bot]
2a9a71af29 fix: overzealous media key listening on Windows (#34646)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-20 13:13:45 -04:00
Shelley Vohr
7073603150 refactor: remove deprecated drag-and-drop code (#34652) 2022-06-20 12:02:06 -04:00
trop[bot]
82bb684765 fix: performance problem in crashReporter.start() on macOS (#34638)
* fix: performance problem in crashReporter.start() on macOS

This change reduces the duration of crashReporter.start() on Intel macOS
from 622 milliseconds to 257 milliseconds!

Backports https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3641386

  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 -
  8f8d82dea0: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>

Fixes: https://github.com/electron/electron/issues/34321
Signed-off-by: Darshan Sen <raisinten@gmail.com>

* Update .patches

Co-authored-by: Darshan Sen <raisinten@gmail.com>
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2022-06-20 16:43:11 +09:00
trop[bot]
36169d15da chore: fix BrowserView painting when origin updated (#34642)
chore: fix View painting when origin updated

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-20 16:41:45 +09:00
Raymond Zhao
5174419819 refactor: use stubs for libgdk-pixbuf dependency (#34602) 2022-06-20 13:30:43 +09:00
Keeley Hammond
64fc21fec9 build: Revert "Bump v19.0.6" & "Bump v19.0.5" (#34600)
Revert "Bump v19.0.6" & "Bump v19.0.5"
2022-06-17 15:50:10 -04:00
trop[bot]
74df9ad42f fix: draggable regions updating on bounds change (#34611)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-17 15:36:26 +02:00
Sudowoodo Release Bot
12f4be7fba Bump v19.0.6 2022-06-15 18:26:49 -07:00
Sudowoodo Release Bot
9b8cd7cc53 Bump v19.0.5 2022-06-15 17:52:45 -07:00
trop[bot]
59a3898ba8 chore: increase timeout for electron-build step (#34584)
Co-authored-by: VerteDinde <vertedinde@electronjs.org>
2022-06-16 09:44:16 +09:00
Sudowoodo Release Bot
6cb2b75fbb Revert "Bump v19.0.5"
This reverts commit 0764bb1560.
2022-06-15 13:21:08 -07:00
Sudowoodo Release Bot
0764bb1560 Bump v19.0.5 2022-06-15 08:31:03 -07:00
trop[bot]
5dd4b6aaed test: add missing page-title-updated event spec for webview (#34543)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2022-06-14 16:16:28 -04:00
trop[bot]
4e97448f70 fix: crash when setWindowOpenHandler callback throws (#34546)
* fix: crash when setWindowOpenHandler throws

* refactor: throw as process uncaughtException event

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-14 15:23:14 -04:00
electron-roller[bot]
92528220db chore: bump chromium to 102.0.5005.115 (19-x-y) (#34498)
* chore: bump chromium in DEPS to 102.0.5005.115

* Trigger Build

* chore: update patches

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-06-14 11:04:22 -04:00
trop[bot]
108d1f9a29 refactor: move duplicate code to RendererClientBase::ShouldLoadPreload helper (#34519)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2022-06-13 15:20:37 +02:00
trop[bot]
4dedbadcb5 fix: all files selection logic on linux (#34517)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-13 11:16:57 +02:00
trop[bot]
ea2bea7382 fix: don't require glibc 2.29+ on linux arm64 builds (#34502)
* build: use sid sysroot for linux arm64

* try this

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-06-10 20:55:23 -04:00
trop[bot]
3cf901e45b fix: update normal bounds prior to minimizing (#34484)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-09 15:39:08 -04:00
trop[bot]
e87d17b728 build: update clang-format script (#34480)
* build: fix clang-format script

* chore: remove python2 cases

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-09 11:13:54 +09:00
Sudowoodo Release Bot
def1ec7f99 Bump v19.0.4 2022-06-08 08:30:58 -07:00
trop[bot]
85f6bffee0 chore: fix nan spec runner on macOS (#34463)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-08 10:30:12 +02:00
trop[bot]
94632e9703 test: fix for flaky renderer crash test (#34455)
Co-authored-by: samuelmaddock <samuel.maddock@gmail.com>
2022-06-07 09:46:11 +02:00
trop[bot]
7acd622750 test: skip flaky test on 32-bit Windows (#34449)
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-06-06 14:40:03 -04:00
trop[bot]
c050839202 docs: fix did-frame-navigate example in webFrameMain docs (#34444)
docs: fix did-frame-navigate example in webFrameMain docs

Co-authored-by: Will Anderson <will@itsananderson.com>
2022-06-06 11:21:08 -04:00
David Sanders
c0555c1668 fix: on macOS show BrowserWindow on maximize if not currently shown (#34365)
fix: on macOS show BrowserWindow on maximize if not currently shown (#32949)
2022-06-06 11:12:20 -04:00
Sudowoodo Release Bot
07344857d6 Bump v19.0.3 2022-06-02 19:35:06 -07:00
trop[bot]
f57ca1174c fix: render process crash handling (#34431)
* fix: crash when renderer process is reused

Could occur when a renderer crashes and the same-origin URL is loaded again
which leads to reusing the renderer process.

* test: renderer process crash recovery

* fix: handle case which leads to render frame DCHECK

* fix: lint

Co-authored-by: samuelmaddock <samuel.maddock@gmail.com>
2022-06-02 19:33:11 -07:00
trop[bot]
a189d3dde6 ci: cache python install to better deal with download errors. (#34408)
* ci: cache python install to better deal with download errors.

* chore: use our CDN to download python2

* build: DRY up the python install steps

* fixup backport for 19-x-y

* trigger ci

* fixup the fixup

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
Co-authored-by: Samuel Attard <sattard@salesforce.com>
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2022-06-02 17:15:19 -04:00
trop[bot]
a49f0f7318 fix: crash in WebFrameMain mojo connection when RenderFrameHost is nullptr (#34416)
* fix: crash when RenderFrameHost is nullptr

* chore: lint fix

Co-authored-by: samuelmaddock <samuel.maddock@gmail.com>
2022-06-02 15:16:41 +09:00
Sudowoodo Release Bot
ba32b32ec3 Bump v19.0.2 2022-06-01 08:31:50 -07:00
trop[bot]
3ad5a45173 fix: zombie windows when fullscreening and closing (#34391)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-31 15:36:50 +02:00
Shelley Vohr
b9d16ab3eb fix: esc not working on Windows during fullscreen (#34359)
* fix: esc not working on Windows during fullscreen

* chore: fix lint
2022-05-30 16:28:51 +02:00
Shelley Vohr
7e2606df02 fix: missing resource printing DCHECK (#34369)
* fix: missing resource printing DCHECK

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-05-30 12:19:27 +02:00
trop[bot]
8692a5b921 test: add setTitlebarOverlay spec (#34252)
spec: add setTitlebarOverlay spec

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-26 11:05:03 -04:00
electron-roller[bot]
abf438bddc chore: bump chromium to 102.0.5005.63 (19-x-y) (#34349)
chore: bump chromium in DEPS to 102.0.5005.63

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2022-05-25 16:45:55 -04:00
Sudowoodo Release Bot
56515ad544 Bump v19.0.1 2022-05-25 08:31:42 -07:00
trop[bot]
a9ff8f1359 build: do not checkout chromium for ts docs check (#34344)
Co-authored-by: Samuel Attard <sattard@salesforce.com>
2022-05-24 21:36:57 -07:00
trop[bot]
f84cafe4fd chore: add a TRACE call for crash_reporter::Start() (#34326)
chore: add a TRACE call for crash_reporter::Start()

Initializing the crashReporter takes around 620 milliseconds on Intel
macOS. I have sent a CL to crashpad to partially fix the performance
issue in
https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3641386.
It would be beneficial to log the performance impact of this function in
the traces in case this slows down in the future.

Signed-off-by: Darshan Sen <raisinten@gmail.com>

Co-authored-by: Darshan Sen <raisinten@gmail.com>
2022-05-24 07:41:08 -07:00
Sudowoodo Release Bot
42266d2bf0 Bump v19.0.0 2022-05-23 10:25:38 -07:00
John Kleinschmidt
44b5c72f67 fix: crash on navigator.serial.getPorts() (#34323)
(cherry picked from commit 7f9431764f)
2022-05-23 10:21:05 -07:00
trop[bot]
96e1c7ec92 fix: building node modules with Visual Studio 2017 (#34316)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2022-05-23 10:11:38 -07:00
trop[bot]
0206a9b7ed fix: service worker registration with custom protocols (#34315)
* fix: service worker registration with custom protocols

Refs https://github.com/electron/electron/issues/32664

* chore: update patches

Co-authored-by: deepak1556 <hop2deep@gmail.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-05-23 08:29:54 -07:00
trop[bot]
10ad6f8295 docs: remove X-Content-Security-Policy header in quick-start.md (#34319)
reference: Note: It is known that having both Content-Security-Policy
 and X-Content-Security-Policy or X-Webkit-CSP causes unexpected
 behaviours on certain versions of browsers. Please avoid using deprecated
 X-* headers. https://content-security-policy.com/
also:
1ad18486ed

Co-authored-by: Letu Ren <fantasquex@gmail.com>
2022-05-23 11:19:05 -04:00
Keeley Hammond
23c18be06f revert: add first-instance-ack event to the app.requestSingleInstanceLock() flow (#34312)
fix: revert "feat: add first-instance-ack event to the `app.requestSingleInstanceLock()` flow (#34295)

* Revert "fix: requestSingleInstanceLock API ConnectNamedPipe sometimes hangs program (#33778)"

This reverts commit ffb8749243.

* fix: revert "feat: add first-instance-ack event to the `app.requestSingleInstanceLock()` flow"

* chore: update patch
2022-05-23 14:51:05 +02:00
Keeley Hammond
cc4565bb41 ci: save Windows artifacts at end of job in case they fail (#34313) 2022-05-23 11:25:53 +02:00
Shelley Vohr
9d1c53a7e4 fix: DCHECK on webContents.print() (#34299)
* fix: DCHECK on webContents.print()

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-05-23 10:11:06 +02:00
Shelley Vohr
c7cd23c069 fix: WCO crash on bad titlebarStyle (#34302) 2022-05-22 19:42:27 -07:00
Shelley Vohr
f9dc5b52d0 fix: crash when loading extension with missing manifest (#34303) 2022-05-22 19:32:22 -07:00
electron-roller[bot]
aca4b543d5 chore: bump chromium to 102.0.5005.61 (19-x-y) (#34288)
chore: bump chromium in DEPS to 102.0.5005.61

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
2022-05-20 10:32:30 +02:00
trop[bot]
c2a11cef63 fix: crash when creating interface for speculative frame (#34294)
Co-authored-by: samuelmaddock <samuel.maddock@gmail.com>
2022-05-19 15:13:48 -07:00
trop[bot]
85a7498bd6 fix: delayed bounds when moving/resizing and preventing default (#34285)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-19 18:25:11 +02:00
Sudowoodo Release Bot
b0c255b72c Bump v19.0.0-beta.8 2022-05-19 06:31:02 -07:00
trop[bot]
2d91a03b36 docs: Update release dates for E20 (#34275)
Co-authored-by: Sofia Nguy <sofianguy@gmail.com>
2022-05-18 13:21:51 -07:00
trop[bot]
424fd85b1a docs: add missing explanation for [angle|dawn]_enable_vulkan_validation_layers = false (#34259)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2022-05-17 11:43:55 -04:00
electron-roller[bot]
ad48ccbb9b chore: bump chromium to 102.0.5005.49 (19-x-y) (#34198)
* chore: bump chromium in DEPS to 102.0.5005.49

* chore: update patches

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-05-16 12:01:03 -04:00
Sudowoodo Release Bot
2ea9be3ade Bump v19.0.0-beta.7 2022-05-16 06:31:51 -07:00
trop[bot]
52a9566f28 fix: fetching PDF element from WebLocalFrame (#34215)
fix: fetching PDF element from WebLocalFrame

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-13 11:24:01 -05:00
trop[bot]
4ecaae9555 fix: call loadUrl when opening new windows from links (#34189)
* fix: call loadUrl when opening new windows from links

* spec: add regression test

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-12 21:49:25 -04:00
trop[bot]
21b8200170 fix: tray icon not highlighting on empty menu (#34208)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-12 16:20:00 -07:00
Sudowoodo Release Bot
2b6cd3458f Bump v19.0.0-beta.6 2022-05-12 06:31:14 -07:00
trop[bot]
00e747ac24 test: fixup done being called multiple times (#34188)
* test: fixup done being called multiple times

* chore: run ci

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
Co-authored-by: Samuel Attard <sattard@salesforce.com>
2022-05-12 11:04:42 +02:00
Keeley Hammond
082b06cf4e build: re-enable 32-bit Windows symbol generation (#34179) 2022-05-11 13:16:49 -07:00
trop[bot]
b07e17a3bb fix: fix a crash in safeStorage on Linux (#34147)
On Linux, `isEncryptionAvailable()` was crashing instead of returning a
boolean before the 'ready' event was emitted by the app. The reason of
the crash is that [`CreateKeyStorage()`](https://source.chromium.org/chromium/chromium/src/+/main:components/os_crypt/os_crypt_linux.cc;l=74;drc=35be6215ec8f09e50176f36753c68f26c63d1885;bpv=1;bpt=0)
expects the config to be set but the function responsible for setting the
config, [`SetConfig()`](https://source.chromium.org/chromium/chromium/src/+/main:components/os_crypt/os_crypt_linux.cc;l=237;drc=35be6215ec8f09e50176f36753c68f26c63d1885;bpv=1;bpt=0),
is called only after the app is ready inside [`PostCreateMainMessageLoop()`](https://github.com/electron/electron/blob/main/shell/browser/electron_browser_main_parts.cc#L499).
So this changes `IsEncryptionAvailable()` to return `false` when the app
is not ready on Linux and uses that instead of the raw API in other
places like `EncryptString()` and `DecryptString()`.

Fixes: https://github.com/electron/electron/issues/32206
Signed-off-by: Darshan Sen <raisinten@gmail.com>

Co-authored-by: Darshan Sen <raisinten@gmail.com>
2022-05-11 10:20:30 -04:00
trop[bot]
49ee456797 test: run node specs with py3 (#34165)
Co-authored-by: Jeremy Rose <japthorp@slack-corp.com>
2022-05-11 10:02:00 +02:00
trop[bot]
85063322e9 fix: building node modules with Visual Studio 2017 (#34164)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2022-05-11 10:01:48 +02:00
trop[bot]
633d2961eb fix: create singleton pipename from user & executable (#34160)
* fix: create singleton pipename from user & executable

* fix: use process id & main thread id for pipe name

* fix: write rand to file using WIN method

* fix: remove file rand, add user_name to pipe

* chore: style fixes, shorten program_name & user_name

* fix: remove user_name

* chore: trigger CircleCI

Co-authored-by: VerteDinde <keeleymhammond@gmail.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
2022-05-10 16:26:05 -07:00
trop[bot]
4f0592101b chore: stub gtk_native_dialog_get_type (#34151)
Co-authored-by: deepak1556 <hop2deep@gmail.com>
2022-05-09 20:59:41 -07:00
trop[bot]
8797485564 build: change upload-to-s3 vars to upload-to-storage (#34143)
* build: change upload-to-s3 vars to upload-to-az

* build: change upload-to-az to upload-to-storage

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
2022-05-09 12:57:14 -07:00
104 changed files with 2696 additions and 1328 deletions

View File

@@ -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'

View File

@@ -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

View File

@@ -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
View File

@@ -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':

View File

@@ -1 +1 @@
19.0.0-beta.5
19.0.7

View File

@@ -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 )

View File

@@ -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

View File

@@ -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...

View File

@@ -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)`

View File

@@ -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")'

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

View File

@@ -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:**

View File

@@ -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>

View File

@@ -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.
![IE](https://i-msdn.sec.s-msft.com/dynimg/IC420539.png)
![Taskbar JumpList](../images/windows-taskbar-jumplist.png)
> 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.
![player](https://i-msdn.sec.s-msft.com/dynimg/IC420540.png)
![Thumbnail toolbar](../images/windows-taskbar-thumbnail-toolbar.png)
> 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.
![Overlay on taskbar button](https://i-msdn.sec.s-msft.com/dynimg/IC420441.png)
![Overlay on taskbar button](../images/windows-taskbar-icon-overlay.png)
> NOTE: The screenshot above is an example of overlay on a taskbar button

View File

@@ -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",

View File

@@ -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.">

View File

@@ -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) {

View File

@@ -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';

View File

@@ -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;

View File

@@ -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 --"

View File

@@ -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

View File

@@ -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.

View 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);

View File

@@ -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",

View 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,

View File

@@ -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, &current_directory)) {
+ std::vector<const uint8_t> additional_data;
+ if (!ParseCommandLine(cds, &parsed_command_line, &current_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);

View File

@@ -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, &current_directory)) {
+ std::vector<uint8_t> additional_data;
+ if (!ParseCommandLine(cds, &parsed_command_line, &current_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);

View File

@@ -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();

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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();
+

View File

@@ -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 cant 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 its 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
- // shouldnt 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.
- //
- // Its 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 clients 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 dont 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 its 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 shouldnt 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.
+ //
+ // Its 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 clients 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 dont 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
//! parents process group or session and will not have a controlling terminal,
//! providing isolation from signals not intended for it. The grandchilds
@@ -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_

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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',

View File

@@ -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);

View File

@@ -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',

View File

@@ -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 = {

View File

@@ -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`,

View File

@@ -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])

View File

@@ -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:

View File

@@ -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",

View File

@@ -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()) {

View File

@@ -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();

View File

@@ -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;

View File

@@ -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. "

View File

@@ -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(

View File

@@ -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,

View File

@@ -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");
}

View File

@@ -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_;

View File

@@ -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,

View File

@@ -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;

View File

@@ -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_;
}

View File

@@ -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;

View File

@@ -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:

View File

@@ -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)

View File

@@ -27,6 +27,9 @@ void NativeBrowserViewViews::SetAutoResizeFlags(uint8_t flags) {
void NativeBrowserViewViews::UpdateDraggableRegions(
const std::vector<mojom::DraggableRegionPtr>& regions) {
if (&draggable_regions_ != &regions)
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() {

View File

@@ -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.

View File

@@ -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();
}

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View 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)

View File

@@ -1 +1,2 @@
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gtk/gtk.h>

View File

@@ -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);

View File

@@ -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)) {

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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

View File

@@ -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_;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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))

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();

View File

@@ -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();

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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');
});
});

View File

@@ -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: './'})`);
});

View File

@@ -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');

View File

@@ -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', () => {

View File

@@ -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,

View File

@@ -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];

View File

@@ -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'));

View 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>

View 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();
});

View 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>Window From Link</title>
</head>
<body>
I'm a window opened from a link!
</body>
</html>

View File

@@ -0,0 +1,4 @@
{
"name": "electron-test-open-new-window-from-link",
"main": "main.js"
}

View File

@@ -0,0 +1,3 @@
window.addEventListener('click', e => {
console.log('click', e);
});

View 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);
});

View File

@@ -0,0 +1 @@
console.log('oh no where is my manifest');

View File

@@ -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