Compare commits

..

116 Commits

Author SHA1 Message Date
Sudowoodo Release Bot
dce1b44b61 Bump v18.3.9 2022-08-17 08:34:27 -07:00
Pedro Pontes
8b7a1ca78f chore: cherry-pick bd9724c9fe63 from chromium (#35276)
* chore: cherry-pick bd9724c9fe63 from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-08-16 13:31:41 -04:00
trop[bot]
00120e6337 fix: add uv_loop_close when object release to fix crash (#35340)
Co-authored-by: yangzuohui <yangzuohui@bytedance.com>
2022-08-15 16:48:01 -04:00
Pedro Pontes
3e524ffe31 chore: cherry-pick c643d18a078d from chromium (#35272)
* chore: cherry-pick c643d18a078d from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-08-15 15:46:06 +09:00
trop[bot]
57f29743b4 fix: WCO occlusion of DevTools (#35307)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-08-15 10:30:49 +09:00
Pedro Pontes
75b03a5aae chore: cherry-pick 54a7927b19f9 from chromium (#35268)
Co-authored-by: Electron Bot <electron@github.com>
2022-08-15 09:58:51 +09:00
Sudowoodo Release Bot
6efe392926 Bump v18.3.8 2022-08-10 08:32:20 -07:00
Pedro Pontes
16a5482dde chore: cherry-pick 60d8559e150a from chromium (#35270)
* chore: cherry-pick 60d8559e150a from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-08-09 14:51:52 -04:00
Pedro Pontes
047a48ebfd chore: cherry-pick 54e32332750c from chromium (#35274)
* chore: cherry-pick 54e32332750c from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-08-09 10:37:12 +02:00
Jeremy Rose
f78d95d3ad chore: cherry-pick 94a8bdafc8c6 from chromium (#35236)
* chore: cherry-pick 94a8bdafc8c6 from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-08-09 09:38:56 +02:00
trop[bot]
3314f0cbf5 fix: merge crash annotations instead of overwriting (#35263)
ElectronCrashReporterClient::GetProcessSimpleAnnotations() merges
annotations provided as argument with global_annotations_,
preserving useful information.

Co-authored-by: Alexander Petrov <zowers+github@zowers.net>
2022-08-08 21:57:43 +02:00
Milan Burda
05bf827fc0 build: fix building with enable_plugins = false (#35261) 2022-08-08 11:46:01 -04:00
Jeremy Rose
03d98f04b6 chore: cherry-pick 06aea31d10f8 from webrtc (#35238)
* chore: cherry-pick 06aea31d10f8 from webrtc

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-08-08 16:44:08 +02:00
trop[bot]
b494afe684 fix: add support for --ozone-platform-hint flag on Linux (#35013)
* fix: add support for --ozone-platform-hint flag on Linux

* fixup! fix: add support for --ozone-platform-hint flag on Linux

Co-authored-by: Valentin Hăloiu <valentin.haloiu@gmail.com>
2022-08-04 10:57:46 +02:00
trop[bot]
f54c9df0cd fix: handle WCO pressed state when going maximized -> minimized (#35072)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-08-04 10:51:48 +02:00
Sudowoodo Release Bot
dee6e01e9e Bump v18.3.7 2022-08-03 08:32:21 -07:00
Robo
483e39cc74 chore: cherry-pick 97193a64b431 from chromium (#35184)
Backports https://chromium-review.googlesource.com/c/chromium/src/+/3542265

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-08-03 10:34:29 -04:00
trop[bot]
cd7490d233 fix: consider dock space when showing menu (#35198)
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
2022-08-03 10:21:04 -04:00
trop[bot]
b990bd6c97 fix: allow setsize to be called within a move or resize for preventDefault (#35082)
fix: #34599 allow setsize to be called within a move or resize for preventDefault

Co-authored-by: Ian German Mesner <mesner@gmail.com>
2022-08-03 11:54:58 +02:00
trop[bot]
56a0b45ef2 fix: modify file extension generation on Windows (#35171)
fix: modify file extension generation on Windows (#34723)

* fix: modify file extension generation on Windows

* modify includes

* include vector in header

* add win build flags

* remove hardcoded strings

* Update shell/browser/electron_download_manager_delegate.h

Co-authored-by: Charles Kerr <charles@charleskerr.com>

* fix string manipulation and function definitions

* Update electron_download_manager_delegate.h

* convert to std::string and modify for electron

* Update shell/browser/electron_download_manager_delegate.cc

Co-authored-by: Charles Kerr <charles@charleskerr.com>

* remove vector include and update conversion

* add vectr include for lint

Co-authored-by: Charles Kerr <charles@charleskerr.com>

Co-authored-by: Michaela Laurencin <35157522+mlaurencin@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2022-08-02 15:54:02 -04:00
Robo
5871f81bb9 fix: touch events not recognized by WCO on windows (#35117) (#35177)
* fix: touch events not recognized by WCO on windows (#35117)

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-08-02 15:39:57 -04:00
trop[bot]
511f27506f ci: turn off windows on arm test result comments (#35167)
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-08-02 10:58:22 -04:00
trop[bot]
8189ee64b9 chore: add electron deps to //src gitignore (#35148)
chore: add electon deps to //src gitignore

Co-authored-by: Samuel Attard <sattard@salesforce.com>
2022-08-01 16:55:07 -04:00
trop[bot]
cc52f07023 ci: switch to GHA for WOA (#35127)
ci: switch to GHA for WOA (#35109)

* ci: switch to GHA for WOA

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
(cherry picked from commit 674596d11e)

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-07-29 14:56:31 -04:00
trop[bot]
890adefb95 docs: new main -> renderers messageChannel example (#35133)
* docs: new main -> renderers messageChannel example

* consistent use of your

* fix a typo

* linting

* markdown linting

* Update docs/tutorial/message-ports.md

Co-authored-by: Erick Zhao <erick@hotmail.ca>

* update code example headings, reference contextIsolation example

* remove nodeIntegration: false from browserWindows

* rename "messagePort" to "electronMessagePort" for compatibility

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>
Co-authored-by: Erick Zhao <erick@hotmail.ca>
2022-07-29 11:44:45 -07:00
trop[bot]
348e76b1d6 fix: empty result of webContents.getUserAgent() (#35130)
fix: empty result of webContents.getUserAgent()

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-07-29 11:44:14 -07:00
Pedro Pontes
cfee3286b2 chore: cherry-pick 902f0d144a5b from chromium (#35097)
* chore: cherry-pick 902f0d144a5b from chromium

* chore: update patches

* Trigger Build

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-07-29 10:57:32 -04:00
Pedro Pontes
d7acbeb8ee chore: cherry-pick 3466cc056b05 from pdfium (#35099)
* chore: cherry-pick 3466cc056b05 from pdfium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-07-28 15:46:48 -04:00
Sudowoodo Release Bot
b35c90777a Bump v18.3.6 2022-07-28 09:21:57 -07:00
Pedro Pontes
b5bc30da45 chore: cherry-pick 664e0d8b4cfb from chromium (#35102)
* chore: cherry-pick 664e0d8b4cfb from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-07-28 11:54:23 -04:00
Devin Foley
3ca4944ad8 fix: Make disable_color_correct_rendering patch work again (#35088)
Fix disable_color_correct_rendering patch.
2022-07-28 12:14:13 +02:00
Keeley Hammond
40fcfaa5e9 fix: use win_clang_x64 binary for x86 extract symbols (#35078) (#35096) 2022-07-28 16:55:54 +09:00
John Kleinschmidt
da6e16157f ci: explicitly use pylint-1.5 for pylint (#35105)
3780302: pylint: drop unversioned program

https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/3780302
2022-07-27 16:26:54 -04:00
trop[bot]
358d6f83cf fix: crash on BrowserWindow.setEnabled() (#34973)
fix: crash on BrowserWindow.setEnabled()

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-07-27 11:16:01 +02:00
Jeremy Rose
c5918acbf1 chore: cherry-pick ecad352cd614 from chromium (#34689) 2022-07-25 15:44:10 -07:00
Jeremy Rose
dba8e7fe42 chore: cherry-pick d7a5d6b38ea8 from chromium (#34998)
* chore: [18-x-y] cherry-pick d7a5d6b38ea8 from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-07-25 10:50:07 +02:00
Sudowoodo Release Bot
2fc331859f Revert "Bump v18.3.6"
This reverts commit c3a3274826.
2022-07-22 14:52:13 -07:00
Sudowoodo Release Bot
c3a3274826 Bump v18.3.6 2022-07-22 13:10:03 -07:00
Jeremy Rose
675737397d chore: cherry-pick 22abbad430b6 from chromium (#35004)
* chore: [18-x-y] cherry-pick 22abbad430b6 from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-07-21 10:29:03 -07:00
Jeremy Rose
5059502456 chore: cherry-pick 3cbd5973d704 from chromium (#35002)
* chore: [18-x-y] cherry-pick 3cbd5973d704 from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-07-21 09:37:35 +02:00
Jeremy Rose
b1093f454c chore: cherry-pick 8ea66a7833e2 from v8 (#35000)
* chore: [18-x-y] cherry-pick 8ea66a7833e2 from v8

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-07-21 09:37:24 +02:00
John Kleinschmidt
51146df170 build: run CI tests on Xcode 13.3.0 (#34982)
* build: run CI tests on Xcode 13.3.0 (#34850)

* build: test disabling security

* build: install python2 during tests

* build: do not install python2 on arm64 runners

* attempt 2

* build: only allow 13.3.0 xcode

(cherry picked from commit 459404f536)

* chore: update circleci config with needed changes from main

(cherry picked from commit f3ed1bd0f1)

Co-authored-by: Samuel Attard <sam@electronjs.org>
2022-07-20 09:30:48 +02:00
trop[bot]
1c59c8eb61 ci: wait longer for goma to be ready (#34964)
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-07-19 16:28:17 +02:00
trop[bot]
09bb0c14a7 fix: alwaysOnTop browser window option for X11 Linux (#34911)
fix: alwaysontop browser window option for x11

Co-authored-by: Gellert Hegyi <gellert.hegyi@around.co>
2022-07-19 10:52:59 +02:00
trop[bot]
fcef0963d2 fix: prevent brief display of "Ozone X11" in window title on Linux (#34943) 2022-07-19 10:19:59 +02:00
Shelley Vohr
b608874076 fix: fullscreen windows aren't resizable on macOS (#34907) 2022-07-14 09:22:17 +02:00
Shelley Vohr
f06842e6f8 fix: call loadUrl when opening new windows from links (#34910) 2022-07-13 17:17:04 -04:00
trop[bot]
4119881a36 fix: set Wayland application ID (#34877)
* refactor: extract XDG app ID logic into a method

* fix: set application ID on Wayland

Co-authored-by: Valentin Hăloiu <valentin.haloiu@gmail.com>
2022-07-12 10:16:15 +02:00
Pedro Pontes
ecb9afd7d6 chore: cherry-pick 13ffdf63a471 from v8 (#34881)
* chore: [18-x-y] cherry-pick 13ffdf63a471 from v8

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-07-12 10:16:05 +02:00
Pedro Pontes
a70431f8db chore: cherry-pick 763d847f1e5a from webrtc (#34882)
* chore: cherry-pick 763d847f1e5a from webrtc

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-07-11 20:08:19 -04:00
trop[bot]
b21dae6bee fix: safer check for WCO button updates (#34873)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-07-11 13:37:42 +02:00
trop[bot]
cb3fef890f fix: WCO pressed background state updates (#34838)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-07-11 17:21:36 +09:00
trop[bot]
b61a6f8021 fix: setRepresentedFilename with non-default titlebarStyle (#34847)
fix: setRepresentedFilename with non-default titlebarStyle

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-07-08 10:50:54 +02:00
Sudowoodo Release Bot
b390336a5c Revert "Bump v18.3.6"
This reverts commit 7a9747021d.
2022-07-06 13:00:39 -07:00
Sudowoodo Release Bot
7a9747021d Bump v18.3.6 2022-07-06 08:32:51 -07:00
Jeremy Rose
18fd51af5f chore: cherry-pick f427936d32db from chromium (#34685)
* chore: [18-x-y] cherry-pick f427936d32db from chromium

* fix patch.

* chore: update patches

Co-authored-by: Electron Bot <electron@github.com>
Co-authored-by: Pedro Pontes <pepontes@microsoft.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-07-05 13:51:45 -04:00
Jeremy Rose
9e51cf4a65 chore: cherry-pick d0882b3dff76 from v8 (#34687)
* chore: [18-x-y] cherry-pick d0882b3dff76 from v8

* fix patch.

* fix patch pt. 2

Co-authored-by: Electron Bot <electron@github.com>
Co-authored-by: Pedro Pontes <pepontes@microsoft.com>
2022-07-05 11:37:11 -04:00
Sudowoodo Release Bot
76a07c227d Revert "Bump v18.3.6"
This reverts commit 9308ac6e67.
2022-06-30 22:39:32 -07:00
Sudowoodo Release Bot
9308ac6e67 Bump v18.3.6 2022-06-30 15:06:31 -07:00
trop[bot]
3823245a46 fix: BrowserView background color in webContents (#33478)
* chore: fix BrowserView background color in webContents

* disable screen capture test on linux

* spec: fix platform failure condition

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-30 12:35:07 -07:00
Keeley Hammond
f49f74831b docs: expand tutorial (#34604) (#34799)
* docs: base tutorial update

* more docs

* zzz

* remove unused images

Co-authored-by: Erick Zhao <erick@hotmail.ca>
2022-06-30 09:23:36 -07:00
trop[bot]
0b26e76739 build: remove appveyor hook to defunct service (#34793)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-29 10:45:16 -07:00
Sudowoodo Release Bot
079010f01a Revert "Bump v18.3.6"
This reverts commit 8f673fe81d.
2022-06-29 10:44:30 -07:00
Sudowoodo Release Bot
8f673fe81d Bump v18.3.6 2022-06-29 08:32:34 -07:00
trop[bot]
1012fd687a fix: resolve symlinks when computing relative asar paths for integrity (#34780)
Co-authored-by: Samuel Attard <sattard@salesforce.com>
2022-06-29 10:12:55 +02:00
trop[bot]
871be236f8 docs: replace broken Windows taskbar images (#34752)
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
2022-06-28 10:08:13 +02:00
trop[bot]
cee4a64493 fix: make navigator.userAgentData non-empty (#34758)
fix: make navigator.userAgentData non-empty (#34481)

Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2022-06-28 10:07:42 +02:00
trop[bot]
d71c08a404 refactor: load webFrame via process._linkedBinding in security-warnings.ts (#34746)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2022-06-27 14:12:41 +02:00
trop[bot]
5a0f75f42c fix: WCO respects maximizable/closable/minimizable (#34720)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-23 21:07:28 +02:00
Pedro Pontes
f5e26258da chore: cherry-pick 22c61cfae5d1 from chromium (#34714)
* chore: cherry-pick 22c61cfae5d1 from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-06-23 14:24:29 -04:00
Sudowoodo Release Bot
6165f6afc9 Bump v18.3.5 2022-06-22 08:31:20 -07:00
Jeremy Rose
35f871c702 chore: cherry-pick 44c4e56fea2c from v8 (#34692) 2022-06-22 12:26:57 +02:00
trop[bot]
8c3c382673 fix: window button visibility fullscreen interaction (#34673)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-22 10:49:09 +02:00
Shelley Vohr
16ae9e16cf refactor: remove deprecated drag-and-drop code (#34653) 2022-06-20 15:57:04 -04:00
trop[bot]
1d14d15d8c fix: performance problem in crashReporter.start() on macOS (#34640)
* 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

* chore: update patches

Co-authored-by: Darshan Sen <raisinten@gmail.com>
Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-06-20 16:43:17 +09:00
trop[bot]
32df7f58bb chore: fix BrowserView painting when origin updated (#34641)
chore: fix View painting when origin updated

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-20 09:19:36 +02:00
Raymond Zhao
66c3319458 refactor: use stubs for gdk-pixbuf dependency (#34601) 2022-06-20 13:30:47 +09:00
Milan Burda
49955512a8 fix: crash when setWindowOpenHandler callback throws (#34627)
fix: crash when `setWindowOpenHandler` callback throws (#34523)

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-20 09:34:59 +09:00
Pedro Pontes
977dc2527e chore: cherry-pick b03797bdb1df from chromium (#34631)
* chore: cherry-pick b03797bdb1df from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-06-20 09:33:19 +09:00
trop[bot]
4f70332460 fix: draggable regions updating on bounds change (#34610)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-17 15:36:29 +02:00
Jeremy Rose
d8e1f4f73b chore: add ffmpeg patch dir to config.json (#34597)
* chore: add ffmpeg patch dir to config.json

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-06-16 17:14:27 -07:00
Jeremy Rose
ec40581e83 chore: cherry-pick e481fc655a62 from ffmpeg (#34560) 2022-06-16 18:31:13 +02:00
Jeremy Rose
22cdcf400c chore: cherry-pick 2782c7bc5bbe from chromium (#34569)
* chore: cherry-pick 2782c7bc5bbe from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-06-16 16:43:45 +09:00
Jeremy Rose
1b36d1d175 chore: cherry-pick 801b904aea7d from angle (#34566)
* chore: cherry-pick 801b904aea7d from angle

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-06-15 15:47:53 -04:00
Jeremy Rose
4578597dea chore: cherry-pick f3d01ff794dc from chromium (#34556)
* chore: cherry-pick f3d01ff794dc from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-06-15 15:46:01 -04:00
Jeremy Rose
20deb65058 chore: cherry-pick 03aa5ae75c29 from angle (#34568)
* chore: cherry-pick 03aa5ae75c29 from angle

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-06-15 12:53:44 -04:00
Jeremy Rose
7b7d22fa7c chore: cherry-pick 919b1ffe1fe7 from chromium (#34558)
* chore: cherry-pick 919b1ffe1fe7 from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-06-15 12:51:11 -04:00
Pedro Pontes
851350b9f3 chore: cherry-pick f1504440487f from chromium (#34540)
* chore: cherry-pick f1504440487f from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Co-authored-by: Electron Bot <electron@github.com>
2022-06-15 11:33:37 -04:00
Sudowoodo Release Bot
7162f641b5 Bump v18.3.4 2022-06-15 08:31:31 -07:00
Jeremy Rose
88e3f4a1e6 chore: cherry-pick 6661eb4900da from angle (#34554)
* chore: cherry-pick 6661eb4900da from angle

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-06-15 14:25:57 +09:00
trop[bot]
0d1900048d test: add missing page-title-updated event spec for webview (#34545)
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2022-06-15 14:25:44 +09:00
Jeremy Rose
af65324717 chore: cherry-pick f1dd785e021e from chromium (#34562)
* chore: cherry-pick f1dd785e021e from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-06-15 11:29:54 +09:00
Jeremy Rose
6d1cc18322 chore: cherry-pick 21139756239b from chromium (#34534)
* chore: cherry-pick 21139756239b from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-06-14 15:26:47 -04:00
Pedro Pontes
4d73469ed5 chore: cherry-pick 9768648fffc9 from angle (#34537)
* chore: cherry-pick 9768648fffc9 from angle

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-06-14 15:22:19 -04:00
trop[bot]
ca17f574de fix: all files selection logic on linux (#34518)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-13 16:22:06 +02:00
trop[bot]
a5fdd272b1 fix: update normal bounds prior to minimizing (#34485)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-09 15:39:37 -04:00
Sudowoodo Release Bot
15f3c45fe9 Bump v18.3.3 2022-06-08 08:31:14 -07:00
trop[bot]
3f01fb3110 test: fix for flaky renderer crash test (#34453)
Co-authored-by: samuelmaddock <samuel.maddock@gmail.com>
2022-06-08 11:33:01 +02:00
trop[bot]
96789ee78b chore: fix nan spec runner on macOS (#34461)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-06-08 16:10:16 +09:00
trop[bot]
4b8885ffc8 docs: fix did-frame-navigate example in webFrameMain docs (#34445)
docs: fix did-frame-navigate example in webFrameMain docs

Co-authored-by: Will Anderson <will@itsananderson.com>
2022-06-06 11:20:05 -04:00
trop[bot]
c47b196d02 fix: render process crash handling (#34430)
* 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 20:12:50 -07:00
trop[bot]
d67c319642 fix: crash in WebFrameMain mojo connection when RenderFrameHost is nullptr (#34415)
* fix: crash when RenderFrameHost is nullptr

* chore: lint fix

Co-authored-by: samuelmaddock <samuel.maddock@gmail.com>
2022-06-02 15:16:43 +09:00
Sudowoodo Release Bot
6839dd92b8 Bump v18.3.2 2022-06-01 08:32:20 -07:00
trop[bot]
d468a73edb fix: zombie windows when fullscreening and closing (#34392)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-31 15:36:47 +02:00
Robo
e4dbd1407e chore: backport a704c3a from chromium (#34385)
* chore: backport a704c3a from chromium

Refs https://chromium-review.googlesource.com/c/chromium/src/+/3545665
Fixes https://github.com/electron/electron/issues/25387

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-05-31 10:43:38 +02:00
Shelley Vohr
37a422d05b fix: esc not working on Windows during fullscreen (#34361)
* fix: esc not working on Windows during fullscreen

* chore: fix lint
2022-05-31 14:08:45 +09:00
Sudowoodo Release Bot
26e7f27110 Bump v18.3.1 2022-05-25 08:32:14 -07:00
John Kleinschmidt
707e8d5b7b fix: crash on navigator.serial.getPorts() (#34327)
(cherry picked from commit 7f9431764f)
2022-05-24 16:55:20 +02:00
trop[bot]
6fda94858d chore: add a TRACE call for crash_reporter::Start() (#34325)
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 10:21:42 +02:00
trop[bot]
1eece7ada0 docs: remove X-Content-Security-Policy header in quick-start.md (#34318)
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 13:45:06 -04:00
Sudowoodo Release Bot
c33bfffeec Bump v18.3.0 2022-05-23 08:54:56 -07:00
Shelley Vohr
02bf71fb43 fix: crash when loading extension with missing manifest (#34304) 2022-05-23 10:43:53 -04:00
Robo
74b85afca4 fix: service worker registration with custom protocols (#34291)
Refs https://github.com/electron/electron/issues/32664
2022-05-23 21:20:03 +09:00
Keeley Hammond
fbc398f9bc 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-22 22:22:01 -07:00
trop[bot]
4c32d141d8 fix: delayed bounds when moving/resizing and preventing default (#34284)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-05-20 10:32:44 +02:00
trop[bot]
88bedfcf25 fix: crash when creating interface for speculative frame (#34293)
* fix: crash when creating interface for speculative frame

* fix: (attempt 2) always try to connect when using renderer api

* fix: potential crash when rfh is disposed

* refactor: always teardown mojo connection

This should eliminate an entire class of potential errors from appearing in the future.

Co-authored-by: samuelmaddock <samuel.maddock@gmail.com>
2022-05-19 16:37:22 -04:00
167 changed files with 10587 additions and 1766 deletions

View File

@@ -53,14 +53,8 @@ executors:
description: "macOS executor size"
type: enum
enum: ["macos.x86.medium.gen2", "large"]
xcode:
description: "xcode version"
default: 13.2.1
type: enum
enum: ["12.4.0", "13.2.1"]
macos:
xcode: << parameters.xcode >>
xcode: 13.3.0
resource_class: << parameters.size >>
# Electron Runners
@@ -131,6 +125,9 @@ env-apple-silicon: &env-apple-silicon
USE_PREBUILT_V8_CONTEXT_SNAPSHOT: 1
npm_config_arch: arm64
env-runner: &env-runner
IS_ELECTRON_RUNNER: 1
env-arm64: &env-arm64
GN_EXTRA_ARGS: 'target_cpu = "arm64" fatal_linker_warnings = false enable_linux_installer = false'
MKSNAPSHOT_TOOLCHAIN: //build/toolchain/linux:clang_arm64
@@ -346,7 +343,7 @@ step-wait-for-goma: &step-wait-for-goma
sleep 5
done
echo "Goma ready"
no_output_timeout: 2m
no_output_timeout: 5m
step-restore-brew-cache: &step-restore-brew-cache
restore_cache:
@@ -498,6 +495,7 @@ step-install-signing-cert-on-mac: &step-install-signing-cert-on-mac
name: Import and trust self-signed codesigning cert on MacOS
command: |
if [ "$TARGET_ARCH" != "arm64" ] && [ "`uname`" == "Darwin" ]; then
sudo security authorizationdb write com.apple.trust-settings.admin allow
cd src/electron
./script/codesign/generate-identity.sh
fi
@@ -628,7 +626,7 @@ step-electron-publish: &step-electron-publish
echo 'Uploading Electron release distribution to Azure'
script/release/uploaders/upload.py --verbose --UPLOAD_TO_STORAGE
else
echo 'Uploading Electron release distribution to Github releases'
echo 'Uploading Electron release distribution to GitHub releases'
script/release/uploaders/upload.py --verbose
fi
@@ -997,6 +995,7 @@ steps-electron-gn-check: &steps-electron-gn-check
- *step-checkout-electron
- *step-depot-tools-get
- *step-depot-tools-add-to-path
- install-python2-mac
- *step-setup-env-for-build
- *step-setup-goma-for-build
- *step-generate-deps-hash
@@ -1101,6 +1100,7 @@ steps-tests: &steps-tests
- *step-setup-linux-for-headless-testing
- *step-restore-brew-cache
- *step-fix-known-hosts-linux
- install-python2-mac
- *step-install-signing-cert-on-mac
- run:
@@ -1190,6 +1190,31 @@ steps-test-node: &steps-test-node
# Command Aliases
commands:
install-python2-mac:
steps:
- restore_cache:
keys:
- v2.7.18-python-cache-{{ arch }}
name: Restore python cache
- run:
name: Install python2 on macos
command: |
if [ "`uname`" == "Darwin" ] && [ "$IS_ELECTRON_RUNNER" != "1" ]; then
if [ ! -f "python-downloads/python-2.7.18-macosx10.9.pkg" ]; then
mkdir python-downloads
echo 'Downloading Python 2.7.18'
curl -O https://dev-cdn.electronjs.org/python/python-2.7.18-macosx10.9.pkg
mv python-2.7.18-macosx10.9.pkg python-downloads
else
echo 'Using Python install from cache'
fi
sudo installer -pkg python-downloads/python-2.7.18-macosx10.9.pkg -target /
fi
- save_cache:
paths:
- python-downloads
key: v2.7.18-python-cache-{{ arch }}
name: Persisting python cache
maybe-restore-portaled-src-cache:
parameters:
halt-if-successful:
@@ -1357,6 +1382,7 @@ commands:
- run: rm -rf src/electron
- *step-restore-brew-cache
- *step-install-gnutar-on-mac
- install-python2-mac
- *step-save-brew-cache
- when:
condition: << parameters.build >>
@@ -1548,6 +1574,7 @@ commands:
- *step-depot-tools-get
- *step-depot-tools-add-to-path
- *step-restore-brew-cache
- install-python2-mac
- *step-get-more-space-on-mac
- when:
condition: << parameters.checkout >>
@@ -1607,7 +1634,7 @@ jobs:
name: linux-docker
size: medium
environment:
<<: *env-linux-medium
<<: *env-linux-2xlarge
<<: *env-testing-build
GCLIENT_EXTRA_ARGS: '--custom-var=checkout_arm=True --custom-var=checkout_arm64=True'
<<: *steps-electron-ts-compile-for-doc-change
@@ -2180,7 +2207,6 @@ jobs:
osx-testing-x64-tests:
executor:
name: macos
xcode: 12.4.0
size: macos.x86.medium.gen2
environment:
<<: *env-mac-large
@@ -2194,12 +2220,12 @@ jobs:
<<: *env-mac-large
<<: *env-stack-dumping
<<: *env-apple-silicon
<<: *env-runner
<<: *steps-tests
mas-testing-x64-tests:
executor:
name: macos
xcode: 12.4.0
size: macos.x86.medium.gen2
environment:
<<: *env-mac-large
@@ -2213,6 +2239,7 @@ jobs:
<<: *env-mac-large
<<: *env-stack-dumping
<<: *env-apple-silicon
<<: *env-runner
<<: *steps-tests
# Layer 4: Summary.

View File

@@ -0,0 +1,178 @@
name: Electron WOA Testing
on:
push:
branches: '**'
workflow_dispatch:
inputs:
appveyor_job_id:
description: 'Job Id of Appveyor WOA job to test'
type: text
required: true
jobs:
electron-woa-init:
if: ${{ github.event_name == 'push' && github.repository == 'electron/electron' }}
runs-on: ubuntu-latest
steps:
- name: Dummy step for push event
run: |
echo "This job is a needed initialization step for Electron WOA testing. Another test result will appear once the electron-woa-testing build is done."
electron-woa-testing:
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'electron/electron' }}
runs-on: [self-hosted, woa]
permissions:
checks: write
pull-requests: write
steps:
- uses: LouisBrunner/checks-action@v1.1.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: electron-woa-testing
status: in_progress
details_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
output: |
{"summary":"Test In Progress","text_description":"See job details here: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}
- name: Clean Workspace
run: |
Remove-Item * -Recurse -Force
shell: powershell
- name: Checkout
uses: actions/checkout@v3
with:
path: src\electron
fetch-depth: 0
- name: Yarn install
run: |
cd src\electron
node script/yarn.js install --frozen-lockfile
- name: Download and extract dist.zip for test
run: |
$localArtifactPath = "$pwd\dist.zip"
$serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/dist.zip"
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -osrc\out\Default -y $localArtifactPath
shell: powershell
- name: Download and extract native test executables for test
run: |
$localArtifactPath = "src\out\Default\shell_browser_ui_unittests.exe"
$serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/shell_browser_ui_unittests.exe"
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
shell: powershell
- name: Download and extract ffmpeg.zip for test
run: |
$localArtifactPath = "$pwd\ffmpeg.zip"
$serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/ffmpeg.zip"
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -osrc\out\ffmpeg $localArtifactPath
shell: powershell
- name: Download node headers for test
run: |
$localArtifactPath = "src\node_headers.zip"
$serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/node_headers.zip"
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
cd src
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -y node_headers.zip
shell: powershell
- name: Download electron.lib for test
run: |
$localArtifactPath = "src\out\Default\electron.lib"
$serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/electron.lib"
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
shell: powershell
# Uncomment the following block if pdb files are needed to debug issues
# - name: Download pdb files for detailed stacktraces
# if: ${{ github.event_name == 'workflow_dispatch' }}
# run: |
# try {
# $localArtifactPath = "src\pdb.zip"
# $serverArtifactPath = "https://ci.appveyor.com/api/buildjobs/${{ inputs.appveyor_job_id }}/artifacts/pdb.zip"
# Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer ${{ secrets.APPVEYOR_TOKEN }}" }
# cd src
# & "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -y pdb.zip
# } catch {
# Write-Host "There was an exception encountered while downloading pdb files:" $_.Exception.Message
# } finally {
# $global:LASTEXITCODE = 0
# }
# shell: powershell
- name: Setup node headers
run: |
New-Item src\out\Default\gen\node_headers\Release -Type directory
Copy-Item -path src\out\Default\electron.lib -destination src\out\Default\gen\node_headers\Release\node.lib
shell: powershell
- name: Run Electron Main process tests
run: |
cd src
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
set npm_config_arch=arm64
cd electron
node script/yarn test --runners=main --enable-logging --disable-features=CalculateNativeWinOcclusion
env:
ELECTRON_ENABLE_STACK_DUMPING: true
ELECTRON_OUT_DIR: Default
IGNORE_YARN_INSTALL_ERROR: 1
ELECTRON_TEST_RESULTS_DIR: junit
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
MOCHA_REPORTER: mocha-multi-reporters
ELECTRON_SKIP_NATIVE_MODULE_TESTS: true
- name: Run Electron Remote based tests
if: ${{ success() || failure() }}
run: |
cd src
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
set npm_config_arch=arm64
cd electron
node script/yarn test --runners=remote --enable-logging --disable-features=CalculateNativeWinOcclusion
env:
ELECTRON_OUT_DIR: Default
IGNORE_YARN_INSTALL_ERROR: 1
ELECTRON_TEST_RESULTS_DIR: junit
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
MOCHA_REPORTER: mocha-multi-reporters
ELECTRON_SKIP_NATIVE_MODULE_TESTS: true
- name: Verify ffmpeg
run: |
cd src
echo "Verifying non proprietary ffmpeg"
python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
shell: cmd
- name: Kill processes left running from last test run
if: ${{ always() }}
run: |
Get-Process | Where Name -Like "electron*" | Stop-Process
Get-Process | Where Name -Like "msedge*" | Stop-Process
shell: powershell
- name: Delete user app data directories
if: ${{ always() }}
run: |
Remove-Item -path $env:APPDATA/Electron* -Recurse -Force -ErrorAction Ignore
shell: powershell
- uses: LouisBrunner/checks-action@v1.1.1
if: ${{ success() }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: electron-woa-testing
conclusion: "${{ job.status }}"
details_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
output: |
{"summary":"${{ job.status }}","text_description":"See job details here: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}
- uses: LouisBrunner/checks-action@v1.1.1
if: ${{ success() }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: electron-woa-testing
conclusion: "${{ job.status }}"
details_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
output: |
{"summary":"Job Succeeded","text_description":"See job details here: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}
- uses: LouisBrunner/checks-action@v1.1.1
if: ${{ ! success() }}
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: electron-woa-testing
conclusion: "${{ job.status }}"
details_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
output: |
{"summary":"Job Failed","text_description":"See job details here: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}

View File

@@ -23,7 +23,5 @@
"br_spaces": 0
},
"single-h1": false,
"no-inline-html": {
"allowed_elements": ["br"]
}
"no-inline-html": false
}

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

View File

@@ -1 +1 @@
18.2.4
18.3.9

View File

@@ -38,16 +38,6 @@ environment:
MOCHA_REPORTER: mocha-multi-reporters
MOCHA_MULTI_REPORTERS: mocha-appveyor-reporter, tap
GOMA_FALLBACK_ON_AUTH_FAILURE: true
notifications:
- provider: Webhook
url: https://electron-mission-control.herokuapp.com/rest/appveyor-hook
method: POST
headers:
x-mission-control-secret:
secure: 90BLVPcqhJPG7d24v0q/RRray6W3wDQ8uVQlQjOHaBWkw1i8FoA1lsjr2C/v1dVok+tS2Pi6KxDctPUkwIb4T27u4RhvmcPzQhVpfwVJAG9oNtq+yKN7vzHfg7k/pojEzVdJpQLzeJGcSrZu7VY39Q==
on_build_success: false
on_build_failure: true
on_build_status_changed: false
build_script:
- ps: >-
if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
@@ -243,7 +233,7 @@ deploy_script:
& python script\release\uploaders\upload.py --verbose
}
} elseif (Test-Path Env:\TEST_WOA) {
node script/release/ci-release-build.js --job=electron-woa-testing --ci=VSTS --armTest --appveyorJobId=$env:APPVEYOR_JOB_ID $env:APPVEYOR_REPO_BRANCH
node script/release/ci-release-build.js --job=electron-woa-testing --ci=GHA --appveyorJobId=$env:APPVEYOR_JOB_ID $env:APPVEYOR_REPO_BRANCH
}
on_finish:
- if exist src\electron\electron.log ( appveyor-retry appveyor PushArtifact src\electron\electron.log )

View File

@@ -1,121 +0,0 @@
steps:
- task: CopyFiles@2
displayName: 'Copy Files to: src/electron'
inputs:
TargetFolder: src/electron
- bash: |
cd src/electron
node script/yarn.js install --frozen-lockfile
displayName: 'Yarn install'
- bash: |
export ZIP_DEST=$PWD/src/out/Default
echo "##vso[task.setvariable variable=ZIP_DEST]$ZIP_DEST"
mkdir -p $ZIP_DEST
cd src/electron
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=dist.zip --dest=$ZIP_DEST
cd $ZIP_DEST
unzip -o dist.zip
xattr -cr Electron.app
displayName: 'Download and unzip dist files for test'
env:
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
- bash: |
export FFMPEG_ZIP_DEST=$PWD/src/out/ffmpeg
mkdir -p $FFMPEG_ZIP_DEST
cd src/electron
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=ffmpeg.zip --dest=$FFMPEG_ZIP_DEST
cd $FFMPEG_ZIP_DEST
unzip -o ffmpeg.zip
displayName: 'Download and unzip ffmpeg for test'
env:
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
- bash: |
export NODE_HEADERS_DEST=$PWD/src/out/Default/gen
mkdir -p $NODE_HEADERS_DEST
cd src/electron
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=node_headers.tar.gz --dest=$NODE_HEADERS_DEST
cd $NODE_HEADERS_DEST
tar xzf node_headers.tar.gz
displayName: 'Download and untar node header files for test'
env:
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
- bash: |
export CROSS_ARCH_SNAPSHOTS=$PWD/src/out/Default/cross-arch-snapshots
mkdir -p $CROSS_ARCH_SNAPSHOTS
cd src/electron
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=cross-arch-snapshots/snapshot_blob.bin --dest=$CROSS_ARCH_SNAPSHOTS
node script/download-circleci-artifacts.js --buildNum=$CIRCLE_BUILD_NUM --name=cross-arch-snapshots/v8_context_snapshot.arm64.bin --dest=$CROSS_ARCH_SNAPSHOTS
displayName: 'Download cross arch snapshot files'
env:
CIRCLE_TOKEN: $(CIRCLECI_TOKEN)
- bash: |
cd src
export ELECTRON_OUT_DIR=Default
export npm_config_arch=arm64
(cd electron && node script/yarn test --enable-logging --runners main)
displayName: 'Run Electron main tests'
timeoutInMinutes: 20
env:
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
IGNORE_YARN_INSTALL_ERROR: 1
ELECTRON_TEST_RESULTS_DIR: junit
- bash: |
cd src
export ELECTRON_OUT_DIR=Default
export npm_config_arch=arm64
(cd electron && node script/yarn test --enable-logging --runners remote)
displayName: 'Run Electron remote tests'
timeoutInMinutes: 20
condition: succeededOrFailed()
env:
ELECTRON_DISABLE_SECURITY_WARNINGS: 1
IGNORE_YARN_INSTALL_ERROR: 1
ELECTRON_TEST_RESULTS_DIR: junit
- bash: |
cd src
python electron/script/verify-ffmpeg.py --source-root "$PWD" --build-dir out/Default --ffmpeg-path out/ffmpeg
displayName: Verify non proprietary ffmpeg
timeoutInMinutes: 5
condition: succeededOrFailed()
env:
TARGET_ARCH: arm64
- bash: |
cd src
echo Verify cross arch snapshot
python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default --snapshot-files-dir $PWD/out/Default/cross-arch-snapshots
displayName: Verify cross arch snapshot
timeoutInMinutes: 5
condition: succeededOrFailed()
- task: PublishTestResults@2
displayName: 'Publish Test Results'
inputs:
testResultsFiles: '*.xml'
searchFolder: '$(System.DefaultWorkingDirectory)/src/junit/'
condition: succeededOrFailed()
- bash: killall Electron || echo "No Electron processes left running"
displayName: 'Kill processes left running from last test run'
condition: always()
- bash: |
rm -rf ~/Library/Application\ Support/Electron*
rm -rf ~/Library/Application\ Support/electron*
displayName: 'Delete user app data directories'
condition: always()
- task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3
displayName: 'Clean Agent Directories'
condition: always()

View File

@@ -1,130 +0,0 @@
workspace:
clean: all
steps:
- checkout: self
path: src\electron
- script: |
node script/yarn.js install --frozen-lockfile
displayName: 'Yarn install'
- powershell: |
$localArtifactPath = "$pwd\dist.zip"
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/dist.zip"
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -o$(Pipeline.Workspace)\src\out\Default -y $localArtifactPath
displayName: 'Download and extract dist.zip for test'
env:
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
- powershell: |
$localArtifactPath = "$(Pipeline.Workspace)\src\out\Default\shell_browser_ui_unittests.exe"
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/shell_browser_ui_unittests.exe"
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
displayName: 'Download and extract native test executables for test'
env:
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
- powershell: |
$localArtifactPath = "$pwd\ffmpeg.zip"
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/ffmpeg.zip"
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -o$(Pipeline.Workspace)\src\out\ffmpeg $localArtifactPath
displayName: 'Download and extract ffmpeg.zip for test'
env:
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
- powershell: |
$localArtifactPath = "$(Pipeline.Workspace)\src\node_headers.zip"
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/node_headers.zip"
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
cd $(Pipeline.Workspace)\src
& "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -y node_headers.zip
displayName: 'Download node headers for test'
env:
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
- powershell: |
$localArtifactPath = "$(Pipeline.Workspace)\src\out\Default\electron.lib"
$serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/electron.lib"
Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
displayName: 'Download electron.lib for test'
env:
APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
# Uncomment the following block if pdb files are needed to debug issues
# - powershell: |
# try {
# $localArtifactPath = "$(Pipeline.Workspace)\src\pdb.zip"
# $serverArtifactPath = "$env:APPVEYOR_URL/buildjobs/$env:APPVEYOR_JOB_ID/artifacts/pdb.zip"
# Invoke-RestMethod -Method Get -Uri $serverArtifactPath -OutFile $localArtifactPath -Headers @{ "Authorization" = "Bearer $env:APPVEYOR_TOKEN" }
# cd $(Pipeline.Workspace)\src
# & "${env:ProgramFiles(x86)}\7-Zip\7z.exe" x -y pdb.zip
# } catch {
# Write-Host "There was an exception encountered while downloading pdb files:" $_.Exception.Message
# } finally {
# $global:LASTEXITCODE = 0
# }
# displayName: 'Download pdb files for detailed stacktraces'
# env:
# APPVEYOR_TOKEN: $(APPVEYOR_TOKEN)
- powershell: |
New-Item $(Pipeline.Workspace)\src\out\Default\gen\node_headers\Release -Type directory
Copy-Item -path $(Pipeline.Workspace)\src\out\Default\electron.lib -destination $(Pipeline.Workspace)\src\out\Default\gen\node_headers\Release\node.lib
displayName: 'Setup node headers'
- script: |
cd $(Pipeline.Workspace)\src
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
set npm_config_arch=arm64
cd electron
node script/yarn test --runners=main --enable-logging --disable-features=CalculateNativeWinOcclusion
displayName: 'Run Electron Main process tests'
env:
ELECTRON_ENABLE_STACK_DUMPING: true
ELECTRON_OUT_DIR: Default
IGNORE_YARN_INSTALL_ERROR: 1
ELECTRON_TEST_RESULTS_DIR: junit
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
MOCHA_REPORTER: mocha-multi-reporters
- script: |
cd $(Pipeline.Workspace)\src
set npm_config_nodedir=%cd%\out\Default\gen\node_headers
set npm_config_arch=arm64
cd electron
node script/yarn test --runners=remote --enable-logging --disable-features=CalculateNativeWinOcclusion
displayName: 'Run Electron Remote based tests'
env:
ELECTRON_OUT_DIR: Default
IGNORE_YARN_INSTALL_ERROR: 1
ELECTRON_TEST_RESULTS_DIR: junit
MOCHA_MULTI_REPORTERS: 'mocha-junit-reporter, tap'
MOCHA_REPORTER: mocha-multi-reporters
condition: succeededOrFailed()
- task: PublishTestResults@2
displayName: 'Publish Test Results'
inputs:
testResultsFiles: '*.xml'
searchFolder: '$(Pipeline.Workspace)/src/junit/'
condition: always()
- script: |
cd $(Pipeline.Workspace)\src
echo "Verifying non proprietary ffmpeg"
python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
displayName: 'Verify ffmpeg'
- powershell: |
Get-Process | Where Name Like "electron*" | Stop-Process
Get-Process | Where Name Like "msedge*" | Stop-Process
displayName: 'Kill processes left running from last test run'
condition: always()
- powershell: |
Remove-Item -path $env:APPDATA/Electron* -Recurse -Force -ErrorAction Ignore
displayName: 'Delete user app data directories'
condition: always()

View File

@@ -24,7 +24,11 @@ template("extract_symbols") {
assert(defined(invoker.binary), "Need binary to dump")
assert(defined(invoker.symbol_dir), "Need directory for symbol output")
dump_syms_label = "//third_party/breakpad:dump_syms($host_toolchain)"
if (host_os == "win" && target_cpu == "x86") {
dump_syms_label = "//third_party/breakpad:dump_syms(//build/toolchain/win:win_clang_x64)"
} else {
dump_syms_label = "//third_party/breakpad:dump_syms($host_toolchain)"
}
dump_syms_binary = get_label_info(dump_syms_label, "root_out_dir") +
"/dump_syms$_host_executable_suffix"

View File

@@ -70,9 +70,6 @@ an issue:
* [Windows Store](tutorial/windows-store-guide.md)
* [Snapcraft](tutorial/snapcraft.md)
* [Updates](tutorial/updates.md)
* [Deploying an Update Server](tutorial/updates.md#deploying-an-update-server)
* [Implementing Updates in Your App](tutorial/updates.md#implementing-updates-in-your-app)
* [Applying Updates](tutorial/updates.md#applying-updates)
* [Getting Support](tutorial/support.md)
## Detailed Tutorials

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

@@ -1638,6 +1638,8 @@ Opens the devtools.
When `contents` is a `<webview>` tag, the `mode` would be `detach` by default,
explicitly passing an empty `mode` can force using last used dock state.
On Windows, if Windows Control Overlay is enabled, Devtools will be opened with `mode: 'detach'`.
#### `contents.closeDevTools()`
Closes the devtools.

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

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
<p id="info"></p>
</body>
<script src="./renderer.js"></script>
</html>

View File

@@ -0,0 +1,26 @@
const { app, BrowserWindow } = require('electron');
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
});
win.loadFile('index.html');
};
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
<p id="info"></p>
</body>
<script src="./renderer.js"></script>
</html>

View File

@@ -0,0 +1,30 @@
const { app, BrowserWindow } = require('electron');
const path = require('path');
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
win.loadFile('index.html');
};
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});

View File

@@ -0,0 +1,7 @@
const { contextBridge } = require('electron');
contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron,
});

View File

@@ -0,0 +1,2 @@
const information = document.getElementById('info');
information.innerText = `This app is using Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

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

@@ -1,26 +1,26 @@
# Application Distribution
---
title: 'Application Packaging'
description: 'To distribute your app with Electron, you need to package and rebrand it. To do this, you can either use specialized tooling or manual approaches.'
slug: application-distribution
hide_title: false
---
## Overview
To distribute your app with Electron, you need to package and rebrand it.
To do this, you can either use specialized tooling or manual approaches.
To distribute your app with Electron, you need to package and rebrand it. To do this, you
can either use specialized tooling or manual approaches.
## With tooling
You can use the following tools to distribute your application:
There are a couple tools out there that exist to package and distribute your Electron app.
We recommend using [Electron Forge](https://www.electronforge.io). You can check out
its documentation directly, or refer to the [Packaging and Distribution](./tutorial-5-packaging.md)
part of the Electron tutorial.
* [electron-forge](https://github.com/electron-userland/electron-forge)
* [electron-builder](https://github.com/electron-userland/electron-builder)
* [electron-packager](https://github.com/electron/electron-packager)
## Manual packaging
These tools will take care of all the steps you need to take to end up with a
distributable Electron application, such as bundling your application,
rebranding the executable, and setting the right icons.
If you prefer the manual approach, there are 2 ways to distribute your application:
You can check the example of how to package your app with `electron-forge` in
the [Quick Start guide](quick-start.md#package-and-distribute-your-application).
## Manual distribution
- With prebuilt binaries
- With an app source code archive
### With prebuilt binaries
@@ -29,21 +29,19 @@ binaries](https://github.com/electron/electron/releases). Next, the folder
containing your app should be named `app` and placed in Electron's resources
directory as shown in the following examples.
> *NOTE:* the location of Electron's prebuilt binaries is indicated
:::note
The location of Electron's prebuilt binaries is indicated
with `electron/` in the examples below.
:::
*On macOS:*
```plaintext
```plain title='macOS'
electron/Electron.app/Contents/Resources/app/
├── package.json
├── main.js
└── index.html
```
*On Windows and Linux:*
```plaintext
```plain title='Windows and Linux'
electron/resources/app
├── package.json
├── main.js
@@ -54,7 +52,7 @@ Then execute `Electron.app` on macOS, `electron` on Linux, or `electron.exe`
on Windows, and Electron will start as your app. The `electron` directory
will then be your distribution to deliver to users.
### With an app source code archive
### With an app source code archive (asar)
Instead of shipping your app by copying all of its source files, you can
package your app into an [asar] archive to improve the performance of reading
@@ -65,16 +63,12 @@ To use an `asar` archive to replace the `app` folder, you need to rename the
archive to `app.asar`, and put it under Electron's resources directory like
below, and Electron will then try to read the archive and start from it.
*On macOS:*
```plaintext
```plain title='macOS'
electron/Electron.app/Contents/Resources/
└── app.asar
```
*On Windows and Linux:*
```plaintext
```plain title='Windows'
electron/resources/
└── app.asar
```
@@ -87,47 +81,44 @@ You can find more details on how to use `asar` in the
After bundling your app into Electron, you will want to rebrand Electron
before distributing it to users.
#### macOS
- **Windows:** You can rename `electron.exe` to any name you like, and edit
its icon and other information with tools like [rcedit](https://github.com/electron/rcedit).
- **Linux:** You can rename the `electron` executable to any name you like.
- **macOS:** You can rename `Electron.app` to any name you want, and you also have to rename
the `CFBundleDisplayName`, `CFBundleIdentifier` and `CFBundleName` fields in the
following files:
You can rename `Electron.app` to any name you want, and you also have to rename
the `CFBundleDisplayName`, `CFBundleIdentifier` and `CFBundleName` fields in the
following files:
- `Electron.app/Contents/Info.plist`
- `Electron.app/Contents/Frameworks/Electron Helper.app/Contents/Info.plist`
* `Electron.app/Contents/Info.plist`
* `Electron.app/Contents/Frameworks/Electron Helper.app/Contents/Info.plist`
You can also rename the helper app to avoid showing `Electron Helper` in the
Activity Monitor, but make sure you have renamed the helper app's executable
file's name.
You can also rename the helper app to avoid showing `Electron Helper` in the
Activity Monitor, but make sure you have renamed the helper app's executable
file's name.
The structure of a renamed app would be like:
The structure of a renamed app would be like:
```plaintext
```plain
MyApp.app/Contents
├── Info.plist
├── MacOS/
│   └── MyApp
└── MyApp
└── Frameworks/
└── MyApp Helper.app
├── Info.plist
└── MacOS/
   └── MyApp Helper
└── MyApp Helper
```
#### Windows
:::note
You can rename `electron.exe` to any name you like, and edit its icon and other
information with tools like [rcedit](https://github.com/electron/rcedit).
#### Linux
You can rename the `electron` executable to any name you like.
### Rebranding by rebuilding Electron from source
It is also possible to rebrand Electron by changing the product name and
it is also possible to rebrand Electron by changing the product name and
building it from source. To do this you need to set the build argument
corresponding to the product name (`electron_product_name = "YourProductName"`)
in the `args.gn` file and rebuild.
Keep in mind this is not recommended as setting up the environment to compile
from source is not trivial and takes significant time.
:::
[asar]: https://github.com/electron/asar

View File

@@ -1,14 +1,20 @@
# Code Signing
---
title: 'Code Signing'
description: 'Code signing is a security technology that you use to certify that an app was created by you.'
slug: code-signing
hide_title: false
---
Code signing is a security technology that you use to certify that an app was
created by you.
created by you. You should sign your application so it does not trigger any
operating system security checks.
On macOS the system can detect any change to the app, whether the change is
On macOS, the system can detect any change to the app, whether the change is
introduced accidentally or by malicious code.
On Windows, the system assigns a trust level to your code signing certificate
which if you don't have, or if your trust level is low, will cause security
dialogs to appear when users start using your application. Trust level builds
dialogs to appear when users start using your application. Trust level builds
over time so it's better to start code signing as early as possible.
While it is possible to distribute unsigned apps, it is not recommended. Both
@@ -16,20 +22,19 @@ Windows and macOS will, by default, prevent either the download or the execution
of unsigned applications. Starting with macOS Catalina (version 10.15), users
have to go through multiple manual steps to open unsigned applications.
![macOS Catalina Gatekeeper warning: The app cannot be opened because the
developer cannot be verified](../images/gatekeeper.png)
![macOS Catalina Gatekeeper warning: The app cannot be opened because the developer cannot be verified](../images/gatekeeper.png)
As you can see, users get two options: Move the app straight to the trash or
cancel running it. You don't want your users to see that dialog.
If you are building an Electron app that you intend to package and distribute,
it should be code-signed.
it should be code signed.
# Signing & notarizing macOS builds
## Signing & notarizing macOS builds
Properly preparing macOS applications for release requires two steps: First, the
app needs to be code-signed. Then, the app needs to be uploaded to Apple for a
process called "notarization", where automated systems will further verify that
Properly preparing macOS applications for release requires two steps. First, the
app needs to be code signed. Then, the app needs to be uploaded to Apple for a
process called **notarization**, where automated systems will further verify that
your app isn't doing anything to endanger its users.
To start the process, ensure that you fulfill the requirements for signing and
@@ -42,18 +47,18 @@ notarizing your app:
Electron's ecosystem favors configuration and freedom, so there are multiple
ways to get your application signed and notarized.
## `electron-forge`
### Using Electron Forge
If you're using Electron's favorite build tool, getting your application signed
and notarized requires a few additions to your configuration. [Forge](https://electronforge.io) is a
collection of the official Electron tools, using [`electron-packager`],
[`electron-osx-sign`], and [`electron-notarize`] under the hood.
Let's take a look at an example configuration with all required fields. Not all
of them are required: the tools will be clever enough to automatically find a
suitable `identity`, for instance, but we recommend that you are explicit.
Let's take a look at an example `package.json` configuration with all required fields. Not all of them are
required: the tools will be clever enough to automatically find a suitable `identity`, for instance,
but we recommend that you are explicit.
```json
```json title="package.json" {7}
{
"name": "my-app",
"version": "0.0.1",
@@ -69,7 +74,7 @@ suitable `identity`, for instance, but we recommend that you are explicit.
},
"osxNotarize": {
"appleId": "felix@felix.fun",
"appleIdPassword": "my-apple-id-password",
"appleIdPassword": "my-apple-id-password"
}
}
}
@@ -77,11 +82,11 @@ suitable `identity`, for instance, but we recommend that you are explicit.
}
```
The `plist` file referenced here needs the following macOS-specific entitlements
The `entitlements.plist` file referenced here needs the following macOS-specific entitlements
to assure the Apple security mechanisms that your app is doing these things
without meaning any harm:
```xml
```xml title="entitlements.plist"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
@@ -104,7 +109,7 @@ file](https://github.com/electron/fiddle/blob/master/forge.config.js).
If you plan to access the microphone or camera within your app using Electron's APIs, you'll also
need to add the following entitlements:
```xml
```xml title="entitlements.plist"
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
@@ -113,28 +118,26 @@ need to add the following entitlements:
If these are not present in your app's entitlements when you invoke, for example:
```js
```js title="main.js"
const { systemPreferences } = require('electron')
const microphone = systemPreferences.askForMediaAccess('microphone')
```
Your app may crash. See the Resource Access section in [Hardened Runtime](https://developer.apple.com/documentation/security/hardened_runtime) for more information and entitlements you may need.
## `electron-builder`
### Using Electron Builder
Electron Builder comes with a custom solution for signing your application. You
can find [its documentation here](https://www.electron.build/code-signing).
## `electron-packager`
### Using Electron Packager
If you're not using an integrated build pipeline like Forge or Builder, you
are likely using [`electron-packager`], which includes [`electron-osx-sign`] and
[`electron-notarize`].
If you're using Packager's API, you can pass [in configuration that both signs
and notarizes your
application](https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html).
and notarizes your application](https://electron.github.io/electron-packager/main/interfaces/electronpackager.options.html).
```js
const packager = require('electron-packager')
@@ -155,11 +158,11 @@ packager({
})
```
The `plist` file referenced here needs the following macOS-specific entitlements
The `entitlements.plist` file referenced here needs the following macOS-specific entitlements
to assure the Apple security mechanisms that your app is doing these things
without meaning any harm:
```xml
```xml title="entitlements.plist"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
@@ -175,11 +178,11 @@ without meaning any harm:
Up until Electron 12, the `com.apple.security.cs.allow-unsigned-executable-memory` entitlement was required
as well. However, it should not be used anymore if it can be avoided.
## Mac App Store
### Signing Mac App Store applications
See the [Mac App Store Guide].
# Signing Windows builds
## Signing Windows builds
Before signing Windows builds, you must do the following:
@@ -190,31 +193,140 @@ Before signing Windows builds, you must do the following:
You can get a code signing certificate from a lot of resellers. Prices vary, so
it may be worth your time to shop around. Popular resellers include:
* [digicert](https://www.digicert.com/code-signing/microsoft-authenticode.htm)
* [Sectigo](https://sectigo.com/ssl-certificates-tls/code-signing)
* Amongst others, please shop around to find one that suits your needs, Google
is your friend 😄
- [digicert](https://www.digicert.com/code-signing/microsoft-authenticode.htm)
- [Sectigo](https://sectigo.com/ssl-certificates-tls/code-signing)
- Amongst others, please shop around to find one that suits your needs! 😄
There are a number of tools for signing your packaged app:
:::caution Keep your certificate password private
Your certificate password should be a **secret**. Do not share it publicly or
commit it to your source code.
:::
* [`electron-winstaller`] will generate an installer for windows and sign it for
you
* [`electron-forge`] can sign installers it generates through the
Squirrel.Windows or MSI targets.
* [`electron-builder`] can sign some of its windows targets
### Using Electron Forge
## Windows Store
Once you have a code signing certificate file (`.pfx`), you can sign
[Squirrel.Windows][maker-squirrel] and [MSI][maker-msi] installers in Electron Forge
with the `certificateFile` and `certificatePassword` fields in their respective
configuration objects.
For example, if you keep your Forge config in your `package.json` file and are
creating a Squirrel.Windows installer:
```json {9-15} title='package.json'
{
"name": "my-app",
"version": "0.0.1",
//...
"config": {
"forge": {
"packagerConfig": {},
"makers": [
{
"name": "@electron-forge/maker-squirrel",
"config": {
"certificateFile": "./cert.pfx",
"certificatePassword": "this-is-a-secret"
}
}
]
}
}
//...
}
```
### Using electron-winstaller (Squirrel.Windows)
[`electron-winstaller`] is a package that can generate Squirrel.Windows installers for your
Electron app. This is the tool used under the hood by Electron Forge's
[Squirrel.Windows Maker][maker-squirrel]. If you're not using Electron Forge and want to use
`electron-winstaller` directly, use the `certificateFile` and `certificatePassword` configuration
options when creating your installer.
```js {10-11}
const electronInstaller = require('electron-winstaller')
// NB: Use this syntax within an async function, Node does not have support for
// top-level await as of Node 12.
try {
await electronInstaller.createWindowsInstaller({
appDirectory: '/tmp/build/my-app-64',
outputDirectory: '/tmp/build/installer64',
authors: 'My App Inc.',
exe: 'myapp.exe',
certificateFile: './cert.pfx',
certificatePassword: 'this-is-a-secret',
})
console.log('It worked!')
} catch (e) {
console.log(`No dice: ${e.message}`)
}
```
For full configuration options, check out the [`electron-winstaller`] repository!
### Using electron-wix-msi (WiX MSI)
[`electron-wix-msi`] is a package that can generate MSI installers for your
Electron app. This is the tool used under the hood by Electron Forge's [MSI Maker][maker-msi].
If you're not using Electron Forge and want to use `electron-wix-msi` directly, use the
`certificateFile` and `certificatePassword` configuration options
or pass in parameters directly to [SignTool.exe] with the `signWithParams` option.
```js {12-13}
import { MSICreator } from 'electron-wix-msi'
// Step 1: Instantiate the MSICreator
const msiCreator = new MSICreator({
appDirectory: '/path/to/built/app',
description: 'My amazing Kitten simulator',
exe: 'kittens',
name: 'Kittens',
manufacturer: 'Kitten Technologies',
version: '1.1.2',
outputDirectory: '/path/to/output/folder',
certificateFile: './cert.pfx',
certificatePassword: 'this-is-a-secret',
})
// Step 2: Create a .wxs template file
const supportBinaries = await msiCreator.create()
// 🆕 Step 2a: optionally sign support binaries if you
// sign you binaries as part of of your packaging script
supportBinaries.forEach(async (binary) => {
// Binaries are the new stub executable and optionally
// the Squirrel auto updater.
await signFile(binary)
})
// Step 3: Compile the template to a .msi file
await msiCreator.compile()
```
For full configuration options, check out the [`electron-wix-msi`] repository!
### Using Electron Builder
Electron Builder comes with a custom solution for signing your application. You
can find [its documentation here](https://www.electron.build/code-signing).
### Signing Windows Store applications
See the [Windows Store Guide].
[Apple Developer Program]: https://developer.apple.com/programs/
[apple developer program]: https://developer.apple.com/programs/
[`electron-builder`]: https://github.com/electron-userland/electron-builder
[`electron-forge`]: https://github.com/electron-userland/electron-forge
[`electron-osx-sign`]: https://github.com/electron-userland/electron-osx-sign
[`electron-packager`]: https://github.com/electron/electron-packager
[`electron-notarize`]: https://github.com/electron/electron-notarize
[`electron-winstaller`]: https://github.com/electron/windows-installer
[Xcode]: https://developer.apple.com/xcode
[`electron-wix-msi`]: https://github.com/felixrieseberg/electron-wix-msi
[xcode]: https://developer.apple.com/xcode
[signing certificates]: https://github.com/electron/electron-osx-sign/wiki/1.-Getting-Started#certificates
[Mac App Store Guide]: mac-app-store-submission-guide.md
[Windows Store Guide]: windows-store-guide.md
[mac app store guide]: ./mac-app-store-submission-guide.md
[windows store guide]: ./windows-store-guide.md
[maker-squirrel]: https://www.electronforge.io/config/makers/squirrel.windows
[maker-msi]: https://www.electronforge.io/config/makers/wix-msi
[signtool.exe]: https://docs.microsoft.com/en-us/dotnet/framework/tools/signtool-exe

View File

@@ -0,0 +1,54 @@
---
title: 'Distribution Overview'
description: 'To distribute your app with Electron, you need to package and rebrand it. To do this, you can either use specialized tooling or manual approaches.'
slug: distribution-overview
hide_title: false
---
Once your app is ready for production, there are a couple steps you need to take before
you can deliver it to your users.
## Packaging
To distribute your app with Electron, you need to package all your resources and assets
into an executable and rebrand it. To do this, you can either use specialized tooling
or do it manually. See the [Application Packaging][application-packaging] tutorial
for more information.
## Code signing
Code signing is a security technology that you use to certify that an app was
created by you. You should sign your application so it does not trigger the
security checks of your user's operating system.
To get started with each operating system's code signing process, please read the
[Code Signing][code-signing] docs.
## Publishing
Once your app is packaged and signed, you can freely distribute your app directly
to users by uploading your installers online.
To reach more users, you can also choose to upload your app to each operating system's
digital distribution platform (i.e. app store). These require another build step aside
from your direct download app. For more information, check out each individual app store guide:
- [Mac App Store][mac-app]
- [Windows Store][windows-store]
- [Snapcraft (Linux)][snapcraft]
## Updating
Electron's auto-updater allows you to deliver application updates to users
without forcing them to manually download new versions of your application.
Check out the [Updating Applications][updates] guide for details on implementing automatic updates
with Electron.
<!-- Link labels -->
[application-packaging]: ./application-distribution.md
[code-signing]: ./code-signing.md
[mac-app]: ./mac-app-store-submission-guide.md
[windows-store]: ./windows-store-guide.md
[snapcraft]: ./snapcraft.md
[updates]: ./updates.md

56
docs/tutorial/examples.md Normal file
View File

@@ -0,0 +1,56 @@
---
title: 'Examples Overview'
description: 'A set of examples for common Electron features'
slug: examples
hide_title: false
---
# Examples Overview
In this section, we have collected a set of guides for common features
that you may want to implement in your Electron application. Each guide
contains a practical example in a minimal, self-contained example app.
The easiest way to run these examples is by downloading [Electron Fiddle][fiddle].
Once Fiddle is installed, you can press on the "Open in Fiddle" button that you
will find below code samples like the following one:
```fiddle docs/fiddles/quick-start
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
```
If there is still something that you do not know how to do, please take a look at the [API][app]
as there is a chance it might be documented just there (and also open an issue requesting the
guide!).
<!-- guide-table-start -->
| Guide | Description |
| :-------------------- | ------------------------------------------------------------------------------------------------------------------- |
| [Message ports] | This guide provides some examples of how you might use MessagePorts in your app to communicate different processes. |
| [Device access] | Learn how to access the device hardware (Bluetooth, USB, Serial). |
| [Keyboard shortcuts] | Configure local and global keyboard shortcuts for your Electron application. |
| [Multithreading] | With Web Workers, it is possible to run JavaScript in OS-level threads |
| [Offscreen rendering] | Offscreen rendering lets you obtain the content of a BrowserWindow in a bitmap, so it can be rendered anywhere. |
| [Spellchecker] | Learn how to use the built-in spellchecker, set languages, etc. |
| [Web embeds] | Discover the different ways to embed third-party web content in your application. |
<!-- guide-table-end -->
## How to...?
You can find the full list of "How to?" in the sidebar. If there is
something that you would like to do that is not documented, please join
our [Discord server][] and let us know!
[discord server]: https://discord.com/invite/electron
[fiddle]: https://www.electronjs.org/fiddle

View File

@@ -1,10 +1,11 @@
# Introduction
---
title: 'Introduction'
description: 'Welcome to the Electron documentation! If this is your first time developing an Electron app, read through this Getting Started section to get familiar with the basics. Otherwise, feel free to explore our guides and API documentation!'
slug: /latest/
hide_title: false
---
Welcome to the Electron documentation! If this is your first time developing
an Electron app, read through this Getting Started section to get familiar with the
basics. Otherwise, feel free to explore our guides and API documentation!
## What is Electron?
# What is Electron?
Electron is a framework for building desktop applications using JavaScript,
HTML, and CSS. By embedding [Chromium][chromium] and [Node.js][node] into its
@@ -12,20 +13,12 @@ binary, Electron allows you to maintain one JavaScript codebase and create
cross-platform apps that work on Windows, macOS, and Linux — no native development
experience required.
## Prerequisites
## Getting started
These docs operate under the assumption that the reader is familiar with both
Node.js and general web development. If you need to get more comfortable with
either of these areas, we recommend the following resources:
* [Getting started with the Web (MDN)][mdn-guide]
* [Introduction to Node.js][node-guide]
Moreover, you'll have a better time understanding how Electron works if you get
acquainted with Chromium's process model. You can get a brief overview of
Chrome architecture with the [Chrome comic][comic], which was released alongside
Chrome's launch back in 2008. Although it's been over a decade since then, the
core principles introduced in the comic remain helpful to understand Electron.
We recommend you to start with the [tutorial], which guides you through the
process of developing an Electron app and distributing it to users.
The [examples] and [API documentation] are also good places to browse around
and discover new things.
## Running examples with Electron Fiddle
@@ -39,21 +32,44 @@ a code block. If you have Fiddle installed, this button will open a
`fiddle.electronjs.org` link that will automatically load the example into Fiddle,
no copy-pasting required.
```fiddle docs/fiddles/quick-start
```
## What is in the docs?
All the official documentation is available from the sidebar. These
are the different categories and what you can expect on each one:
- **Tutorial**: An end-to-end guide on how to create and publish your first Electron
application.
- **Processes in Electron**: In-depth reference on Electron processes and how to work with them.
- **Best Practices**: Important checklists to keep in mind when developing an Electron app.
- **How-To Examples**: Quick references to add features to your Electron app.
- **Development**: Miscellaneous development guides.
- **Distribution**: Learn how to distribute your app to end users.
- **Testing and debugging**: How to debug JavaScript, write tests, and other tools used
to create quality Electron applications.
- **Resources**: Useful links to better understand how the Electron project works
and is organized.
- **Contributing to Electron**: Compiling Electron and making contributions can be daunting.
We try to make it easier in this section.
## Getting help
Are you getting stuck anywhere? Here are a few links to places to look:
* If you need help with developing your app, our [community Discord server][discord]
is a great place to get advice from other Electron app developers.
* If you suspect you're running into a bug with the `electron` package, please check
the [GitHub issue tracker][issue-tracker] to see if any existing issues match your
problem. If not, feel free to fill out our bug report template and submit a new issue.
- If you need help with developing your app, our [community Discord server][discord]
is a great place to get advice from other Electron app developers.
- If you suspect you're running into a bug with the `electron` package, please check
the [GitHub issue tracker][issue-tracker] to see if any existing issues match your
problem. If not, feel free to fill out our bug report template and submit a new issue.
<!-- Links -->
[api documentation]: ../api/app.md
[chromium]: https://www.chromium.org/
[node]: https://nodejs.org/
[mdn-guide]: https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web
[node-guide]: https://nodejs.dev/learn
[comic]: https://www.google.com/googlebooks/chrome/
[discord]: https://discord.com/invite/APGC3k5yaH
[examples]: examples.md
[fiddle]: https://electronjs.org/fiddle
[issue-tracker]: https://github.com/electron/electron/issues
[discord]: https://discord.gg/electronjs
[node]: https://nodejs.org/

View File

@@ -8,8 +8,7 @@ your app.
Here is a very brief example of what a MessagePort is and how it works:
```js
// renderer.js ///////////////////////////////////////////////////////////////
```js title='renderer.js (Renderer Process)'
// MessagePorts are created in pairs. A connected pair of message ports is
// called a channel.
const channel = new MessageChannel()
@@ -28,8 +27,7 @@ port2.postMessage({ answer: 42 })
ipcRenderer.postMessage('port', null, [port1])
```
```js
// main.js ///////////////////////////////////////////////////////////////////
```js title='main.js (Main Process)'
// In the main process, we receive the port.
ipcMain.on('port', (event) => {
// When we receive a MessagePort in the main process, it becomes a
@@ -84,14 +82,84 @@ process, you can listen for the `close` event by calling `port.on('close',
## Example use cases
### Setting up a MessageChannel between two renderers
In this example, the main process sets up a MessageChannel, then sends each port
to a different renderer. This allows renderers to send messages to each other
without needing to use the main process as an in-between.
```js title='main.js (Main Process)'
const { BrowserWindow, app, MessageChannelMain } = require('electron')
app.whenReady().then(async () => {
// create the windows.
const mainWindow = new BrowserWindow({
show: false,
webPreferences: {
contextIsolation: false,
preload: 'preloadMain.js'
}
})
const secondaryWindow = BrowserWindow({
show: false,
webPreferences: {
contextIsolation: false,
preload: 'preloadSecondary.js'
}
})
// set up the channel.
const { port1, port2 } = new MessageChannelMain()
// once the webContents are ready, send a port to each webContents with postMessage.
mainWindow.once('ready-to-show', () => {
mainWindow.webContents.postMessage('port', null, [port1])
})
secondaryWindow.once('ready-to-show', () => {
secondaryWindow.webContents.postMessage('port', null, [port2])
})
})
```
Then, in your preload scripts you receive the port through IPC and set up the
listeners.
```js title='preloadMain.js and preloadSecondary.js (Preload scripts)'
const { ipcRenderer } = require('electron')
ipcRenderer.on('port', e => {
// port received, make it globally available.
window.electronMessagePort = e.ports[0]
window.electronMessagePort.onmessage = messageEvent => {
// handle message
}
})
```
In this example messagePort is bound to the `window` object directly. It is better
to use `contextIsolation` and set up specific contextBridge calls for each of your
expected messages, but for the simplicity of this example we don't. You can find an
example of context isolation further down this page at [Communicating directly between the main process and the main world of a context-isolated page](#communicating-directly-between-the-main-process-and-the-main-world-of-a-context-isolated-page)
That means window.messagePort is globally available and you can call
`postMessage` on it from anywhere in your app to send a message to the other
renderer.
```js title='renderer.js (Renderer Process)'
// elsewhere in your code to send a message to the other renderers message handler
window.electronMessagePort.postmessage('ping')
```
### Worker process
In this example, your app has a worker process implemented as a hidden window.
You want the app page to be able to communicate directly with the worker
process, without the performance overhead of relaying via the main process.
```js
// main.js ///////////////////////////////////////////////////////////////////
```js title='main.js (Main Process)'
const { BrowserWindow, app, ipcMain, MessageChannelMain } = require('electron')
app.whenReady().then(async () => {
@@ -129,8 +197,7 @@ app.whenReady().then(async () => {
})
```
```html
<!-- worker.html ------------------------------------------------------------>
```html title='worker.html'
<script>
const { ipcRenderer } = require('electron')
@@ -153,8 +220,7 @@ ipcRenderer.on('new-client', (event) => {
</script>
```
```html
<!-- app.html --------------------------------------------------------------->
```html title='app.html'
<script>
const { ipcRenderer } = require('electron')
@@ -182,9 +248,7 @@ Electron's built-in IPC methods only support two modes: fire-and-forget
can implement a "response stream", where a single request responds with a
stream of data.
```js
// renderer.js ///////////////////////////////////////////////////////////////
```js title='renderer.js (Renderer Process)'
const makeStreamingRequest = (element, callback) => {
// MessageChannels are lightweight--it's cheap to create a new one for each
// request.
@@ -213,9 +277,7 @@ makeStreamingRequest(42, (data) => {
// We will see "got response data: 42" 10 times.
```
```js
// main.js ///////////////////////////////////////////////////////////////////
```js title='main.js (Main Process)'
ipcMain.on('give-me-a-stream', (event, msg) => {
// The renderer has sent us a MessagePort that it wants us to send our
// response over.
@@ -242,8 +304,7 @@ the renderer are delivered to the isolated world, rather than to the main
world. Sometimes you want to deliver messages to the main world directly,
without having to step through the isolated world.
```js
// main.js ///////////////////////////////////////////////////////////////////
```js title='main.js (Main Process)'
const { BrowserWindow, app, MessageChannelMain } = require('electron')
const path = require('path')
@@ -278,8 +339,7 @@ app.whenReady().then(async () => {
})
```
```js
// preload.js ////////////////////////////////////////////////////////////////
```js title='preload.js (Preload Script)'
const { ipcRenderer } = require('electron')
// We need to wait until the main world is ready to receive the message before
@@ -297,8 +357,7 @@ ipcRenderer.on('main-world-port', async (event) => {
})
```
```html
<!-- index.html ------------------------------------------------------------->
```html title='index.html'
<script>
window.onmessage = (event) => {
// event.source === window means the message is coming from the preload

View File

@@ -1,10 +1,17 @@
---
title: 'Process Model'
description: 'Electron inherits its multi-process architecture from Chromium, which makes the framework architecturally very similar to a modern web browser. This guide will expand on the concepts applied in the tutorial.'
slug: process-model
hide_title: false
---
# Process Model
Electron inherits its multi-process architecture from Chromium, which makes the framework
architecturally very similar to a modern web browser. In this guide, we'll expound on
the conceptual knowledge of Electron that we applied in the minimal [quick start app][].
architecturally very similar to a modern web browser. This guide will expand on the
concepts applied in the [Tutorial][tutorial].
[quick start app]: ./quick-start.md
[tutorial]: ./tutorial-1-prerequisites.md
## Why not a single process?
@@ -27,10 +34,10 @@ visualizes this model:
![Chrome's multi-process architecture](../images/chrome-processes.png)
Electron applications are structured very similarly. As an app developer, you control
two types of processes: main and renderer. These are analogous to Chrome's own browser
and renderer processes outlined above.
two types of processes: [main](#the-main-process) and [renderer](#the-renderer-process).
These are analogous to Chrome's own browser and renderer processes outlined above.
[Chrome Comic]: https://www.google.com/googlebooks/chrome/
[chrome comic]: https://www.google.com/googlebooks/chrome/
## The main process
@@ -68,7 +75,7 @@ When a `BrowserWindow` instance is destroyed, its corresponding renderer process
terminated as well.
[browser-window]: ../api/browser-window.md
[web-embed]: ./web-embeds.md
[web-embed]: ../tutorial/web-embeds.md
[web-contents]: ../api/web-contents.md
[event-emitter]: https://nodejs.org/api/events.html#events_class_eventemitter
@@ -90,7 +97,7 @@ app.on('window-all-closed', () => {
```
[app]: ../api/app.md
[quick-start-lifecycle]: ./quick-start.md#manage-your-windows-lifecycle
[quick-start-lifecycle]: ../tutorial/quick-start.md#manage-your-windows-lifecycle
### Native APIs
@@ -105,7 +112,7 @@ For a full list of Electron's main process modules, check out our API documentat
Each Electron app spawns a separate renderer process for each open `BrowserWindow`
(and each web embed). As its name implies, a renderer is responsible for
*rendering* web content. For all intents and purposes, code ran in renderer processes
_rendering_ web content. For all intents and purposes, code ran in renderer processes
should behave according to web standards (insofar as Chromium does, at least).
Therefore, all user interfaces and app functionality within a single browser
@@ -115,18 +122,22 @@ web.
Although explaining every web spec is out of scope for this guide, the bare minimum
to understand is:
* An HTML file is your entry point for the renderer process.
* UI styling is added through Cascading Style Sheets (CSS).
* Executable JavaScript code can be added through `<script>` elements.
- An HTML file is your entry point for the renderer process.
- UI styling is added through Cascading Style Sheets (CSS).
- Executable JavaScript code can be added through `<script>` elements.
Moreover, this also means that the renderer has no direct access to `require`
or other Node.js APIs. In order to directly include NPM modules in the renderer,
you must use the same bundler toolchains (for example, `webpack` or `parcel`) that you
use on the web.
> Note: Renderer processes can be spawned with a full Node.js environment for ease of
> development. Historically, this used to be the default, but this feature was disabled
> for security reasons.
:::warning
Renderer processes can be spawned with a full Node.js environment for ease of
development. Historically, this used to be the default, but this feature was disabled
for security reasons.
:::
At this point, you might be wondering how your renderer process user interfaces
can interact with Node.js and Electron's native desktop functionality if these
@@ -135,8 +146,9 @@ way to import Electron's content scripts.
## Preload scripts
<!-- Note: This guide doesn't take sandboxing into account, which might fundamentally
<!-- Note: This guide doesn't take sandboxing into account, which might fundamentally
change the statements here. -->
Preload scripts contain code that executes in a renderer process before its web content
begins loading. These scripts run within the renderer context, but are granted more
privileges by having access to Node.js APIs.
@@ -149,8 +161,8 @@ const { BrowserWindow } = require('electron')
//...
const win = new BrowserWindow({
webPreferences: {
preload: 'path/to/preload.js'
}
preload: 'path/to/preload.js',
},
})
//...
```
@@ -165,7 +177,7 @@ the [`contextIsolation`][context-isolation] default.
```js title='preload.js'
window.myAPI = {
desktop: true
desktop: true,
}
```
@@ -184,7 +196,7 @@ securely:
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('myAPI', {
desktop: true
desktop: true,
})
```
@@ -195,14 +207,15 @@ console.log(window.myAPI)
This feature is incredibly useful for two main purposes:
* By exposing [`ipcRenderer`][ipcRenderer] helpers to the renderer, you can use
- By exposing [`ipcRenderer`][ipcrenderer] helpers to the renderer, you can use
inter-process communication (IPC) to trigger main process tasks from the
renderer (and vice-versa).
* If you're developing an Electron wrapper for an existing web app hosted on a remote
- If you're developing an Electron wrapper for an existing web app hosted on a remote
URL, you can add custom properties onto the renderer's `window` global that can
be used for desktop-only logic on the web client's side.
[window-mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window
[context-isolation]: ./context-isolation.md
[context-bridge]: ../api/context-bridge.md
[ipcRenderer]: ../api/ipc-renderer.md
[ipcrenderer]: ../api/ipc-renderer.md
[tutorial]: ./tutorial-1-prerequisites.md

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

@@ -0,0 +1,143 @@
---
title: 'Prerequisites'
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.'
slug: tutorial-prerequisites
hide_title: false
---
:::info Follow along the tutorial
This is **part 1** of the Electron tutorial.
1. **[Prerequisites][prerequisites]**
1. [Building your First App][building your first app]
1. [Using Preload Scripts][preload]
1. [Adding Features][features]
1. [Packaging Your Application][packaging]
1. [Publishing and Updating][updates]
:::
Electron is a framework for building desktop applications using JavaScript,
HTML, and CSS. By embedding [Chromium][chromium] and [Node.js][node] into a
single binary file, Electron allows you to create cross-platform apps that
work on Windows, macOS, and Linux with a single JavaScript codebase.
This tutorial will guide you through the process of developing a desktop
application with Electron and distributing it to end users.
## Assumptions
Electron is a native wrapper layer for web apps and is run in a Node.js environment.
Therefore, this tutorial assumes you are generally familiar with Node and
front-end web development basics. If you need to do some background reading before
continuing, we recommend the following resources:
- [Getting started with the Web (MDN Web Docs)][mdn-guide]
- [Introduction to Node.js][node-guide]
## Required tools
### Code editor
You will need a text editor to write your code. We recommend using [Visual Studio Code],
although you can choose whichever one you prefer.
### Command line
Throughout the tutorial, we will ask you to use various command-line interfaces (CLIs). You can
type these commands into your system's default terminal:
- Windows: Command Prompt or PowerShell
- macOS: Terminal
- Linux: varies depending on distribution (e.g. GNOME Terminal, Konsole)
Most code editors also come with an integrated terminal, which you can also use.
### Git and GitHub
Git is a commonly-used version control system for source code, and GitHub is a collaborative
development platform built on top of it. Although neither is strictly necessary to building
an Electron application, we will use GitHub releases to set up automatic updates later
on in the tutorial. Therefore, we'll require you to:
- [Create a GitHub account](https://github.com/join)
- [Install Git](https://github.com/git-guides/install-git)
If you're unfamiliar with how Git works, we recommend reading GitHub's [Git guides]. You can also
use the [GitHub Desktop] app if you prefer using a visual interface over the command line.
We recommend that you create a local Git repository and publish it to GitHub before starting
the tutorial, and commit your code after every step.
:::info Installing Git via GitHub Desktop
GitHub Desktop will install the latest version of Git on your system if you don't already have
it installed.
:::
### Node.js and npm
To begin developing an Electron app, you need to install the [Node.js][node-download]
runtime and its bundled npm package manager onto your system. We recommend that you
use the latest long-term support (LTS) version.
:::tip
Please install Node.js using pre-built installers for your platform.
You may encounter incompatibility issues with different development tools otherwise.
If you are using macOS, we recommend using a package manager like [Homebrew] or
[nvm] to avoid any directory permission issues.
:::
To check that Node.js was installed correctly, you can use the `-v` flag when
running the `node` and `npm` commands. These should print out the installed
versions.
```sh
$ node -v
v16.14.2
$ npm -v
8.7.0
```
:::caution
Although you need Node.js installed locally to scaffold an Electron project,
Electron **does not use your system's Node.js installation to run its code**. Instead, it
comes bundled with its own Node.js runtime. This means that your end users do not
need to install Node.js themselves as a prerequisite to running your app.
To check which version of Node.js is running in your app, you can access the global
[`process.versions`] variable in the main process or preload script. You can also reference
the list of versions in the [electron/releases] repository.
:::
<!-- Links -->
[chromium]: https://www.chromium.org/
[electron/releases]: https://github.com/electron/releases/blob/master/readme.md#releases
[homebrew]: https://brew.sh/
[mdn-guide]: https://developer.mozilla.org/en-US/docs/Learn/
[node]: https://nodejs.org/
[node-guide]: https://nodejs.dev/learn
[node-download]: https://nodejs.org/en/download/
[nvm]: https://github.com/nvm-sh/nvm
[process-model]: ./process-model.md
[`process.versions`]: https://nodejs.org/api/process.html#processversions
[github]: https://github.com/
[git guides]: https://github.com/git-guides/
[github desktop]: https://desktop.github.com/
[visual studio code]: https://code.visualstudio.com/
<!-- Tutorial links -->
[prerequisites]: tutorial-1-prerequisites.md
[building your first app]: tutorial-2-first-app.md
[preload]: tutorial-3-preload.md
[features]: tutorial-4-adding-features.md
[packaging]: tutorial-5-packaging.md
[updates]: tutorial-6-publishing-updating.md

View File

@@ -0,0 +1,480 @@
---
title: 'Building your First App'
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.'
slug: tutorial-first-app
hide_title: false
---
:::info Follow along the tutorial
This is **part 2** of the Electron tutorial.
1. [Prerequisites][prerequisites]
1. **[Building your First App][building your first app]**
1. [Using Preload Scripts][preload]
1. [Adding Features][features]
1. [Packaging Your Application][packaging]
1. [Publishing and Updating][updates]
:::
## Learning goals
In this part of the tutorial, you will learn how to set up your Electron project
and write a minimal starter application. By the end of this section,
you should be able to run a working Electron app in development mode from
your terminal.
## Setting up your project
:::caution Avoid WSL
If you are on a Windows machine, please do not use [Windows Subsystem for Linux][wsl] (WSL)
when following this tutorial as you will run into issues when trying to execute the
application.
<!--https://www.electronforge.io/guides/developing-with-wsl-->
:::
### Initializing your npm project
Electron apps are scaffolded using npm, with the package.json file
as an entry point. Start by creating a folder and initializing an npm package
within it with `npm init`.
```sh npm2yarn
mkdir my-electron-app && cd my-electron-app
npm init
```
This command will prompt you to configure some fields in your package.json.
There are a few rules to follow for the purposes of this tutorial:
- _entry point_ should be `main.js` (you will be creating that file soon).
- _author_, _license_, and _description_ can be any value, but will be necessary for
[packaging][packaging] later on.
Then, install Electron into your app's **devDependencies**, which is the list of external
development-only package dependencies not required in production.
:::info Why is Electron a devDependency?
This may seem counter-intuitive since your production code is running Electron APIs.
However, packaged apps will come bundled with the Electron binary, eliminating the need to specify
it as a production dependency.
:::
```sh npm2yarn
npm install electron --save-dev
```
Your package.json file should look something like this after initializing your package
and installing Electron. You should also now have a `node_modules` folder containing
the Electron executable, as well as a `package-lock.json` lockfile that specifies
the exact dependency versions to install.
```json title='package.json'
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"author": "Jane Doe",
"license": "MIT",
"devDependencies": {
"electron": "19.0.0"
}
}
```
:::info Advanced Electron installation steps
If installing Electron directly fails, please refer to our [Advanced Installation][installation]
documentation for instructions on download mirrors, proxies, and troubleshooting steps.
:::
### Adding a .gitignore
The [`.gitignore`][gitignore] file specifies which files and directories to avoid tracking
with Git. You should place a copy of [GitHub's Node.js gitignore template][gitignore-template]
into your project's root folder to avoid committing your project's `node_modules` folder.
## Running an Electron app
:::tip Further reading
Read [Electron's process model][process-model] documentation to better
understand how Electron's multiple processes work together.
:::
The [`main`][package-json-main] script you defined in package.json is the entry point of any
Electron application. This script controls the **main process**, which runs in a Node.js
environment and is responsible for controlling your app's lifecycle, displaying native
interfaces, performing privileged operations, and managing renderer processes
(more on that later).
Before creating your first Electron app, you will first use a trivial script to ensure your
main process entry point is configured correctly. Create a `main.js` file in the root folder
of your project with a single line of code:
```js title='main.js'
console.log(`Hello from Electron 👋`)
```
Because Electron's main process is a Node.js runtime, you can execute arbitrary Node.js code
with the `electron` command (you can even use it as a [REPL]). To execute this script,
add `electron .` to the `start` command in the [`scripts`][package-scripts]
field of your package.json. This command will tell the Electron executable to look for the main
script in the current directory and run it in dev mode.
```json {8-10} title='package.json'
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "Hello World!",
"main": "main.js",
"author": "Jane Doe",
"license": "MIT",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^19.0.0"
}
}
```
```sh npm2yarn
npm run start
```
Your terminal should print out `Hello from Electron 👋`. Congratulations,
you have executed your first line of code in Electron! Next, you will learn
how to create user interfaces with HTML and load that into a native window.
## Loading a web page into a BrowserWindow
In Electron, each window displays a web page that can be loaded either from a local HTML
file or a remote web address. For this example, you will be loading in a local file. Start
by creating a barebones web page in an `index.html` file in the root folder of your project:
```html title='index.html'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
</body>
</html>
```
Now that you have a web page, you can load it into an Electron [BrowserWindow][browser-window].
Replace the contents your `main.js` file with the following code. We will explain each
highlighted block separately.
```js {1,3-10,12-14} title='main.js' showLineNumbers
const { app, BrowserWindow } = require('electron')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
})
```
### Importing modules
```js title='main.js (Line 1)'
const { app, BrowserWindow } = require('electron')
```
In the first line, we are importing two Electron modules
with CommonJS module syntax:
- [app][app], which controls your application's event lifecycle.
- [BrowserWindow][browser-window], which creates and manages app windows.
:::info Capitalization conventions
You might have noticed the capitalization difference between the **a**pp
and **B**rowser**W**indow modules. Electron follows typical JavaScript conventions here,
where PascalCase modules are instantiable class constructors (e.g. BrowserWindow, Tray,
Notification) whereas camelCase modules are not instantiable (e.g. app, ipcRenderer, webContents).
:::
:::warning ES Modules in Electron
[ECMAScript modules](https://nodejs.org/api/esm.html) (i.e. using `import` to load a module)
are currently not directly supported in Electron. You can find more information about the
state of ESM in Electron in [electron/electron#21457](https://github.com/electron/electron/issues/21457).
:::
### Writing a reusable function to instantiate windows
The `createWindow()` function loads your web page into a new BrowserWindow instance:
```js title='main.js (Lines 3-10)'
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
})
win.loadFile('index.html')
}
```
### Calling your function when the app is ready
```js title='main.js (Lines 12-14)'
app.whenReady().then(() => {
createWindow()
})
```
Many of Electron's core modules are Node.js [event emitters] that adhere to Node's asynchronous
event-driven architecture. The app module is one of these emitters.
In Electron, BrowserWindows can only be created after the app module's [`ready`][app-ready] event
is fired. You can wait for this event by using the [`app.whenReady()`][app-when-ready] API and
calling `createWindow()` once its promise is fulfilled.
:::info
You typically listen to Node.js events by using an emitter's `.on` function.
```diff
+ app.on('ready').then(() => {
- app.whenReady().then(() => {
createWindow()
})
```
However, Electron exposes `app.whenReady()` as a helper specifically for the `ready` event to
avoid subtle pitfalls with directly listening to that event in particular.
See [electron/electron#21972](https://github.com/electron/electron/pull/21972) for details.
:::
At this point, running your Electron application's `start` command should successfully
open a window that displays your web page!
Each web page your app displays in a window will run in a separate process called a
**renderer** process (or simply _renderer_ for short). Renderer processes have access
to the same JavaScript APIs and tooling you use for typical front-end web
development, such as using [webpack] to bundle and minify your code or [React][react]
to build your user interfaces.
## Managing your app's window lifecycle
Application windows behave differently on each operating system. Rather than
enforce these conventions by default, Electron gives you the choice to implement
them in your app code if you wish to follow them. You can implement basic window
conventions by listening for events emitted by the app and BrowserWindow modules.
:::tip Process-specific control flow
Checking against Node's [`process.platform`][node-platform] variable can help you
to run code conditionally on certain platforms. Note that there are only three
possible platforms that Electron can run in: `win32` (Windows), `linux` (Linux),
and `darwin` (macOS).
:::
### Quit the app when all windows are closed (Windows & Linux)
On Windows and Linux, closing all windows will generally quit an application entirely.
To implement this pattern in your Electron app, listen for the app module's
[`window-all-closed`][window-all-closed] event, and call [`app.quit()`][app-quit]
to exit your app if the user is not on macOS.
```js
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit()
})
```
### Open a window if none are open (macOS)
In contrast, macOS apps generally continue running even without any windows open.
Activating the app when no windows are available should open a new one.
To implement this feature, listen for the app module's [`activate`][activate]
event, and call your existing `createWindow()` method if no BrowserWindows are open.
Because windows cannot be created before the `ready` event, you should only listen for
`activate` events after your app is initialized. Do this by only listening for activate
events inside your existing `whenReady()` callback.
```js
app.whenReady().then(() => {
createWindow()
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
```
## Final starter code
```fiddle docs/fiddles/tutorial-first-app
```
## Optional: Debugging from VS Code
If you want to debug your application using VS Code, you have need attach VS Code to
both the main and renderer processes. Here is a sample configuration for you to
run. Create a launch.json configuration in a new `.vscode` folder in your project:
```json title='.vscode/launch.json'
{
"version": "0.2.0",
"compounds": [
{
"name": "Main + renderer",
"configurations": ["Main", "Renderer"],
"stopAll": true
}
],
"configurations": [
{
"name": "Renderer",
"port": 9222,
"request": "attach",
"type": "pwa-chrome",
"webRoot": "${workspaceFolder}"
},
{
"name": "Main",
"type": "pwa-node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
},
"args": [".", "--remote-debugging-port=9222"],
"outputCapture": "std",
"console": "integratedTerminal"
}
]
}
```
The "Main + renderer" option will appear when you select "Run and Debug"
from the sidebar, allowing you to set breakpoints and inspect all the variables among
other things in both the main and renderer processes.
What we have done in the `launch.json` file is to create 3 configurations:
- `Main` is used to start the main process and also expose port 9222 for remote debugging
(`--remote-debugging-port=9222`). This is the port that we will use to attach the debugger
for the `Renderer`. Because the main process is a Node.js process, the type is set to
`pwa-node` (`pwa-` is the prefix that tells VS Code to use the latest JavaScript debugger).
- `Renderer` is used to debug the renderer process. Because the main process is the one
that creates the process, we have to "attach" to it (`"request": "attach"`) instead of
creating a new one.
The renderer process is a web one, so the debugger we have to use is `pwa-chrome`.
- `Main + renderer` is a [compound task] that executes the previous ones simultaneously.
:::caution
Because we are attaching to a process in `Renderer`, it is possible that the first lines of
your code will be skipped as the debugger will not have had enough time to connect before they are
being executed.
You can work around this by refreshing the page or setting a timeout before executing the code
in development mode.
:::
:::info Further reading
If you want to dig deeper in the debugging area, the following guides provide more information:
- [Application Debugging]
- [DevTools Extensions][devtools extension]
:::
## Summary
Electron applications are set up using npm packages. The Electron executable should be installed
in your project's `devDependencies` and can be run in development mode using a script in your
package.json file.
The executable runs the JavaScript entry point found in the `main` property of your package.json.
This file controls Electron's **main process**, which runs an instance of Node.js and is
responsible for your app's lifecycle, displaying native interfaces, performing privileged operations,
and managing renderer processes.
**Renderer processes** (or renderers for short) are responsible for display graphical content. You can
load a web page into a renderer by pointing it to either a web address or a local HTML file.
Renderers behave very similarly to regular web pages and have access to the same web APIs.
In the next section of the tutorial, we will be learning how to augment the renderer process with
privileged APIs and how to communicate between processes.
<!-- Links -->
[activate]: ../api/app.md#event-activate-macos
[advanced-installation]: installation.md
[app]: ../api/app.md
[app-quit]: ../api/app.md#appquit
[app-ready]: ../api/app.md#event-ready
[app-when-ready]: ../api/app.md#appwhenready
[application debugging]: ./application-debugging.md
[browser-window]: ../api/browser-window.md
[commonjs]: https://nodejs.org/docs/../api/modules.html#modules_modules_commonjs_modules
[compound task]: https://code.visualstudio.com/Docs/editor/tasks#_compound-tasks
[devtools extension]: ./devtools-extension.md
[event emitters]: https://nodejs.org/api/events.html#events
[gitignore]: https://git-scm.com/docs/gitignore
[gitignore-template]: https://github.com/github/gitignore/blob/main/Node.gitignore
[installation]: ./installation.md
[node-platform]: https://nodejs.org/api/process.html#process_process_platform
[package-json-main]: https://docs.npmjs.com/cli/v7/configuring-npm/package-json#main
[package-scripts]: https://docs.npmjs.com/cli/v7/using-npm/scripts
[process-model]: process-model.md
[react]: https://reactjs.org
[repl]: ./repl.md
[sandbox]: ./sandbox.md
[webpack]: https://webpack.js.org
[window-all-closed]: ../api/app.md#event-window-all-closed
[wsl]: https://docs.microsoft.com/en-us/windows/wsl/about#what-is-wsl-2
<!-- Tutorial links -->
[prerequisites]: tutorial-1-prerequisites.md
[building your first app]: tutorial-2-first-app.md
[preload]: tutorial-3-preload.md
[features]: tutorial-4-adding-features.md
[packaging]: tutorial-5-packaging.md
[updates]: tutorial-6-publishing-updating.md

View File

@@ -0,0 +1,271 @@
---
title: 'Using Preload Scripts'
description: 'This guide will step you through the process of creating a barebones Hello World app in Electron, similar to electron/electron-quick-start.'
slug: tutorial-preload
hide_title: false
---
:::info Follow along the tutorial
This is **part 3** of the Electron tutorial.
1. [Prerequisites][prerequisites]
1. [Building your First App][building your first app]
1. **[Using Preload Scripts][preload]**
1. [Adding Features][features]
1. [Packaging Your Application][packaging]
1. [Publishing and Updating][updates]
:::
## Learning goals
In this part of the tutorial, you will learn what a preload script is and how to use one
to securely expose privileged APIs into the renderer process. You will also learn how to
communicate between main and renderer processes with Electron's inter-process
communication (IPC) modules.
## What is a preload script?
Electron's main process is a Node.js environment that has full operating system access.
On top of [Electron modules][modules], you can also access [Node.js built-ins][node-api],
as well as any packages installed via npm. On the other hand, renderer processes run web
pages and do not run Node.js by default for security reasons.
To bridge Electron's different process types together, we will need to use a special script
called a **preload**.
## Augmenting the renderer with a preload script
A BrowserWindow's preload script runs in a context that has access to both the HTML DOM
and a Node.js environment. Preload scripts are injected before a web page loads in the renderer,
similar to a Chrome extension's [content scripts][content-script]. To add features to your renderer
that require privileged access, you can define [global] objects through the
[contextBridge][contextbridge] API.
To demonstrate this concept, you will create a preload script that exposes your app's
versions of Chrome, Node, and Electron into the renderer.
Add a new `preload.js` script that exposes selected properties of Electron's `process.versions`
object to the renderer process in a `versions` global variable.
```js title="preload.js"
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron,
// we can also expose variables, not just functions
})
```
To attach this script to your renderer process, pass its path to the
`webPreferences.preload` option in the BrowserWindow constructor:
```js {8-10} title="main.js"
const { app, BrowserWindow } = require('electron')
const path = require('path')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
})
```
:::info
There are two Node.js concepts that are used here:
- The [`__dirname`][dirname] string points to the path of the currently executing script
(in this case, your project's root folder).
- The [`path.join`][path-join] API joins multiple path segments together, creating a
combined path string that works across all platforms.
:::
At this point, the renderer has access to the `versions` global, so let's display that
information in the window. This variable can be accessed via `window.versions` or simply
`versions`. Create a `renderer.js` script that uses the [`document.getElementById`]
DOM API to replace the displayed text for the HTML element with `info` as its `id` property.
```js title="renderer.js"
const information = document.getElementById('info')
information.innerText = `This app is using Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), and Electron (v${versions.electron()})`
```
Then, modify your `index.html` by adding a new element with `info` as its `id` property,
and attach your `renderer.js` script:
```html {18,20} title="index.html"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>Hello from Electron renderer!</title>
</head>
<body>
<h1>Hello from Electron renderer!</h1>
<p>👋</p>
<p id="info"></p>
</body>
<script src="./renderer.js"></script>
</html>
```
After following the above steps, your app should look something like this:
![Electron app showing This app is using Chrome (v102.0.5005.63), Node.js (v16.14.2), and Electron (v19.0.3)](../images/preload-example.png)
And the code should look like this:
```fiddle docs/fiddles/tutorial-preload
```
## Communicating between processes
As we have mentioned above, Electron's main and renderer process have distinct responsibilities
and are not interchangeable. This means it is not possible to access the Node.js APIs directly
from the renderer process, nor the HTML Document Object Model (DOM) from the main process.
The solution for this problem is to use Electron's `ipcMain` and `ipcRenderer` modules for
inter-process communication (IPC). To send a message from your web page to the main process,
you can set up a main process handler with `ipcMain.handle` and
then expose a function that calls `ipcRenderer.invoke` to trigger the handler in your preload script.
To illustrate, we will add a global function to the renderer called `ping()`
that will return a string from the main process.
First, set up the `invoke` call in your preload script:
```js {1,7} title="preload.js"
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron,
ping: () => ipcRenderer.invoke('ping'),
// we can also expose variables, not just functions
})
```
:::caution IPC security
Notice how we wrap the `ipcRenderer.invoke('ping')` call in a helper function rather
than expose the `ipcRenderer` module directly via context bridge. You **never** want to
directly expose the entire `ipcRenderer` module via preload. This would give your renderer
the ability to send arbitrary IPC messages to the main process, which becomes a powerful
attack vector for malicious code.
:::
Then, set up your `handle` listener in the main process. We do this _before_
loading the HTML file so that the handler is guaranteed to be ready before
you send out the `invoke` call from the renderer.
```js {1,11} title="main.js"
const { ipcMain } = require('electron')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
ipcMain.handle('ping', () => 'pong')
win.loadFile('index.html')
}
```
Once you have the sender and receiver set up, you can now send messages from the renderer
to the main process through the `'ping'` channel you just defined.
```js title='renderer.js'
const func = async () => {
const response = await window.versions.ping()
console.log(response) // prints out 'pong'
}
func()
```
:::info
For more in-depth explanations on using the `ipcRenderer` and `ipcMain` modules,
check out the full [Inter-Process Communication][ipc] guide.
:::
## Summary
A preload script contains code that runs before your web page is loaded into the browser
window. It has access to both DOM APIs and Node.js environment, and is often used to
expose privileged APIs to the renderer via the `contextBridge` API.
Because the main and renderer processes have very different responsibilities, Electron
apps often use the preload script to set up inter-process communication (IPC) interfaces
to pass arbitrary messages between the two kinds of processes.
In the next part of the tutorial, we will be showing you resources on adding more
functionality to your app, then teaching you distributing your app to users.
<!-- Links -->
[advanced-installation]: ./installation.md
[application debugging]: ./application-debugging.md
[app]: ../api/app.md
[app-ready]: ../api/app.md#event-ready
[app-when-ready]: ../api/app.md#appwhenready
[browser-window]: ../api/browser-window.md
[commonjs]: https://nodejs.org/docs/latest/api/modules.html#modules_modules_commonjs_modules
[compound task]: https://code.visualstudio.com/Docs/editor/tasks#_compound-tasks
[content-script]: https://developer.chrome.com/docs/extensions/mv3/content_scripts/
[contextbridge]: ../api/context-bridge.md
[context-isolation]: ./context-isolation.md
[`document.getelementbyid`]: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById
[devtools-extension]: ./devtools-extension.md
[dirname]: https://nodejs.org/api/modules.html#modules_dirname
[global]: https://developer.mozilla.org/en-US/docs/Glossary/Global_object
[ipc]: ./ipc.md
[mdn-csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
[modules]: ../api/app.md
[node-api]: https://nodejs.org/dist/latest/docs/api/
[package-json-main]: https://docs.npmjs.com/cli/v7/configuring-npm/package-json#main
[package-scripts]: https://docs.npmjs.com/cli/v7/using-npm/scripts
[path-join]: https://nodejs.org/api/path.html#path_path_join_paths
[process-model]: ./process-model.md
[react]: https://reactjs.org
[sandbox]: ./sandbox.md
[webpack]: https://webpack.js.org
<!-- Tutorial links -->
[prerequisites]: tutorial-1-prerequisites.md
[building your first app]: tutorial-2-first-app.md
[preload]: tutorial-3-preload.md
[features]: tutorial-4-adding-features.md
[packaging]: tutorial-5-packaging.md
[updates]: tutorial-6-publishing-updating.md

View File

@@ -0,0 +1,77 @@
---
title: 'Adding Features'
description: 'In this step of the tutorial, we will share some resources you should read to add features to your application'
slug: tutorial-adding-features
hide_title: false
---
:::info Follow along the tutorial
This is **part 4** of the Electron tutorial.
1. [Prerequisites][prerequisites]
1. [Building your First App][building your first app]
1. [Using Preload Scripts][preload]
1. **[Adding Features][features]**
1. [Packaging Your Application][packaging]
1. [Publishing and Updating][updates]
:::
## Adding application complexity
If you have been following along, you should have a functional Electron application
with a static user interface. From this starting point, you can generally progress
in developing your app in two broad directions:
1. Adding complexity to your renderer process' web app code
1. Deeper integrations with the operating system and Node.js
It is important to understand the distinction between these two broad concepts. For the
first point, Electron-specific resources are not necessary. Building a pretty to-do
list in Electron is just pointing your Electron BrowserWindow to a pretty
to-do list web app. Ultimately, you are building your renderer's UI using the same tools
(HTML, CSS, JavaScript) that you would on the web. Therefore, Electron's docs will
not go in-depth on how to use standard web tools.
On the other hand, Electron also provides a rich set of tools that allow
you to integrate with the desktop environment, from creating tray icons to adding
global shortcuts to displaying native menus. It also gives you all the power of a
Node.js environment in the main process. This set of capabilities separates
Electron applications from running a website in a browser tab, and are the
focus of Electron's documentation.
## How-to examples
Electron's documentation has many tutorials to help you with more advanced topics
and deeper operating system integrations. To get started, check out the
[How-To Examples][how-to] doc.
:::note Let us know if something is missing!
If you can't find what you are looking for, please let us know on [GitHub] or in
our [Discord server][discord]!
:::
## What's next?
For the rest of the tutorial, we will be shifting away from application code
and giving you a look at how you can get your app from your developer machine
into end users' hands.
<!-- Link labels -->
[discord]: https://discord.com/invite/APGC3k5yaH
[github]: https://github.com/electron/electronjs.org-new/issues/new
[how to]: ./examples.md
[node-platform]: https://nodejs.org/api/process.html#process_process_platform
<!-- Tutorial links -->
[prerequisites]: tutorial-1-prerequisites.md
[building your first app]: tutorial-2-first-app.md
[preload]: tutorial-3-preload.md
[features]: tutorial-4-adding-features.md
[packaging]: tutorial-5-packaging.md
[updates]: tutorial-6-publishing-updating.md

View File

@@ -0,0 +1,225 @@
---
title: 'Packaging Your Application'
description: 'To distribute your app with Electron, you need to package it and create installers.'
slug: tutorial-packaging
hide_title: false
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
:::info Follow along the tutorial
This is **part 5** of the Electron tutorial.
1. [Prerequisites][prerequisites]
1. [Building your First App][building your first app]
1. [Using Preload Scripts][preload]
1. [Adding Features][features]
1. **[Packaging Your Application][packaging]**
1. [Publishing and Updating][updates]
:::
## Learning goals
In this part of the tutorial, we'll be going over the basics of packaging and distributing
your app with [Electron Forge].
## Using Electron Forge
Electron does not have any tooling for packaging and distribution bundled into its core
modules. Once you have a working Electron app in dev mode, you need to use
additional tooling to create a packaged app you can distribute to your users (also known
as a **distributable**). Distributables can be either installers (e.g. MSI on Windows) or
portable executable files (e.g. `.app` on macOS).
Electron Forge is an all-in-one tool that handles the packaging and distribution of Electron
apps. Under the hood, it combines a lot of existing Electron tools (e.g. [`electron-packager`],
[`@electron/osx-sign`], [`electron-winstaller`], etc.) into a single interface so you do not
have to worry about wiring them all together.
### Importing your project into Forge
You can install Electron Forge's CLI in your project's `devDependencies` and import your
existing project with a handy conversion script.
```sh npm2yarn
npm install --save-dev @electron-forge/cli
npx electron-forge import
```
Once the conversion script is done, Forge should have added a few scripts
to your `package.json` file.
```json title='package.json'
//...
"scripts": {
"start": "electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make"
},
//...
```
:::info CLI documentation
For more information on `make` and other Forge APIs, check out
the [Electron Forge CLI documentation].
:::
You should also notice that your package.json now has a few more packages installed
under your `devDependencies`, and contains an added `config.forge` field with an array
of makers configured. **Makers** are Forge plugins that create distributables from
your source code. You should see multiple makers in the pre-populated configuration,
one for each target platform.
### Creating a distributable
To create a distributable, use your project's new `make` script, which runs the
`electron-forge make` command.
```sh npm2yarn
npm run make
```
This `make` command contains two steps:
1. It will first run `electron-forge package` under the hood, which bundles your app
code together with the Electron binary. The packaged code is generated into a folder.
1. It will then use this packaged app folder to create a separate distributable for each
configured maker.
After the script runs, you should see an `out` folder containing both the distributable
and a folder containing the packaged application code.
```plain title='macOS output example'
out/
├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
├── ...
└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app
```
The distributable in the `out/make` folder should be ready to launch! You have now
created your first bundled Electron application.
:::tip Distributable formats
Electron Forge can be configured to create distributables in different OS-specific formats
(e.g. DMG, deb, MSI, etc.). See Forge's [Makers] documentation for all configuration options.
:::
:::note Packaging without Electron Forge
If you want to manually package your code, or if you're just interested understanding the
mechanics behind packaging an Electron app, check out the full [Application Packaging]
documentation.
:::
## Important: signing your code
In order to distribute desktop applications to end users, we _highly recommended_ for you
to **code sign** your Electron app. Code signing is an important part of shipping
desktop applications, and is mandatory for the auto-update step in the final part
of the tutorial.
Code signing is a security technology that you use to certify that a desktop app was
created by a known source. Windows and macOS have their own OS-specific code signing
systems that will make it difficult for users to download or launch unsigned applications.
If you already have code signing certificates for Windows and macOS, you can set your
credentials in your Forge configuration. Otherwise, please refer to the full
[Code Signing] documentation to learn how to purchase a certificate and for more information
on the desktop app code signing process.
On macOS, code signing is done at the app packaging level. On Windows, distributable installers
are signed instead.
<Tabs>
<TabItem value="macos" label="macOS" default>
```json title='package.json' {6-18}
{
//...
"config": {
"forge": {
//...
"packagerConfig": {
"osxSign": {
"identity": "Developer ID Application: Felix Rieseberg (LT94ZKYDCJ)",
"hardened-runtime": true,
"entitlements": "entitlements.plist",
"entitlements-inherit": "entitlements.plist",
"signature-flags": "library"
},
"osxNotarize": {
"appleId": "felix@felix.fun",
"appleIdPassword": "this-is-a-secret"
}
}
//...
}
}
//...
}
```
</TabItem>
<TabItem value="windows" label="Windows">
```json title='package.json' {6-14}
{
//...
"config": {
"forge": {
//...
"makers": [
{
"name": "@electron-forge/maker-squirrel",
"config": {
"certificateFile": "./cert.pfx",
"certificatePassword": "this-is-a-secret"
}
}
]
//...
}
}
//...
}
```
</TabItem>
</Tabs>
## Summary
Electron applications need to be packaged to be distributed to users. In this tutorial,
you imported your app into Electron Forge and configured it to package your app and
generate installers.
In order for your application to be trusted by the user's system, you need to digitally
certify that the distributable is authentic and untampered by code signing it. Your app
can be signed through Forge once you configure it to use your code signing certificate
information.
[`@electron/osx-sign`]: https://github.com/electron/osx-sign
[application packaging]: ./application-distribution.md
[code signing]: ./code-signing.md
[`electron-packager`]: https://github.com/electron/electron-packager
[`electron-winstaller`]: https://github.com/electron/windows-installer
[electron forge]: https://www.electronforge.io
[electron forge cli documentation]: https://www.electronforge.io/cli#commands
[makers]: https://www.electronforge.io/config/makers
<!-- Tutorial links -->
[prerequisites]: tutorial-1-prerequisites.md
[building your first app]: tutorial-2-first-app.md
[preload]: tutorial-3-preload.md
[features]: tutorial-4-adding-features.md
[packaging]: tutorial-5-packaging.md
[updates]: tutorial-6-publishing-updating.md

View File

@@ -0,0 +1,251 @@
---
title: 'Publishing and Updating'
description: "There are several ways to update an Electron application. The easiest and officially supported one is taking advantage of the built-in Squirrel framework and Electron's autoUpdater module."
slug: tutorial-publishing-updating
hide_title: false
---
:::info Follow along the tutorial
This is **part 6** of the Electron tutorial.
1. [Prerequisites][prerequisites]
1. [Building your First App][building your first app]
1. [Using Preload Scripts][preload]
1. [Adding Features][features]
1. [Packaging Your Application][packaging]
1. **[Publishing and Updating][updates]**
:::
## Learning goals
If you've been following along, this is the last step of the tutorial! In this part,
you will publish your app to GitHub releases and integrate automatic updates
into your app code.
## Using update.electronjs.org
The Electron maintainers provide a free auto-updating service for open-source apps
at https://update.electronjs.org. Its requirements are:
- Your app runs on macOS or Windows
- Your app has a public GitHub repository
- Builds are published to [GitHub releases]
- Builds are [code signed][code-signed]
At this point, we'll assume that you have already pushed all your
code to a public GitHub repository.
:::info Alternative update services
If you're using an alternate repository host (e.g. GitLab or Bitbucket) or if
you need to keep your code repository private, please refer to our
[step-by-step guide][update-server] on hosting your own Electron update server.
:::
## Publishing a GitHub release
Electron Forge has [Publisher] plugins that can automate the distribution
of your packaged application to various sources. In this tutorial, we will
be using the GitHub Publisher, which will allow us to publish
our code to GitHub releases.
### Generating a personal access token
Forge cannot publish to any repository on GitHub without permission. You
need to pass in an authenticated token that gives Forge access to
your GitHub releases. The easiest way to do this is to
[create a new personal access token (PAT)][new-pat]
with the `public_repo` scope, which gives write access to your public repositories.
**Make sure to keep this token a secret.**
### Setting up the GitHub Publisher
#### Installing the module
Forge's [GitHub Publisher] is a plugin that
needs to be installed in your project's `devDependencies`:
```sh npm2yarn
npm install --save-dev @electron-forge/publisher-github
```
#### Configuring the publisher in Forge
Once you have it installed, you need to set it up in your Forge
configuration. A full list of options is documented in the Forge's
[`PublisherGitHubConfig`] API docs.
```json title='package.json' {6-16}
{
//...
"config": {
"forge": {
"publishers": [
{
"name": "@electron-forge/publisher-github",
"config": {
"repository": {
"owner": "github-user-name",
"name": "github-repo-name"
},
"prerelease": false,
"draft": true
}
}
]
}
}
//...
}
```
:::tip Drafting releases before publishing
Notice that you have configured Forge to publish your release as a draft.
This will allow you to see the release with its generated artifacts
without actually publishing it to your end users. You can manually
publish your releases via GitHub after writing release notes and
double-checking that your distributables work.
:::
#### Setting up your authentication token
You also need to make the Publisher aware of your authentication token.
By default, it will use the value stored in the `GITHUB_TOKEN` environment
variable.
### Running the publish command
Add Forge's [publish command] to your npm scripts.
```json {6} title='package.json'
//...
"scripts": {
"start": "electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make",
"publish": "electron-forge publish"
},
//...
```
This command will run your configured makers and publish the output distributables to a new
GitHub release.
```sh npm2yarn
npm run publish
```
By default, this will only publish a single distributable for your host operating system and
architecture. You can publish for different architectures by passing in the `--arch` flag to your
Forge commands.
The name of this release will correspond to the `version` field in your project's package.json file.
:::tip Tagging releases
Optionally, you can also [tag your releases in Git][git-tag] so that your
release is associated with a labeled point in your code history. npm comes
with a handy [`npm version`](https://docs.npmjs.com/cli/v8/commands/npm-version)
command that can handle the version bumping and tagging for you.
:::
#### Bonus: Publishing in GitHub Actions
Publishing locally can be painful, especially because you can only create distributables
for your host operating system (i.e. you can't publish a Window `.exe` file from macOS).
A solution for this would be to publish your app via automation workflows
such as [GitHub Actions], which can run tasks in the
cloud on Ubuntu, macOS, and Windows. This is the exact approach taken by [Electron Fiddle].
You can refer to Fiddle's [Build and Release pipeline][fiddle-build]
and [Forge configuration][fiddle-forge-config]
for more details.
## Instrumenting your updater code
Now that we have a functional release system via GitHub releases, we now need to tell our
Electron app to download an update whenever a new release is out. Electron apps do this
via the [autoUpdater] module, which reads from an update server feed to check if a new version
is available for download.
The update.electronjs.org service provides an updater-compatible feed. For example, Electron
Fiddle v0.28.0 will check the endpoint at https://update.electronjs.org/electron/fiddle/darwin/v0.28.0
to see if a newer GitHub release is available.
After your release is published to GitHub, the update.electronjs.org service should work
for your application. The only step left is to configure the feed with the autoUpdater module.
To make this process easier, the Electron team maintains the [`update-electron-app`] module,
which sets up the autoUpdater boilerplate for update.electronjs.org in one function
call — no configuration required. This module will search for the update.electronjs.org
feed that matches your project's package.json `"repository"` field.
First, install the module as a runtime dependency.
```sh npm2yarn
npm install update-electron-app
```
Then, import the module and call it immediately in the main process.
```js title='main.js'
require('update-electron-app')()
```
And that is all it takes! Once your application is packaged, it will update itself for each new
GitHub release that you publish.
## Summary
In this tutorial, we configured Electron Forge's GitHub Publisher to upload your app's
distributables to GitHub releases. Since distributables cannot always be generated
between platforms, we recommend setting up your building and publishing flow
in a Continuous Integration pipeline if you do not have access to machines.
Electron applications can self-update by pointing the autoUpdater module to an update server feed.
update.electronjs.org is a free update server provided by Electron for open-source applications
published on GitHub releases. Configuring your Electron app to use this service is as easy as
installing and importing the `update-electron-app` module.
If your application is not eligible for update.electronjs.org, you should instead deploy your
own update server and configure the autoUpdater module yourself.
:::info 🌟 You're done!
From here, you have officially completed our tutorial to Electron. Feel free to explore the
rest of our docs and happy developing! If you have questions, please stop by our community
[Discord server].
:::
[autoupdater]: ../api/auto-updater.md
[code-signed]: ./code-signing.md
[discord server]: https://discord.com/invite/APGC3k5yaH
[electron fiddle]: https://electronjs.org/fiddle
[fiddle-build]: https://github.com/electron/fiddle/blob/master/.github/workflows/build.yaml
[fiddle-forge-config]: https://github.com/electron/fiddle/blob/master/forge.config.js
[github actions]: https://github.com/features/actions
[github publisher]: https://www.electronforge.io/config/publishers/github
[github releases]: https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository
[git tag]: https://git-scm.com/book/en/v2/Git-Basics-Tagging
[new-pat]: https://github.com/settings/tokens/new
[publish command]: https://www.electronforge.io/cli#publish
[publisher]: https://www.electronforge.io/config/publishers
[`publishergithubconfig`]: https://js.electronforge.io/publisher/github/interfaces/publishergithubconfig
[`update-electron-app`]: https://github.com/electron/update-electron-app
[update-server]: ./updates.md
<!-- Tutorial links -->
[prerequisites]: tutorial-1-prerequisites.md
[building your first app]: tutorial-2-first-app.md
[preload]: tutorial-3-preload.md
[features]: tutorial-4-adding-features.md
[packaging]: tutorial-5-packaging.md
[updates]: tutorial-6-publishing-updating.md

View File

@@ -1,11 +1,16 @@
# Updating Applications
---
title: 'Updating Applications'
description: "There are several ways to update an Electron application. The easiest and officially supported one is taking advantage of the built-in Squirrel framework and Electron's autoUpdater module."
slug: updates
hide_title: false
---
There are several ways to update an Electron application. The easiest and
officially supported one is taking advantage of the built-in
There are several ways to provide automatic updates to your Electron application.
The easiest and officially supported one is taking advantage of the built-in
[Squirrel](https://github.com/Squirrel) framework and
Electron's [autoUpdater](../api/auto-updater.md) module.
## Using `update.electronjs.org`
## Using update.electronjs.org
The Electron team maintains [update.electronjs.org], a free and open-source
webservice that Electron apps can use to self-update. The service is designed
@@ -13,72 +18,77 @@ for Electron apps that meet the following criteria:
- App runs on macOS or Windows
- App has a public GitHub repository
- Builds are published to GitHub Releases
- Builds are code-signed
- Builds are published to [GitHub Releases][gh-releases]
- Builds are [code-signed](./code-signing.md)
The easiest way to use this service is by installing [update-electron-app],
a Node.js module preconfigured for use with update.electronjs.org.
Install the module:
Install the module using your Node.js package manager of choice:
```sh
```sh npm2yarn
npm install update-electron-app
```
Invoke the updater from your app's main process file:
Then, invoke the updater from your app's main process file:
```js
```js title="main.js"
require('update-electron-app')()
```
By default, this module will check for updates at app startup, then every ten
minutes. When an update is found, it will automatically be downloaded in the background. When the download completes, a dialog is displayed allowing the user
to restart the app.
minutes. When an update is found, it will automatically be downloaded in the background.
When the download completes, a dialog is displayed allowing the user to restart the app.
If you need to customize your configuration, you can
[pass options to `update-electron-app`][update-electron-app]
[pass options to update-electron-app][update-electron-app]
or
[use the update service directly][update.electronjs.org].
## Deploying an Update Server
## Using other update services
If you're developing a private Electron application, or if you're not
publishing releases to GitHub Releases, it may be necessary to run your own
update server.
### Step 1: Deploying an update server
Depending on your needs, you can choose from one of these:
- [Hazel][hazel] Update server for private or open-source apps which can be
deployed for free on [Vercel][vercel]. It pulls from [GitHub Releases][gh-releases]
and leverages the power of GitHub's CDN.
deployed for free on [Vercel][vercel]. It pulls from [GitHub Releases][gh-releases]
and leverages the power of GitHub's CDN.
- [Nuts][nuts] Also uses [GitHub Releases][gh-releases], but caches app
updates on disk and supports private repositories.
updates on disk and supports private repositories.
- [electron-release-server][electron-release-server] Provides a dashboard for
handling releases and does not require releases to originate on GitHub.
handling releases and does not require releases to originate on GitHub.
- [Nucleus][nucleus] A complete update server for Electron apps maintained by
Atlassian. Supports multiple applications and channels; uses a static file store
to minify server cost.
Atlassian. Supports multiple applications and channels; uses a static file store
to minify server cost.
## Implementing Updates in Your App
Once you've deployed your update server, you can instrument your app code to receive and
apply the updates with Electron's [autoUpdater] module.
Once you've deployed your update server, continue with importing the required
modules in your code. The following code might vary for different server
software, but it works like described when using
[Hazel][hazel].
### Step 2: Receiving updates in your app
**Important:** Please ensure that the code below will only be executed in
your packaged app, and not in development. You can use
[electron-is-dev](https://github.com/sindresorhus/electron-is-dev) to check for
the environment.
First, import the required modules in your main process code. The following code might
vary for different server software, but it works like described when using [Hazel][hazel].
```javascript
:::warning Check your execution environment!
Please ensure that the code below will only be executed in your packaged app, and not in development.
You can use the [app.isPackaged](../api/app.md#appispackaged-readonly) API to check the environment.
:::
```javascript title='main.js'
const { app, autoUpdater, dialog } = require('electron')
```
Next, construct the URL of the update server and tell
Next, construct the URL of the update server feed and tell
[autoUpdater](../api/auto-updater.md) about it:
```javascript
```javascript title='main.js'
const server = 'https://your-deployment-url.com'
const url = `${server}/update/${process.platform}/${app.getVersion()}`
@@ -87,32 +97,32 @@ autoUpdater.setFeedURL({ url })
As the final step, check for updates. The example below will check every minute:
```javascript
```javascript title='main.js'
setInterval(() => {
autoUpdater.checkForUpdates()
}, 60000)
```
Once your application is [packaged](../tutorial/application-distribution.md),
Once your application is [packaged](./application-distribution.md),
it will receive an update for each new
[GitHub Release](https://help.github.com/articles/creating-releases/) that you
publish.
## Applying Updates
### Step 3: Notifying users when updates are available
Now that you've configured the basic update mechanism for your application, you
need to ensure that the user will get notified when there's an update. This
can be achieved using the autoUpdater API
[events](../api/auto-updater.md#events):
can be achieved using the [autoUpdater API events](../api/auto-updater.md#events):
```javascript
```javascript title="main.js"
autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => {
const dialogOpts = {
type: 'info',
buttons: ['Restart', 'Later'],
title: 'Application Update',
message: process.platform === 'win32' ? releaseNotes : releaseName,
detail: 'A new version has been downloaded. Restart the application to apply the updates.'
detail:
'A new version has been downloaded. Restart the application to apply the updates.',
}
dialog.showMessageBox(dialogOpts).then((returnValue) => {
@@ -125,16 +135,22 @@ Also make sure that errors are
[being handled](../api/auto-updater.md#event-error). Here's an example
for logging them to `stderr`:
```javascript
autoUpdater.on('error', message => {
```javascript title="main.js"
autoUpdater.on('error', (message) => {
console.error('There was a problem updating the application')
console.error(message)
})
```
## Handling Updates Manually
:::info Handling updates manually
Because the requests made by Auto Update aren't under your direct control, you may find situations that are difficult to handle (such as if the update server is behind authentication). The `url` field does support files, which means that with some effort, you can sidestep the server-communication aspect of the process. [Here's an example of how this could work](https://github.com/electron/electron/issues/5020#issuecomment-477636990).
Because the requests made by autoUpdate aren't under your direct control, you may find situations
that are difficult to handle (such as if the update server is behind authentication). The `url`
field supports the `file://` protocol, which means that with some effort, you can sidestep the
server-communication aspect of the process by loading your update from a local directory.
[Here's an example of how this could work](https://github.com/electron/electron/issues/5020#issuecomment-477636990).
:::
[vercel]: https://vercel.com
[hazel]: https://github.com/vercel/hazel

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

@@ -178,6 +178,7 @@ template("electron_paks") {
"${root_gen_dir}/third_party/blink/public/strings/blink_accessibility_strings_",
"${root_gen_dir}/third_party/blink/public/strings/blink_strings_",
"${root_gen_dir}/device/bluetooth/strings/bluetooth_strings_",
"${root_gen_dir}/extensions/strings/extensions_strings_",
"${root_gen_dir}/services/strings/services_strings_",
"${root_gen_dir}/ui/strings/app_locale_settings_",
"${root_gen_dir}/ui/strings/ax_strings_",
@@ -187,6 +188,7 @@ template("electron_paks") {
"//chrome/app/resources:platform_locale_settings",
"//components/strings:components_strings",
"//device/bluetooth/strings",
"//extensions/strings",
"//services/strings",
"//third_party/blink/public/strings",
"//third_party/blink/public/strings:accessibility_strings",

View File

@@ -23,6 +23,7 @@ filenames = {
lib_sources_linux = [
"shell/browser/browser_linux.cc",
"shell/browser/electron_browser_main_parts_linux.cc",
"shell/browser/lib/power_observer_linux.cc",
"shell/browser/lib/power_observer_linux.h",
"shell/browser/linux/unity_service.cc",

View File

@@ -500,6 +500,7 @@ WebContents.prototype._callWindowOpenHandler = function (event: Electron.Event,
if (!this._windowOpenHandler) {
return null;
}
const response = this._windowOpenHandler(details);
if (typeof response !== 'object') {
@@ -656,7 +657,15 @@ WebContents.prototype._init = function () {
postBody,
disposition
};
const options = this._callWindowOpenHandler(event, details);
let options: ReturnType<typeof this._callWindowOpenHandler>;
try {
options = this._callWindowOpenHandler(event, details);
} catch (err) {
event.preventDefault();
throw err;
}
if (!event.defaultPrevented) {
openGuestWindow({
event,
@@ -684,7 +693,16 @@ WebContents.prototype._init = function () {
referrer,
postBody
};
windowOpenOverriddenOptions = this._callWindowOpenHandler(event, details);
let result: ReturnType<typeof this._callWindowOpenHandler>;
try {
result = this._callWindowOpenHandler(event, details);
} catch (err) {
event.preventDefault();
throw err;
}
windowOpenOverriddenOptions = result;
if (!event.defaultPrevented) {
const secureOverrideWebPreferences = windowOpenOverriddenOptions ? {
// Allow setting of backgroundColor as a webPreference even though

View File

@@ -77,6 +77,19 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition
...browserWindowOptions
});
if (!guest) {
// When we open a new window from a link (via OpenURLFromTab),
// the browser process is responsible for initiating navigation
// in the new window.
window.loadURL(url, {
httpReferrer: referrer,
...(postData && {
postData,
extraHeaders: formatPostDataHeaders(postData as Electron.UploadRawData[])
})
});
}
handleWindowLifecycleEvents({ embedder, frameName, guest: window });
embedder.emit('did-create-window', window, { url, frameName, options: browserWindowOptions, disposition, referrer, postData });
@@ -235,6 +248,15 @@ export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {
};
}
function formatPostDataHeaders (postData: PostData) {
if (!postData) return;
const { contentType, boundary } = parseContentTypeFormat(postData);
if (boundary != null) { return `content-type: ${contentType}; boundary=${boundary}`; }
return `content-type: ${contentType}`;
}
const MULTIPART_CONTENT_TYPE = 'multipart/form-data';
const URL_ENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded';

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": "18.2.4",
"version": "18.3.9",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {

4
patches/angle/.patches Normal file
View File

@@ -0,0 +1,4 @@
cherry-pick-9768648fffc9.patch
cherry-pick-801b904aea7d.patch
cherry-pick-03aa5ae75c29.patch
cherry-pick-6661eb4900da.patch

View File

@@ -0,0 +1,76 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Geoff Lang <geofflang@google.com>
Date: Wed, 1 Jun 2022 11:22:42 -0400
Subject: M102: Ignore eglBind/ReleaseTexImage calls for lost contexts.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
eglBindTexImage and eglReleaseTexImage no-op when no context is
current. Extend this to lost contexts to match the behaviour of making
a GL call on a lost context.
This avoids potential unexpected bad accesses in the backends.
Bug: chromium:1316578
Change-Id: I7b309c297e0c803019720733dee2950abb4c4b5f
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3683869
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Alexis Hétu <sugoi@chromium.org>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
(cherry picked from commit bfab7e60a15dc6f72e34406d3f2a3996cd8d0be2)
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3691180
diff --git a/src/libANGLE/validationEGL.cpp b/src/libANGLE/validationEGL.cpp
index 7e4b3adb8db7ea32c98228155e0a702796f0da4c..4309fd84e74fbb4a21edfb9b90bfc1fcda5a9d4a 100644
--- a/src/libANGLE/validationEGL.cpp
+++ b/src/libANGLE/validationEGL.cpp
@@ -4863,7 +4863,7 @@ bool ValidateBindTexImage(const ValidationContext *val,
}
gl::Context *context = val->eglThread->getContext();
- if (context)
+ if (context && !context->isContextLost())
{
gl::TextureType type = egl_gl::EGLTextureTargetToTextureType(surface->getTextureTarget());
gl::Texture *textureObject = context->getTextureByType(type);
diff --git a/src/libGLESv2/egl_stubs.cpp b/src/libGLESv2/egl_stubs.cpp
index 0554b7f40c65d6a2690380fe7483818886e20533..645f53ba038e3a5ad580eead0d9135cd274c57f8 100644
--- a/src/libGLESv2/egl_stubs.cpp
+++ b/src/libGLESv2/egl_stubs.cpp
@@ -61,7 +61,7 @@ EGLBoolean BindTexImage(Thread *thread, Display *display, Surface *eglSurface, E
GetDisplayIfValid(display), EGL_FALSE);
gl::Context *context = thread->getContext();
- if (context)
+ if (context && !context->isContextLost())
{
gl::TextureType type =
egl_gl::EGLTextureTargetToTextureType(eglSurface->getTextureTarget());
@@ -573,15 +573,18 @@ EGLBoolean ReleaseTexImage(Thread *thread, Display *display, Surface *eglSurface
{
ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglReleaseTexImage",
GetDisplayIfValid(display), EGL_FALSE);
- gl::Texture *texture = eglSurface->getBoundTexture();
-
- if (texture)
+ gl::Context *context = thread->getContext();
+ if (context && !context->isContextLost())
{
- ANGLE_EGL_TRY_RETURN(thread, eglSurface->releaseTexImage(thread->getContext(), buffer),
- "eglReleaseTexImage", GetSurfaceIfValid(display, eglSurface),
- EGL_FALSE);
- }
+ gl::Texture *texture = eglSurface->getBoundTexture();
+ if (texture)
+ {
+ ANGLE_EGL_TRY_RETURN(thread, eglSurface->releaseTexImage(thread->getContext(), buffer),
+ "eglReleaseTexImage", GetSurfaceIfValid(display, eglSurface),
+ EGL_FALSE);
+ }
+ }
thread->setSuccess();
return EGL_TRUE;
}

View File

@@ -0,0 +1,27 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jamie Madill <jmadill@chromium.org>
Date: Mon, 2 May 2022 15:42:23 -0400
Subject: Fix validation cache when deleting a Transform Feedback.
Bug: chromium:1320024
Change-Id: I76ef85a3c65c663c138d8caebd4ef2c0da53cd4f
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3621780
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
(cherry picked from commit 84e42c3b04da9e2c9d93d35bb6f2b1830fef22f4)
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3650697
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 390f7bc6790abe5d84b05f97160966eca46fee05..86da3fd82fff38c911e8678a1f6c415583b38381 100755
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -3075,6 +3075,7 @@ void Context::detachTransformFeedback(TransformFeedbackID transformFeedback)
if (mState.removeTransformFeedbackBinding(this, transformFeedback))
{
bindTransformFeedback(GL_TRANSFORM_FEEDBACK, {0});
+ mStateCache.onActiveTransformFeedbackChange(this);
}
}

View File

@@ -0,0 +1,238 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jamie Madill <jmadill@chromium.org>
Date: Fri, 20 May 2022 10:26:15 -0400
Subject: D3D: Fix race condition with parallel shader compile.
Bug: chromium:1317673
Change-Id: I0fb7c9a66248852e41e8700e80c295393ef941e8
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3651153
Reviewed-by: Jie A Chen <jie.a.chen@intel.com>
Reviewed-by: Lingfeng Yang <lfy@google.com>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
(cherry picked from commit 4a20c9143abbf29c649cf643182735e8952089e3)
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3691050
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index 59fe34fc46a1bfbc7b4be1aad6ad84b6da303b5b..3256357a80d69739661ec1fd32220d3037145875 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -1687,12 +1687,6 @@ class ProgramD3D::GetVertexExecutableTask : public ProgramD3D::GetExecutableTask
angle::Result run() override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetVertexExecutableTask::run");
- if (!mProgram->mState.getAttachedShader(gl::ShaderType::Vertex))
- {
- return angle::Result::Continue;
- }
-
- mProgram->updateCachedInputLayoutFromShader();
ANGLE_TRY(mProgram->getVertexExecutableForCachedInputLayout(this, &mExecutable, &mInfoLog));
@@ -2147,6 +2141,11 @@ std::unique_ptr<LinkEvent> ProgramD3D::link(const gl::Context *context,
linkResources(resources);
+ if (mState.getAttachedShader(gl::ShaderType::Vertex))
+ {
+ updateCachedInputLayoutFromShader();
+ }
+
return compileProgramExecutables(context, infoLog);
}
}
diff --git a/src/tests/gl_tests/ParallelShaderCompileTest.cpp b/src/tests/gl_tests/ParallelShaderCompileTest.cpp
index bcd88ef01308759085c8244ad2058efe68686f2b..a98aff540c642617bba366b2e238519942d7e349 100644
--- a/src/tests/gl_tests/ParallelShaderCompileTest.cpp
+++ b/src/tests/gl_tests/ParallelShaderCompileTest.cpp
@@ -58,9 +58,10 @@ class ParallelShaderCompileTest : public ANGLETest
Task(int id) : mID(id) {}
virtual ~Task() {}
- virtual bool compile() = 0;
- virtual bool isCompileCompleted() = 0;
- virtual bool link() = 0;
+ virtual bool compile() = 0;
+ virtual bool isCompileCompleted() = 0;
+ virtual bool link() = 0;
+ virtual void postLink() {}
virtual void runAndVerify(ParallelShaderCompileTest *test) = 0;
bool isLinkCompleted()
@@ -71,7 +72,7 @@ class ParallelShaderCompileTest : public ANGLETest
}
protected:
- std::string insertRandomString(const std::string &source)
+ static std::string InsertRandomString(const std::string &source)
{
RNG rng;
std::ostringstream ostream;
@@ -80,7 +81,7 @@ class ParallelShaderCompileTest : public ANGLETest
return ostream.str();
}
- GLuint CompileShader(GLenum type, const std::string &source)
+ static GLuint CompileShader(GLenum type, const std::string &source)
{
GLuint shader = glCreateShader(type);
@@ -90,7 +91,14 @@ class ParallelShaderCompileTest : public ANGLETest
return shader;
}
- bool checkShader(GLuint shader)
+ static void RecompileShader(GLuint shader, const std::string &source)
+ {
+ const char *sourceArray[1] = {source.c_str()};
+ glShaderSource(shader, 1, sourceArray, nullptr);
+ glCompileShader(shader);
+ }
+
+ static bool CheckShader(GLuint shader)
{
GLint compileResult;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
@@ -129,7 +137,7 @@ class ParallelShaderCompileTest : public ANGLETest
TaskRunner() {}
~TaskRunner() {}
- void run(ParallelShaderCompileTest *test)
+ void run(ParallelShaderCompileTest *test, unsigned int pollInterval)
{
std::vector<std::unique_ptr<T>> compileTasks;
@@ -151,6 +159,7 @@ class ParallelShaderCompileTest : public ANGLETest
if (task->isCompileCompleted())
{
bool isLinking = task->link();
+ task->postLink();
ASSERT_TRUE(isLinking);
linkTasks.push_back(std::move(task));
compileTasks.erase(compileTasks.begin() + i);
@@ -158,7 +167,10 @@ class ParallelShaderCompileTest : public ANGLETest
}
++i;
}
- angle::Sleep(kPollInterval);
+ if (pollInterval != 0)
+ {
+ angle::Sleep(pollInterval);
+ }
}
while (!linkTasks.empty())
@@ -173,9 +185,16 @@ class ParallelShaderCompileTest : public ANGLETest
linkTasks.erase(linkTasks.begin() + i);
continue;
}
+ else
+ {
+ task->postLink();
+ }
++i;
}
- angle::Sleep(kPollInterval);
+ if (pollInterval != 0)
+ {
+ angle::Sleep(pollInterval);
+ }
}
}
};
@@ -192,9 +211,9 @@ class ParallelShaderCompileTest : public ANGLETest
bool compile() override
{
mVertexShader =
- CompileShader(GL_VERTEX_SHADER, insertRandomString(essl1_shaders::vs::Simple()));
+ CompileShader(GL_VERTEX_SHADER, InsertRandomString(essl1_shaders::vs::Simple()));
mFragmentShader = CompileShader(GL_FRAGMENT_SHADER,
- insertRandomString(essl1_shaders::fs::UniformColor()));
+ InsertRandomString(essl1_shaders::fs::UniformColor()));
return (mVertexShader != 0 && mFragmentShader != 0);
}
@@ -213,7 +232,7 @@ class ParallelShaderCompileTest : public ANGLETest
bool link() override
{
mProgram = 0;
- if (checkShader(mVertexShader) && checkShader(mFragmentShader))
+ if (CheckShader(mVertexShader) && CheckShader(mFragmentShader))
{
mProgram = glCreateProgram();
glAttachShader(mProgram, mVertexShader);
@@ -244,10 +263,25 @@ class ParallelShaderCompileTest : public ANGLETest
ASSERT_GL_NO_ERROR();
}
+ protected:
+ void recompile()
+ {
+ RecompileShader(mVertexShader, essl1_shaders::vs::Simple());
+ RecompileShader(mFragmentShader, essl1_shaders::fs::UniformColor());
+ }
+
private:
- GLColor mColor;
GLuint mVertexShader;
GLuint mFragmentShader;
+ GLColor mColor;
+ };
+
+ class ClearColorWithDrawRecompile : public ClearColorWithDraw
+ {
+ public:
+ ClearColorWithDrawRecompile(int taskID) : ClearColorWithDraw(taskID) {}
+
+ void postLink() override { recompile(); }
};
class ImageLoadStore : public Task
@@ -268,7 +302,7 @@ void main()
imageStore(uImage_2, ivec2(gl_LocalInvocationID.xy), value);
})";
- mShader = CompileShader(GL_COMPUTE_SHADER, insertRandomString(kCSSource));
+ mShader = CompileShader(GL_COMPUTE_SHADER, InsertRandomString(kCSSource));
return mShader != 0;
}
@@ -282,7 +316,7 @@ void main()
bool link() override
{
mProgram = 0;
- if (checkShader(mShader))
+ if (CheckShader(mShader))
{
mProgram = glCreateProgram();
glAttachShader(mProgram, mShader);
@@ -370,7 +404,18 @@ TEST_P(ParallelShaderCompileTest, LinkAndDrawManyPrograms)
ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
TaskRunner<ClearColorWithDraw> runner;
- runner.run(this);
+ runner.run(this, kPollInterval);
+}
+
+// Tests no crash in case that the Shader starts another compile while the Program being attached
+// to is still linking.
+// crbug.com/1317673
+TEST_P(ParallelShaderCompileTest, LinkProgramAndRecompileShader)
+{
+ ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
+
+ TaskRunner<ClearColorWithDrawRecompile> runner;
+ runner.run(this, 0);
}
class ParallelShaderCompileTestES31 : public ParallelShaderCompileTest
@@ -389,7 +434,7 @@ TEST_P(ParallelShaderCompileTestES31, LinkAndDispatchManyPrograms)
ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
TaskRunner<ImageLoadStore> runner;
- runner.run(this);
+ runner.run(this, kPollInterval);
}
ANGLE_INSTANTIATE_TEST_ES2(ParallelShaderCompileTest);

View File

@@ -0,0 +1,26 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: SeongHwan Park <ggabu423@gmail.com>
Date: Tue, 31 May 2022 02:41:32 +0900
Subject: Fix to invalidate cache when binding Transform Feedback.
Bug: chromium:1330379
Change-Id: I091116286ac511c50f9abcffa4d3cf350be920b4
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3677115
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
(cherry picked from commit d96cee6685099f6bcc392a4d20d28c8ec484673a)
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3691799
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 685b235cc5962c4f55f539e387dda8c7edb023bc..390f7bc6790abe5d84b05f97160966eca46fee05 100755
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -1323,6 +1323,7 @@ void Context::bindTransformFeedback(GLenum target, TransformFeedbackID transform
TransformFeedback *transformFeedback =
checkTransformFeedbackAllocation(transformFeedbackHandle);
mState.setTransformFeedbackBinding(this, transformFeedback);
+ mStateCache.onActiveTransformFeedbackChange(this);
}
void Context::bindProgramPipeline(ProgramPipelineID pipelineHandle)

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
@@ -119,3 +119,33 @@ cherry-pick-cf64617c1cc5.patch
cherry-pick-e2b8856012e0.patch
cherry-pick-6b66a45021a0.patch
fix_xkb_keysym_reverse_look_up_for_lacros.patch
custom_protocols_plzserviceworker.patch
pa_support_16kb_pagesize_on_linux_arm64.patch
cherry-pick-f1504440487f.patch
cherry-pick-21139756239b.patch
cherry-pick-2782c7bc5bbe.patch
cherry-pick-f3d01ff794dc.patch
cherry-pick-919b1ffe1fe7.patch
cherry-pick-f1dd785e021e.patch
cherry-pick-b03797bdb1df.patch
posix_replace_doubleforkandexec_with_forkandspawn.patch
cherry-pick-ecad352cd614.patch
cherry-pick-f427936d32db.patch
cherry-pick-22c61cfae5d1.patch
remove_default_window_title.patch
cherry-pick-d7a5d6b38ea8.patch
cherry-pick-22abbad430b6.patch
cherry-pick-3cbd5973d704.patch
cherry-pick-902f0d144a5b.patch
cherry-pick-664e0d8b4cfb.patch
chore_add_electron_deps_to_gitignores.patch
chore_allow_chromium_to_handle_synthetic_mouse_events_for_touch.patch
do_not_reduce_page_size_from_64k_to_4k_on_linux_arm64.patch
cherry-pick-94a8bdafc8c6.patch
fix_mac_build_with_enable_plugins_false.patch
fix_windows_build_with_enable_plugins_false.patch
m104_background_fetch_passing.patch
cherry-pick-c643d18a078d.patch
merge_104_speculative_fix_for_isvalidcodepointinindex_range_crash.patch
cherry-pick-60d8559e150a.patch
cherry-pick-54e32332750c.patch

View File

@@ -0,0 +1,114 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ari Chivukula <arichiv@chromium.org>
Date: Tue, 17 May 2022 18:17:07 +0000
Subject: {M102 PICK} [IndexedDB] Use-After-Free Fix
We can't guarantee order in the task the constructor dispatches the same
way we could before due to all the async changes. Let's be sure all the
objects exist before using them now. Long term, we need to address
ownership of the idb context.
Bug: 1324864, 1218100
Change-Id: Id5753297a4c966432028a1e7e063c5f1bed6f619
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3646994
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Commit-Queue: Srinivas Sista <srinivassista@chromium.org>
Cr-Commit-Position: refs/branch-heads/5005@{#812}
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index 9472e762729312bf68073c95056ce09e324148da..219904d80798f71f0be23ee9ffeb5c58d030fb55 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -167,38 +167,32 @@ IndexedDBContextImpl::IndexedDBContextImpl(
std::move(quota_client_remote),
storage::QuotaClientType::kIndexedDatabase,
{blink::mojom::StorageType::kTemporary});
+ IDBTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(&IndexedDBContextImpl::BindPipesOnIDBSequence,
+ weak_factory_.GetWeakPtr(),
+ std::move(quota_client_receiver),
+ std::move(blob_storage_context),
+ std::move(file_system_access_context)));
+}
- // This is safe because the IndexedDBContextImpl must be destructed on the
- // IDBTaskRunner, and this task will always happen before that.
- idb_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(
- [](mojo::Remote<storage::mojom::BlobStorageContext>*
- blob_storage_context,
- mojo::Remote<storage::mojom::FileSystemAccessContext>*
- file_system_access_context,
- mojo::Receiver<storage::mojom::QuotaClient>* quota_client_receiver,
- mojo::PendingRemote<storage::mojom::BlobStorageContext>
- pending_blob_storage_context,
- mojo::PendingRemote<storage::mojom::FileSystemAccessContext>
- pending_file_system_access_context,
- mojo::PendingReceiver<storage::mojom::QuotaClient>
- quota_client_pending_receiver) {
- quota_client_receiver->Bind(
- std::move(quota_client_pending_receiver));
- if (pending_blob_storage_context) {
- blob_storage_context->Bind(
- std::move(pending_blob_storage_context));
- }
- if (pending_file_system_access_context) {
- file_system_access_context->Bind(
- std::move(pending_file_system_access_context));
- }
- },
- &blob_storage_context_, &file_system_access_context_,
- &quota_client_receiver_, std::move(blob_storage_context),
- std::move(file_system_access_context),
- std::move(quota_client_receiver)));
+void IndexedDBContextImpl::BindPipesOnIDBSequence(
+ mojo::PendingReceiver<storage::mojom::QuotaClient>
+ pending_quota_client_receiver,
+ mojo::PendingRemote<storage::mojom::BlobStorageContext>
+ pending_blob_storage_context,
+ mojo::PendingRemote<storage::mojom::FileSystemAccessContext>
+ pending_file_system_access_context) {
+ DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
+ if (pending_quota_client_receiver) {
+ quota_client_receiver_.Bind(std::move(pending_quota_client_receiver));
+ }
+ if (pending_blob_storage_context) {
+ blob_storage_context_.Bind(std::move(pending_blob_storage_context));
+ }
+ if (pending_file_system_access_context) {
+ file_system_access_context_.Bind(
+ std::move(pending_file_system_access_context));
+ }
}
void IndexedDBContextImpl::Bind(
diff --git a/content/browser/indexed_db/indexed_db_context_impl.h b/content/browser/indexed_db/indexed_db_context_impl.h
index 4b874b244778d831e0fa8ca3bbfd7e751897923e..ad2f984e130ece867c0f0dc7ca9ae52b8ec12ae9 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.h
+++ b/content/browser/indexed_db/indexed_db_context_impl.h
@@ -224,6 +224,14 @@ class CONTENT_EXPORT IndexedDBContextImpl
~IndexedDBContextImpl() override;
+ void BindPipesOnIDBSequence(
+ mojo::PendingReceiver<storage::mojom::QuotaClient>
+ pending_quota_client_receiver,
+ mojo::PendingRemote<storage::mojom::BlobStorageContext>
+ pending_blob_storage_context,
+ mojo::PendingRemote<storage::mojom::FileSystemAccessContext>
+ pending_file_system_access_context);
+
// Binds receiver on bucket retrieval to ensure that a bucket always exists
// for a storage key.
void BindIndexedDBWithBucket(
@@ -282,6 +290,8 @@ class CONTENT_EXPORT IndexedDBContextImpl
mojo::Receiver<storage::mojom::QuotaClient> quota_client_receiver_;
const std::unique_ptr<storage::FilesystemProxy> filesystem_proxy_;
+ // weak_factory_->GetWeakPtr() may be used on any thread, but the resulting
+ // pointer must only be checked/used on idb_task_runner_.
base::WeakPtrFactory<IndexedDBContextImpl> weak_factory_{this};
};

View File

@@ -0,0 +1,427 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Austin Eng <enga@chromium.org>
Date: Thu, 23 Jun 2022 03:12:49 +0000
Subject: WebGPU: Mark the context lost on GPU context lost
M102 merge issues:
- dawn_control_client_holder.h/cc:
GetWGPUInstance() not present in M102
Fixes a bug where completely destructing the context instead of
marking it lost when receiving a context lost notification freed
memory still accessible by the page.
Fixed: 1336014
Change-Id: I662e531102af91362b4f62700bfbee507fc44d1f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3708646
Commit-Queue: Austin Eng <enga@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1017003}
(cherry picked from commit 6c7f327b7a15aabd3fc5d57e9c05b95d02f1cd36)
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 1c72562d4f58eb77742bfd9539e5d132683949fa..c56507ad59bc111a22c0abd072c1bd21ac3d8808 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -662,6 +662,7 @@ source_set("unit_tests") {
"//third_party/blink/renderer/modules/peerconnection:test_support",
"//third_party/blink/renderer/modules/storage:unit_tests",
"//third_party/blink/renderer/modules/webcodecs:unit_tests",
+ "//third_party/blink/renderer/modules/webgpu:unit_tests",
"//third_party/blink/renderer/modules/webtransport:unit_tests",
"//third_party/blink/renderer/platform",
"//third_party/blink/renderer/platform:test_support",
diff --git a/third_party/blink/renderer/modules/webgpu/BUILD.gn b/third_party/blink/renderer/modules/webgpu/BUILD.gn
index dcfece568d0e0e2bc5dbd6383fb1a0c2235a296c..71f2880467c50c145d5a68b4cfac62cba61f8e27 100644
--- a/third_party/blink/renderer/modules/webgpu/BUILD.gn
+++ b/third_party/blink/renderer/modules/webgpu/BUILD.gn
@@ -91,3 +91,17 @@ blink_modules_sources("webgpu") {
"//third_party/dawn/include/dawn:headers",
]
}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [ "gpu_context_lost_test.cc" ]
+
+ deps = [
+ "//base/test:test_support",
+ "//third_party/blink/renderer/controller:blink_bindings_test_sources",
+ "//third_party/blink/renderer/core",
+ "//third_party/blink/renderer/core:testing",
+ "//third_party/blink/renderer/modules",
+ "//third_party/blink/renderer/platform:test_support",
+ ]
+}
diff --git a/third_party/blink/renderer/modules/webgpu/DEPS b/third_party/blink/renderer/modules/webgpu/DEPS
index ded05aa68e59cadddb678d5254119ae3dad1b47e..9e777ab0473f0bb9cbd46e4d6c1498f1a12a6f40 100644
--- a/third_party/blink/renderer/modules/webgpu/DEPS
+++ b/third_party/blink/renderer/modules/webgpu/DEPS
@@ -9,6 +9,7 @@ include_rules = [
"+gpu/command_buffer/client/raster_interface.h",
"+gpu/command_buffer/client/shared_image_interface.h",
"+gpu/command_buffer/client/webgpu_interface.h",
+ "+gpu/command_buffer/client/webgpu_interface_stub.h",
"+media/base/video_frame.h",
"+media/renderers/paint_canvas_video_renderer.h",
"+services/metrics/public/cpp/ukm_builders.h",
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.cc b/third_party/blink/renderer/modules/webgpu/gpu.cc
index 8a1f4dcaf605bc656a6499bd415d9db4db9e8ed4..d86dd384e71894f9aaefaf4491c4fc2aabefbea9 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu.cc
@@ -287,4 +287,9 @@ void BoxedMappableWGPUBufferHandles::ClearAndDestroyAll(
contents_.clear();
}
+void GPU::SetDawnControlClientHolderForTesting(
+ scoped_refptr<DawnControlClientHolder> dawn_control_client) {
+ dawn_control_client_ = std::move(dawn_control_client);
+}
+
} // namespace blink
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.h b/third_party/blink/renderer/modules/webgpu/gpu.h
index a7348a1c18a2004734275cb4d6f1f4aa68b11d8e..17362a2ee1b3111b52c93ffd0e3222c3de893479 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu.h
@@ -9,6 +9,7 @@
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/supplementable.h"
@@ -47,9 +48,9 @@ struct BoxedMappableWGPUBufferHandles
HashSet<void*> contents_;
};
-class GPU final : public ScriptWrappable,
- public Supplement<NavigatorBase>,
- public ExecutionContextLifecycleObserver {
+class MODULES_EXPORT GPU final : public ScriptWrappable,
+ public Supplement<NavigatorBase>,
+ public ExecutionContextLifecycleObserver {
DEFINE_WRAPPERTYPEINFO();
public:
@@ -86,6 +87,9 @@ class GPU final : public ScriptWrappable,
return mappable_buffer_handles_.get();
}
+ void SetDawnControlClientHolderForTesting(
+ scoped_refptr<DawnControlClientHolder> dawn_control_client);
+
private:
void OnRequestAdapterCallback(ScriptState* script_state,
const GPURequestAdapterOptions* options,
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_context_lost_test.cc b/third_party/blink/renderer/modules/webgpu/gpu_context_lost_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1063295a543eaee5650cdaf5b115284b8c2f095e
--- /dev/null
+++ b/third_party/blink/renderer/modules/webgpu/gpu_context_lost_test.cc
@@ -0,0 +1,229 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/test/mock_callback.h"
+#include "gpu/command_buffer/client/webgpu_interface_stub.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/navigator.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/modules/webgpu/gpu.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer_test_helpers.h"
+
+namespace blink {
+
+namespace {
+
+class WebGPUContextProviderForTest
+ : public WebGraphicsContext3DProviderForTests {
+ public:
+ explicit WebGPUContextProviderForTest(
+ base::MockCallback<base::OnceClosure>* destruction_callback)
+ : WebGraphicsContext3DProviderForTests(
+ std::make_unique<gpu::webgpu::WebGPUInterfaceStub>()),
+ destruction_callback_(destruction_callback) {}
+ ~WebGPUContextProviderForTest() override {
+ if (destruction_callback_) {
+ destruction_callback_->Run();
+ }
+ }
+
+ static WebGPUContextProviderForTest* From(
+ scoped_refptr<DawnControlClientHolder>& dawn_control_client) {
+ return static_cast<WebGPUContextProviderForTest*>(
+ dawn_control_client->GetContextProviderWeakPtr()->ContextProvider());
+ }
+
+ void ClearDestructionCallback() { destruction_callback_ = nullptr; }
+
+ void SetLostContextCallback(
+ base::RepeatingClosure lost_context_callback) override {
+ lost_context_callback_ = std::move(lost_context_callback);
+ }
+
+ void CallLostContextCallback() { lost_context_callback_.Run(); }
+
+ private:
+ base::MockCallback<base::OnceClosure>* destruction_callback_;
+ base::RepeatingClosure lost_context_callback_;
+};
+
+class WebGPUContextLostTest : public testing::Test {
+ protected:
+ void SetUp() override { page_ = std::make_unique<DummyPageHolder>(); }
+
+ std::tuple<ExecutionContext*, GPU*> SetUpGPU(V8TestingScope* v8_test_scope) {
+ ExecutionContext* execution_context =
+ ExecutionContext::From(v8_test_scope->GetScriptState());
+
+ Navigator* navigator = page_->GetFrame().DomWindow()->navigator();
+ GPU* gpu = MakeGarbageCollected<GPU>(*navigator);
+ return std::make_tuple(execution_context, gpu);
+ }
+
+ std::unique_ptr<DummyPageHolder> page_;
+};
+
+// Test that the context provider is destructed after the last reference to
+// its owning DawnControlClientHolder is dropped.
+TEST_F(WebGPUContextLostTest, DestructedAfterLastRefDropped) {
+ V8TestingScope v8_test_scope;
+ ExecutionContext* execution_context =
+ ExecutionContext::From(v8_test_scope.GetScriptState());
+
+ base::MockCallback<base::OnceClosure> destruction_callback;
+ auto context_provider =
+ std::make_unique<WebGPUContextProviderForTest>(&destruction_callback);
+
+ auto dawn_control_client = DawnControlClientHolder::Create(
+ std::move(context_provider),
+ execution_context->GetTaskRunner(TaskType::kWebGPU));
+
+ // Drop the last reference to the DawnControlClientHolder which will
+ // now destroy the context provider.
+ EXPECT_CALL(destruction_callback, Run()).Times(1);
+ dawn_control_client = nullptr;
+}
+
+// Test that the GPU lost context callback marks the context lost, but does not
+// destruct it.
+TEST_F(WebGPUContextLostTest, GPULostContext) {
+ V8TestingScope v8_test_scope;
+ auto [execution_context, gpu] = SetUpGPU(&v8_test_scope);
+
+ base::MockCallback<base::OnceClosure> destruction_callback;
+ auto context_provider =
+ std::make_unique<WebGPUContextProviderForTest>(&destruction_callback);
+
+ auto dawn_control_client = DawnControlClientHolder::Create(
+ std::move(context_provider),
+ execution_context->GetTaskRunner(TaskType::kWebGPU));
+
+ gpu->SetDawnControlClientHolderForTesting(dawn_control_client);
+
+ // Trigger the lost context callback, but the context should not be destroyed.
+ EXPECT_CALL(destruction_callback, Run()).Times(0);
+ WebGPUContextProviderForTest::From(dawn_control_client)
+ ->CallLostContextCallback();
+ testing::Mock::VerifyAndClear(&destruction_callback);
+
+ // The context should be marked lost.
+ EXPECT_TRUE(dawn_control_client->IsContextLost());
+
+ // The context provider should still be live.
+ auto context_provider_weak_ptr =
+ dawn_control_client->GetContextProviderWeakPtr();
+ EXPECT_NE(context_provider_weak_ptr, nullptr);
+
+ // Clear the destruction callback since it is stack-allocated in this frame.
+ static_cast<WebGPUContextProviderForTest*>(
+ context_provider_weak_ptr->ContextProvider())
+ ->ClearDestructionCallback();
+}
+
+// Test that the GPU lost context callback marks the context lost, and then when
+// the context is recreated, the context still lives until the previous
+// DawnControlClientHolder is destroyed.
+TEST_F(WebGPUContextLostTest, RecreatedAfterGPULostContext) {
+ V8TestingScope v8_test_scope;
+ auto [execution_context, gpu] = SetUpGPU(&v8_test_scope);
+
+ base::MockCallback<base::OnceClosure> destruction_callback;
+ auto context_provider =
+ std::make_unique<WebGPUContextProviderForTest>(&destruction_callback);
+
+ auto dawn_control_client = DawnControlClientHolder::Create(
+ std::move(context_provider),
+ execution_context->GetTaskRunner(TaskType::kWebGPU));
+
+ gpu->SetDawnControlClientHolderForTesting(dawn_control_client);
+
+ // Trigger the lost context callback, but the context should not be destroyed.
+ EXPECT_CALL(destruction_callback, Run()).Times(0);
+ WebGPUContextProviderForTest::From(dawn_control_client)
+ ->CallLostContextCallback();
+ testing::Mock::VerifyAndClear(&destruction_callback);
+
+ // The context should be marked lost.
+ EXPECT_TRUE(dawn_control_client->IsContextLost());
+
+ // The context provider should still be live.
+ auto context_provider_weak_ptr =
+ dawn_control_client->GetContextProviderWeakPtr();
+ EXPECT_NE(context_provider_weak_ptr, nullptr);
+
+ // Make a new context provider and DawnControlClientHolder
+ base::MockCallback<base::OnceClosure> destruction_callback2;
+ auto context_provider2 =
+ std::make_unique<WebGPUContextProviderForTest>(&destruction_callback2);
+
+ auto dawn_control_client2 = DawnControlClientHolder::Create(
+ std::move(context_provider2),
+ execution_context->GetTaskRunner(TaskType::kWebGPU));
+
+ // Set the new context, but the previous context should still not be
+ // destroyed.
+ EXPECT_CALL(destruction_callback, Run()).Times(0);
+ gpu->SetDawnControlClientHolderForTesting(dawn_control_client2);
+ testing::Mock::VerifyAndClear(&destruction_callback);
+
+ // Drop the last reference to the previous DawnControlClientHolder which will
+ // now destroy the previous context provider.
+ EXPECT_CALL(destruction_callback, Run()).Times(1);
+ dawn_control_client = nullptr;
+ testing::Mock::VerifyAndClear(&destruction_callback);
+
+ // Clear the destruction callback since it is stack-allocated in this frame.
+ static_cast<WebGPUContextProviderForTest*>(
+ dawn_control_client2->GetContextProviderWeakPtr()->ContextProvider())
+ ->ClearDestructionCallback();
+}
+
+// Test that ContextDestroyed lifecycle event destructs the context.
+TEST_F(WebGPUContextLostTest, ContextDestroyed) {
+ V8TestingScope v8_test_scope;
+ auto [execution_context, gpu] = SetUpGPU(&v8_test_scope);
+
+ base::MockCallback<base::OnceClosure> destruction_callback;
+ auto context_provider =
+ std::make_unique<WebGPUContextProviderForTest>(&destruction_callback);
+
+ auto dawn_control_client = DawnControlClientHolder::Create(
+ std::move(context_provider),
+ execution_context->GetTaskRunner(TaskType::kWebGPU));
+
+ gpu->SetDawnControlClientHolderForTesting(dawn_control_client);
+
+ // Trigger the context destroyed lifecycle event. The context should not be
+ // destroyed yet.
+ EXPECT_CALL(destruction_callback, Run()).Times(0);
+ gpu->ContextDestroyed();
+ testing::Mock::VerifyAndClear(&destruction_callback);
+
+ // The context should be marked lost.
+ EXPECT_TRUE(dawn_control_client->IsContextLost());
+
+ // Getting the context provider should return null.
+ EXPECT_EQ(dawn_control_client->GetContextProviderWeakPtr(), nullptr);
+
+ // The context is destructed in a posted task with a fresh callstack to avoid
+ // re-entrancy issues. Expectations should resolve by the end of the next
+ // task.
+ EXPECT_CALL(destruction_callback, Run()).Times(1);
+ base::RunLoop loop;
+ execution_context->GetTaskRunner(TaskType::kWebGPU)
+ ->PostTask(FROM_HERE, loop.QuitClosure());
+ loop.Run();
+ testing::Mock::VerifyAndClear(&destruction_callback);
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc
index be5b392fa83a4197958f9f032eac4dd41af61f61..6a6283972e7bf597fdf720eefafb57dc4e3263a2 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.cc
@@ -17,9 +17,17 @@ scoped_refptr<DawnControlClientHolder> DawnControlClientHolder::Create(
auto dawn_control_client_holder =
base::MakeRefCounted<DawnControlClientHolder>(std::move(context_provider),
std::move(task_runner));
+ // The context lost callback occurs when the client receives
+ // OnGpuControlLostContext. This can happen on fatal errors when the GPU
+ // channel is disconnected: the GPU process crashes, the GPU process fails to
+ // deserialize a message, etc. We mark the context lost, but NOT destroy the
+ // entire WebGraphicsContext3DProvider as that would free services for mapping
+ // shared memory. There may still be outstanding mapped GPUBuffers pointing to
+ // this memory.
dawn_control_client_holder->context_provider_->ContextProvider()
->SetLostContextCallback(WTF::BindRepeating(
- &DawnControlClientHolder::Destroy, dawn_control_client_holder));
+ &DawnControlClientHolder::MarkContextLost,
+ dawn_control_client_holder->weak_ptr_factory_.GetWeakPtr()));
return dawn_control_client_holder;
}
@@ -38,7 +46,7 @@ DawnControlClientHolder::DawnControlClientHolder(
DawnControlClientHolder::~DawnControlClientHolder() = default;
void DawnControlClientHolder::Destroy() {
- api_channel_->Disconnect();
+ MarkContextLost();
// Destroy the WebGPU context.
// This ensures that GPU resources are eagerly reclaimed.
@@ -68,8 +76,16 @@ DawnControlClientHolder::GetContextProviderWeakPtr() const {
return context_provider_->GetWeakPtr();
}
+void DawnControlClientHolder::MarkContextLost() {
+ if (context_lost_) {
+ return;
+ }
+ api_channel_->Disconnect();
+ context_lost_ = true;
+}
+
bool DawnControlClientHolder::IsContextLost() const {
- return !context_provider_;
+ return context_lost_;
}
std::unique_ptr<RecyclableCanvasResource>
diff --git a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
index 550038892c97e8f5f46e7a08c07821aa083b67d1..9e3c086225aff661e2402b974534d27860f4f446 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h
@@ -47,6 +47,7 @@ class PLATFORM_EXPORT DawnControlClientHolder
base::WeakPtr<WebGraphicsContext3DProviderWrapper> GetContextProviderWeakPtr()
const;
const DawnProcTable& GetProcs() const { return procs_; }
+ void MarkContextLost();
bool IsContextLost() const;
std::unique_ptr<RecyclableCanvasResource> GetOrCreateCanvasResource(
const SkImageInfo& info,
@@ -56,11 +57,14 @@ class PLATFORM_EXPORT DawnControlClientHolder
friend class RefCounted<DawnControlClientHolder>;
~DawnControlClientHolder();
+ bool context_lost_ = false;
std::unique_ptr<WebGraphicsContext3DProviderWrapper> context_provider_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
scoped_refptr<gpu::webgpu::APIChannel> api_channel_;
DawnProcTable procs_;
WebGPURecyclableResourceCache recyclable_resource_cache_;
+
+ base::WeakPtrFactory<DawnControlClientHolder> weak_ptr_factory_{this};
};
} // namespace blink

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 86b9af148a86e64f9f4aedb6e39998bf83668745..f6018224c5f0aea3aba6735d49aaa25d077de600 100644
--- a/content/browser/file_system_access/file_system_chooser.cc
+++ b/content/browser/file_system_access/file_system_chooser.cc
@@ -276,13 +276,15 @@ bool FileSystemChooser::IsShellIntegratedExtension(
base::FilePath::StringType extension_lower =
base::ToLowerASCII(GetLastExtension(extension));
- // .lnk and .scf files may be used to execute arbitrary code (see
+ // '.lnk' and '.scf' files may be used to execute arbitrary code (see
// https://nvd.nist.gov/vuln/detail/CVE-2010-2568 and
- // https://crbug.com/1227995, respectively). .local files are used by Windows
- // to determine which DLLs to load for an application.
+ // https://crbug.com/1227995, respectively). '.local' files are used by
+ // Windows to determine which DLLs to load for an application. '.url' files
+ // can be used to read arbirtary files (see https://crbug.com/1307930).
if ((extension_lower == FILE_PATH_LITERAL("lnk")) ||
(extension_lower == FILE_PATH_LITERAL("local")) ||
- (extension_lower == FILE_PATH_LITERAL("scf"))) {
+ (extension_lower == FILE_PATH_LITERAL("scf")) ||
+ (extension_lower == FILE_PATH_LITERAL("url"))) {
return true;
}
diff --git a/content/browser/file_system_access/file_system_chooser_browsertest.cc b/content/browser/file_system_access/file_system_chooser_browsertest.cc
index 9ea4db7807f6bbac799452fd138848b2a650d6fd..79dda31bd228e785d54e5486bb4417a75ee62b3a 100644
--- a/content/browser/file_system_access/file_system_chooser_browsertest.cc
+++ b/content/browser/file_system_access/file_system_chooser_browsertest.cc
@@ -1556,13 +1556,21 @@ IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SuggestedName) {
name_infos.push_back({"not_matching.jpg", ListValueOf(".txt"), false,
"not_matching.jpg", false});
- // ".lnk", ".local", and ".scf" extensions should be sanitized.
- name_infos.push_back({"dangerous_extension.local", ListValueOf(".local"),
- true, "dangerous_extension.download", false});
+ // ".lnk", ".local", ".scf", and ".url" extensions should be sanitized.
name_infos.push_back({"dangerous_extension.lnk", ListValueOf(".lnk"), true,
"dangerous_extension.download", false});
+ name_infos.push_back({"dangerous_extension.lnk", ListValueOf(".LNK"), true,
+ "dangerous_extension.download", false});
+ name_infos.push_back({"dangerous_extension.LNK", ListValueOf(".lnk"), true,
+ "dangerous_extension.download", false});
+ name_infos.push_back({"dangerous_extension.LNK", ListValueOf(".LNK"), true,
+ "dangerous_extension.download", false});
+ name_infos.push_back({"dangerous_extension.local", ListValueOf(".local"),
+ true, "dangerous_extension.download", false});
name_infos.push_back({"dangerous_extension.scf", ListValueOf(".scf"), true,
"dangerous_extension.download", false});
+ name_infos.push_back({"dangerous_extension.url", ListValueOf(".url"), true,
+ "dangerous_extension.download", false});
// Compound extensions ending in a dangerous extension should be sanitized.
name_infos.push_back({"dangerous_extension.png.local", ListValueOf(".local"),
true, "dangerous_extension.png.download", false});
@@ -1570,6 +1578,8 @@ IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SuggestedName) {
true, "dangerous_extension.png.download", false});
name_infos.push_back({"dangerous_extension.png.scf", ListValueOf(".scf"),
true, "dangerous_extension.png.download", false});
+ name_infos.push_back({"dangerous_extension.png.url", ListValueOf(".url"),
+ true, "dangerous_extension.png.download", false});
// Compound extensions not ending in a dangerous extension should not be
// sanitized.
name_infos.push_back({"dangerous_extension.local.png", ListValueOf(".png"),
@@ -1578,6 +1588,8 @@ IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SuggestedName) {
true, "dangerous_extension.lnk.png", true});
name_infos.push_back({"dangerous_extension.scf.png", ListValueOf(".png"),
true, "dangerous_extension.scf.png", true});
+ name_infos.push_back({"dangerous_extension.url.png", ListValueOf(".png"),
+ true, "dangerous_extension.url.png", true});
// Invalid characters should be sanitized.
name_infos.push_back({R"(inv*l:d\\ch%rבאמת!a<ters🤓.txt)",
ListValueOf(".txt"), true,
diff --git a/content/browser/file_system_access/file_system_chooser_unittest.cc b/content/browser/file_system_access/file_system_chooser_unittest.cc
index 9b27d6305bd00a19d94b5ec49f21a7ecff7ddc48..3082c088f9733de9335437278c0d023954a953d9 100644
--- a/content/browser/file_system_access/file_system_chooser_unittest.cc
+++ b/content/browser/file_system_access/file_system_chooser_unittest.cc
@@ -189,7 +189,7 @@ TEST_F(FileSystemChooserTest, IgnoreShellIntegratedExtensions) {
accepts.emplace_back(blink::mojom::ChooseFileSystemEntryAcceptsOption::New(
u"", std::vector<std::string>({}),
std::vector<std::string>(
- {"lnk", "foo.lnk", "foo.bar.local", "text", "local", "scf"})));
+ {"lnk", "foo.lnk", "foo.bar.local", "text", "local", "scf", "url"})));
SyncShowDialog(std::move(accepts), /*include_accepts_all=*/false);
ASSERT_TRUE(dialog_params.file_types);

View File

@@ -0,0 +1,139 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Marijn Kruisselbrink <mek@chromium.org>
Date: Wed, 27 Apr 2022 20:51:50 +0000
Subject: Reland "Close a MessagePort if it is created in a destroyed context."
This is a reland of commit 068f13cc5aa5f7a6e9faf28d8731275e64cb657b
This reland changes the timeout in the test from 3 to 2 seconds, because
two 3 second timeouts is too long for chrome's default overall test
timeout of 6 seconds on non-dcheck release builds.
Original change's description:
> Close a MessagePort if it is created in a destroyed context.
>
> MessagePort assumes it is only destroyed either after ContextDestroyed,
> or after the port has been closed explicitly. As it turns out ports that
> were created in an already detached iframe would violate this invariant,
> causing issues.
>
> Bug: 1228661
> Change-Id: Ib1abce15f1d1d15f044de19fe0534767db488af0
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3561845
> Reviewed-by: Jeremy Roman <jbroman@chromium.org>
> Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#988859}
Bug: 1228661
Change-Id: Ifc5ec866678667b0d81438e2a2c8e5ada6e19d8c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3609249
Commit-Queue: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Auto-Submit: Marijn Kruisselbrink <mek@chromium.org>
Cr-Commit-Position: refs/heads/main@{#996880}
diff --git a/third_party/blink/renderer/core/messaging/message_port.cc b/third_party/blink/renderer/core/messaging/message_port.cc
index 6f67b21803fcdc2498ef207878d1541e04822fca..7f3df8ea567f91cd063122aab63a5c5424ab7919 100644
--- a/third_party/blink/renderer/core/messaging/message_port.cc
+++ b/third_party/blink/renderer/core/messaging/message_port.cc
@@ -55,7 +55,11 @@
namespace blink {
MessagePort::MessagePort(ExecutionContext& execution_context)
- : ExecutionContextLifecycleObserver(&execution_context),
+ : ExecutionContextLifecycleObserver(execution_context.IsContextDestroyed()
+ ? nullptr
+ : &execution_context),
+ // Ports in a destroyed context start out in a closed state.
+ closed_(execution_context.IsContextDestroyed()),
task_runner_(execution_context.GetTaskRunner(TaskType::kPostedMessage)) {}
MessagePort::~MessagePort() {
@@ -168,10 +172,21 @@ void MessagePort::Entangle(MessagePortDescriptor port) {
DCHECK(port.IsValid());
DCHECK(!connector_);
+ // If the context was already destroyed, there is no reason to actually
+ // entangle the port and create a Connector. No messages will ever be able to
+ // be sent or received anyway, as StartReceiving will never be called.
+ if (!GetExecutionContext())
+ return;
+
port_ = std::move(port);
connector_ = std::make_unique<mojo::Connector>(
port_.TakeHandleToEntangle(GetExecutionContext()),
mojo::Connector::SINGLE_THREADED_SEND);
+ // The raw `this` is safe despite `this` being a garbage collected object
+ // because we make sure that:
+ // 1. This object will not be garbage collected while it is connected and
+ // the execution context is not destroyed, and
+ // 2. when the execution context is destroyed, the connector_ is reset.
connector_->set_incoming_receiver(this);
connector_->set_connection_error_handler(
WTF::Bind(&MessagePort::close, WrapWeakPersistent(this)));
diff --git a/third_party/blink/renderer/core/messaging/message_port.h b/third_party/blink/renderer/core/messaging/message_port.h
index 83d7901d99ad01ba039ea1ffa3dbee2595fc31ff..f9baba3c6d13992508da48a13c97bb10c8ec56e0 100644
--- a/third_party/blink/renderer/core/messaging/message_port.h
+++ b/third_party/blink/renderer/core/messaging/message_port.h
@@ -148,7 +148,7 @@ class CORE_EXPORT MessagePort : public EventTargetWithInlineData,
std::unique_ptr<mojo::Connector> connector_;
bool started_ = false;
- bool closed_ = false;
+ bool closed_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/third_party/blink/web_tests/external/wpt/webmessaging/message-channels/detached-iframe.window.js b/third_party/blink/web_tests/external/wpt/webmessaging/message-channels/detached-iframe.window.js
new file mode 100644
index 0000000000000000000000000000000000000000..c1effaf141b7246320883e293b58dabbc3572123
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webmessaging/message-channels/detached-iframe.window.js
@@ -0,0 +1,47 @@
+// META: title=MessageChannel in a detached iframe test
+// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
+// Pull in the with_iframe helper function from the service worker tests
+
+
+const IframeAction = {
+ REMOVE_BEFORE_CREATION: 'remove-before-creation',
+ REMOVE_AFTER_CREATION: 'remove-after-creation',
+};
+
+async function detached_frame_test(t, action) {
+ const iframe = await with_iframe('about:blank');
+ const iframe_MessageChannel = iframe.contentWindow.MessageChannel;
+
+ if (action === IframeAction.REMOVE_BEFORE_CREATION) {
+ iframe.remove();
+ }
+
+ (() => {
+ const mc = new iframe_MessageChannel();
+ mc.port1.postMessage("boo");
+ mc.port2.onmessage = t.unreached_func("message event received");
+ mc.port2.onmessageerror = t.unreached_func("message event received");
+ })();
+
+ if (action === IframeAction.REMOVE_AFTER_CREATION) {
+ iframe.remove();
+ }
+
+ // TODO(https://github.com/web-platform-tests/wpt/issues/7899): Change to
+ // some sort of cross-browser GC trigger.
+ if (self.gc) self.gc();
+
+ // We are testing that neither of the above two events fire. We assume that a 2 second timeout
+ // is good enough. We can't use any other API for an end condition because each MessagePort has
+ // its own independent port message queue, which has no ordering guarantees relative to other
+ // APIs.
+ await new Promise(resolve => t.step_timeout(resolve, 2000));
+}
+
+promise_test(async (t) => {
+ return detached_frame_test(t, IframeAction.REMOVE_AFTER_CREATION);
+}, 'MessageChannel created from a detached iframe should not send messages (remove after create)');
+
+promise_test(async (t) => {
+ return detached_frame_test(t, IframeAction.REMOVE_BEFORE_CREATION);
+}, 'MessageChannel created from a detached iframe should not send messages (remove before create)');

View File

@@ -0,0 +1,102 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
Date: Mon, 18 Jul 2022 20:44:41 +0000
Subject: Keep refptr to ServiceWorkerVersion in MaybeTimeoutRequest
The callback in ServiceWorkerVersion::MaybeTimeoutRequest may reduce the
reference to the version object, which can be the last reference to it.
In thet case, the object can be destroyed and the `inflight_requests_`
field access after the callback become invalid.
This CL keeps this to avoid the object destruction.
(cherry picked from commit 5926fa916d9ad53c77e31ee757e1979275d7466c)
Bug: 1339844
Change-Id: I6564627bad0527dea007ca73261c5636dab56755
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3739330
Commit-Queue: Yoshisato Yanagisawa <yyanagisawa@chromium.org>
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
Reviewed-by: Sergei Glazunov <glazunov@google.com>
Cr-Original-Commit-Position: refs/heads/main@{#1023475}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3766241
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Owners-Override: Srinivas Sista <srinivassista@chromium.org>
Cr-Commit-Position: refs/branch-heads/5005@{#1263}
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 0b4b32d13db8958f579085a41bc5e20b26543f61..5797e3f954ae0769a91c8f259e19e50e2bf31a94 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -1993,18 +1993,17 @@ void ServiceWorkerVersion::OnTimeoutTimer() {
MarkIfStale();
+ // Global `this` protecter.
+ // callbacks initiated by this function sometimes reduce refcnt to 0
+ // to make this instance freed.
+ scoped_refptr<ServiceWorkerVersion> protect_this(this);
+
// Stopping the worker hasn't finished within a certain period.
if (GetTickDuration(stop_time_) > kStopWorkerTimeout) {
DCHECK_EQ(EmbeddedWorkerStatus::STOPPING, running_status());
ReportError(blink::ServiceWorkerStatusCode::kErrorTimeout,
"DETACH_STALLED_IN_STOPPING");
- // Detach the worker. Remove |this| as a listener first; otherwise
- // OnStoppedInternal might try to restart before the new worker
- // is created. Also, protect |this|, since swapping out the
- // EmbeddedWorkerInstance could destroy our ServiceWorkerHost which could in
- // turn destroy |this|.
- scoped_refptr<ServiceWorkerVersion> protect_this(this);
embedded_worker_->RemoveObserver(this);
embedded_worker_->Detach();
embedded_worker_ = std::make_unique<EmbeddedWorkerInstance>(this);
@@ -2031,7 +2030,6 @@ void ServiceWorkerVersion::OnTimeoutTimer() {
DCHECK(running_status() == EmbeddedWorkerStatus::STARTING ||
running_status() == EmbeddedWorkerStatus::STOPPING)
<< static_cast<int>(running_status());
- scoped_refptr<ServiceWorkerVersion> protect(this);
FinishStartWorker(blink::ServiceWorkerStatusCode::kErrorTimeout);
if (running_status() == EmbeddedWorkerStatus::STARTING)
embedded_worker_->Stop();
@@ -2040,17 +2038,22 @@ void ServiceWorkerVersion::OnTimeoutTimer() {
// Requests have not finished before their expiration.
bool stop_for_timeout = false;
- auto timeout_iter = request_timeouts_.begin();
- while (timeout_iter != request_timeouts_.end()) {
+ std::set<InflightRequestTimeoutInfo> request_timeouts;
+ request_timeouts.swap(request_timeouts_);
+ auto timeout_iter = request_timeouts.begin();
+ while (timeout_iter != request_timeouts.end()) {
const InflightRequestTimeoutInfo& info = *timeout_iter;
- if (!RequestExpired(info.expiration))
+ if (!RequestExpired(info.expiration)) {
break;
+ }
if (MaybeTimeoutRequest(info)) {
stop_for_timeout =
stop_for_timeout || info.timeout_behavior == KILL_ON_TIMEOUT;
}
- timeout_iter = request_timeouts_.erase(timeout_iter);
+ timeout_iter = request_timeouts.erase(timeout_iter);
}
+ DCHECK(request_timeouts_.empty());
+ request_timeouts_.swap(request_timeouts);
if (stop_for_timeout && running_status() != EmbeddedWorkerStatus::STOPPING)
embedded_worker_->Stop();
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index dcf0dfaf515f8058438d727b30fb4c508a7211a9..a88bc2513f988a00b538baa596a1e713c2600cf8 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -863,6 +863,8 @@ class CONTENT_EXPORT ServiceWorkerVersion
bool is_browser_startup_complete,
blink::ServiceWorkerStatusCode status);
+ // The caller of MaybeTimeoutRequest must increase reference count of |this|
+ // to avoid it deleted during the execution.
bool MaybeTimeoutRequest(const InflightRequestTimeoutInfo& info);
void SetAllRequestExpirations(const base::TimeTicks& expiration);

View File

@@ -0,0 +1,141 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Min Qin <qinmin@chromium.org>
Date: Tue, 12 Jul 2022 19:48:53 +0000
Subject: Sanitize default file name in windows select file dialog
On windows, '%' is a special character and can be used for environment
variables. So if the default file name is '%DATADIR%', it can actually
refer to another directory and thus causing weird behaviors.
And '%' cannot be escaped when used in the file dialog. Both "^%" and
"%%" don't work. This CL mitigates the issue by replacing '%' with '_'.
This only affects the default file name when showing the dialog. Power
users can still change the file name by adding '%' if needed.
BUG=1308422
(cherry picked from commit 9cdce354cb3b0da5b4b311638973d407be7712b6)
Change-Id: Ibb275f5c3c2c9458c20d1e97ad527f7c95184eaa
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3688608
Reviewed-by: Robert Liao <robliao@chromium.org>
Commit-Queue: Min Qin <qinmin@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1014602}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3758469
Cr-Commit-Position: refs/branch-heads/5112@{#822}
Cr-Branched-From: b13d3fe7b3c47a56354ef54b221008afa754412e-refs/heads/main@{#1012729}
diff --git a/ui/shell_dialogs/execute_select_file_win.cc b/ui/shell_dialogs/execute_select_file_win.cc
index 063d4c7c96cba45448d4d7b0e7a8ddd3a6f67937..162dbc3aeb4ead16a415228b40fb67008e89fe63 100644
--- a/ui/shell_dialogs/execute_select_file_win.cc
+++ b/ui/shell_dialogs/execute_select_file_win.cc
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
+#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/win/com_init_util.h"
#include "base/win/registry.h"
@@ -50,7 +51,7 @@ bool SetDefaultPath(IFileDialog* file_dialog,
default_folder = default_path;
} else {
default_folder = default_path.DirName();
- default_file_name = default_path.BaseName();
+ default_file_name = GetSanitizedFileName(default_path.BaseName());
}
// Do not fail the file dialog operation if the specified folder is invalid.
@@ -371,6 +372,33 @@ std::wstring AppendExtensionIfNeeded(const std::wstring& filename,
return return_value;
}
+base::FilePath GetSanitizedFileName(const base::FilePath& file_name) {
+ base::StringTokenizerT<std::wstring, std::wstring::const_iterator> t(
+ file_name.value(), L"%");
+ t.set_options(base::StringTokenizer::RETURN_EMPTY_TOKENS);
+ std::wstring result;
+ bool token_valid = t.GetNext();
+ while (token_valid) {
+ // Append substring before the first "%".
+ result.append(t.token());
+ // Done if we are reaching the end delimiter,
+ if (!t.GetNext()) {
+ break;
+ }
+ std::wstring string_after_first_percent = t.token();
+ token_valid = t.GetNext();
+ // If there are no other "%", append the string after
+ // the first "%". Otherwise, remove the string between
+ // the "%" and continue handing the remaining string.
+ if (!token_valid) {
+ result.append(L"%");
+ result.append(string_after_first_percent);
+ break;
+ }
+ }
+ return base::FilePath(result);
+}
+
void ExecuteSelectFile(
SelectFileDialog::Type type,
const std::u16string& title,
diff --git a/ui/shell_dialogs/execute_select_file_win.h b/ui/shell_dialogs/execute_select_file_win.h
index dc808af395333a2bd81c2bd73faac4c6063fd409..8fa96906c0288d0021c1500bd442a35c0c29dfd2 100644
--- a/ui/shell_dialogs/execute_select_file_win.h
+++ b/ui/shell_dialogs/execute_select_file_win.h
@@ -26,6 +26,12 @@ SHELL_DIALOGS_EXPORT std::wstring AppendExtensionIfNeeded(
const std::wstring& filter_selected,
const std::wstring& suggested_ext);
+// Given a file name, return the sanitized version by removing substrings that
+// are embedded in double '%' characters as those are reserved for environment
+// variables. Implementation detail exported for unit tests.
+SHELL_DIALOGS_EXPORT base::FilePath GetSanitizedFileName(
+ const base::FilePath& file_name);
+
// Describes a filter for a file dialog.
struct FileFilterSpec {
// A human readable description of this filter. E.g. "HTML Files."
diff --git a/ui/shell_dialogs/execute_select_file_win_unittest.cc b/ui/shell_dialogs/execute_select_file_win_unittest.cc
index 8ea243924a1834dbe88dd53c5c6e8bde46d33b08..6237bf7d640e55929fb3c549674520f20adb0c94 100644
--- a/ui/shell_dialogs/execute_select_file_win_unittest.cc
+++ b/ui/shell_dialogs/execute_select_file_win_unittest.cc
@@ -51,3 +51,38 @@ TEST(ShellDialogsWin, AppendExtensionIfNeeded) {
test_cases[i].suggested_ext));
}
}
+
+TEST(ShellDialogsWin, GetSanitizedFileName) {
+ struct GetSanitizedFileNameTestCase {
+ const wchar_t* filename;
+ const wchar_t* sanitized_filename;
+ } test_cases[] = {
+ {L"", L""},
+ {L"a.txt", L"a.txt"},
+
+ // Only 1 "%" in file name.
+ {L"%", L"%"},
+ {L"%.txt", L"%.txt"},
+ {L"ab%c.txt", L"ab%c.txt"},
+ {L"abc.t%", L"abc.t%"},
+
+ // 2 "%" in file name.
+ {L"%%", L""},
+ {L"%c%", L""},
+ {L"%c%d", L"d"},
+ {L"d%c%.txt", L"d.txt"},
+ {L"ab%c.t%", L"ab"},
+ {L"abc.%t%", L"abc."},
+
+ // More than 2 "%" in file name.
+ {L"%ab%c%.txt", L"c%.txt"},
+ {L"%abc%.%txt%", L"."},
+ {L"%ab%c%.%txt%", L"ctxt%"},
+ };
+
+ for (size_t i = 0; i < std::size(test_cases); ++i) {
+ SCOPED_TRACE(base::StringPrintf("i=%zu", i));
+ EXPECT_EQ(base::FilePath(test_cases[i].sanitized_filename),
+ ui::GetSanitizedFileName(base::FilePath(test_cases[i].filename)));
+ }
+}

View File

@@ -0,0 +1,41 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lei Zhang <thestig@chromium.org>
Date: Tue, 12 Jul 2022 18:52:14 +0000
Subject: M104: Better define "first result" in PDFiumEngine::AddFindResult().
Currently, changing the PDF layout confuses AddFindResult() and causes
it to fail a DCHECK(). Adjust AddFindResult() to avoid the failing
DCHECK().
This is a cherry-pick of https://crrev.com/1021389 without the test
changes.
Bug: 1339745
Change-Id: I25c2b6b436700f9aeca4924fef662ad2909f0a8c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3758626
Reviewed-by: K. Moon <kmoon@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/branch-heads/5112@{#820}
Cr-Branched-From: b13d3fe7b3c47a56354ef54b221008afa754412e-refs/heads/main@{#1012729}
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 0f8171dd0dd0b438a0406535e5693b22358d81e5..7d90add9e441451e3ae23dc5de4fa305819e0590 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -1981,7 +1981,7 @@ void PDFiumEngine::SearchUsingICU(const std::u16string& term,
}
void PDFiumEngine::AddFindResult(const PDFiumRange& result) {
- bool first_result = find_results_.empty();
+ bool first_result = find_results_.empty() && !resume_find_index_.has_value();
// Figure out where to insert the new location, since we could have
// started searching midway and now we wrapped.
size_t result_index;
@@ -1998,7 +1998,6 @@ void PDFiumEngine::AddFindResult(const PDFiumRange& result) {
UpdateTickMarks();
client_->NotifyNumberOfFindResultsChanged(find_results_.size(), false);
if (first_result) {
- DCHECK(!resume_find_index_);
DCHECK(!current_find_index_);
SelectFindResult(/*forward=*/true);
}

View File

@@ -0,0 +1,76 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Etienne Bergeron <etienneb@chromium.org>
Date: Tue, 19 Jul 2022 16:46:43 +0000
Subject: Fix incorrect text itemization for \r codepoint
M96 merge issues:
render_text_unittest.cc
Tests Clusterfuzz_Issue_1298286/1299054 aren't present
in M96 and caused a merge conflict.
The "\r" codepoint should be split to be rendered in a single
harfbuzz run (same as "\n").
We do recognize these sequences as newline:
\r
\n
\r\n
Previously, the itemization will leave the "\r" with the previous
run. This is leading to incorrect multiline lines splitting.
(cherry picked from commit eee0c5ca752ad50df9986c551cb98226ce078893)
Bug: 1287804
Change-Id: Idfc00a3cf147eb53258d5da9ea105e2d6dc25f05
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3708936
Commit-Queue: Etienne Bergeron <etienneb@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1014955}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3726349
Reviewed-by: Etienne Bergeron <etienneb@chromium.org>
Commit-Queue: Roger Felipe Zanoni da Silva <rzanoni@google.com>
Cr-Commit-Position: refs/branch-heads/4664@{#1662}
Cr-Branched-From: 24dc4ee75e01a29d390d43c9c264372a169273a7-refs/heads/main@{#929512}
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index cd19053ad39a8386b348ae89fb2cf59bcfddb01a..cd779904454940f744cd82c5365ece7147633c2b 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -201,7 +201,7 @@ GraphemeProperties RetrieveGraphemeProperties(const base::StringPiece16& text,
properties.block = ublock_getCode(codepoint);
}
- if (codepoint == '\n' || codepoint == ' ')
+ if (codepoint == '\n' || codepoint == '\r' || codepoint == ' ')
properties.has_control = true;
if (IsBracket(codepoint))
properties.has_bracket = true;
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 43d03699495c935cd1203e8195d12571fa92b347..e33d25270efb49ee8498108dfa0473b4f760d732 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -1575,6 +1575,9 @@ const RunListCase kBasicsRunListCases[] = {
{"multiline_newline1", u"\n\n", "[0][1]", true},
{"multiline_newline2", u"\r\n\r\n", "[0->1][2->3]", true},
{"multiline_newline3", u"\r\r\n", "[0][1->2]", true},
+ {"multiline_newline4", u"x\r\r", "[0][1][2]", true},
+ {"multiline_newline5", u"x\n\r\r", "[0][1][2][3]", true},
+ {"multiline_newline6", u"x\ny\rz\r\n", "[0][1][2][3][4][5->6]", true},
};
INSTANTIATE_TEST_SUITE_P(ItemizeTextToRunsBasics,
@@ -8561,4 +8564,14 @@ TEST_F(RenderTextTest, StringSizeUpdatedWhenDeviceScaleFactorChanges) {
}
#endif
+TEST_F(RenderTextTest, Clusterfuzz_Issue_1287804) {
+ RenderText* render_text = GetRenderText();
+ render_text->SetMaxLines(1);
+ render_text->SetText(u">\r\r");
+ render_text->SetMultiline(true);
+ render_text->SetDisplayRect(Rect(0, 0, 100, 24));
+ render_text->SetElideBehavior(ELIDE_TAIL);
+ EXPECT_EQ(RangeF(0, 0), render_text->GetCursorSpan(Range(0, 0)));
+}
+
} // namespace gfx

View File

@@ -0,0 +1,41 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Justin Novosad <junov@chromium.org>
Date: Fri, 15 Jul 2022 23:12:50 +0000
Subject: Mitigate bad cast in OffscreenCanvas::GetFontSelector
This change will cause the browser to crash if the execution context
is not a Window or WorkerGlobalScope. This is a temporary solution
to handle the case where the execution context is an
AudioWorkletGlobalScope. The longer term solution, which will be
implemented in a follow-up CL, is to block OffscreenCanvas objects from
being transferred to AudioWorklets, as required by the postMessage spec.
BUG=1334864
(cherry picked from commit 028c11e59fd41bc22eff06dbec10fe9b0e82bd04)
Change-Id: Ief5e37eca6dff14098b12cdbe6fc362c3dd87d1d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3722117
Auto-Submit: Justin Novosad <junov@chromium.org>
Reviewed-by: Juanmi Huertas <juanmihd@chromium.org>
Commit-Queue: Juanmi Huertas <juanmihd@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1017357}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3752921
Commit-Queue: Srinivas Sista <srinivassista@chromium.org>
Cr-Commit-Position: refs/branch-heads/5005@{#1254}
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
index 584920aad8a3bfd9f00bf0db99fdf1891b13176b..4625aec940a349e94f591e2b96801c99dcb3ef05 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
@@ -548,6 +548,9 @@ FontSelector* OffscreenCanvas::GetFontSelector() {
if (auto* window = DynamicTo<LocalDOMWindow>(GetExecutionContext())) {
return window->document()->GetStyleEngine().GetFontSelector();
}
+ // TODO(crbug.com/1334864): Temporary mitigation. Remove the following
+ // CHECK once a more comprehensive solution has been implemented.
+ CHECK(GetExecutionContext()->IsWorkerGlobalScope());
return To<WorkerGlobalScope>(GetExecutionContext())->GetFontSelector();
}

View File

@@ -0,0 +1,393 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alex Rudenko <alexrudenko@chromium.org>
Date: Tue, 26 Apr 2022 05:53:56 +0000
Subject: DevTools: store weak references to nodes in inspect_tools
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This CL changes how the configuration for persistent overlays is stored.
Instead of trying to save a strong reference to a DOM node, we can
store weak references instead. This requires using a HeapHashMap as
vectors don't support weak reference elements.
(cherry picked from commit 2e0a0b83e1340e948a27cdc319bea8f32849d414)
Fixed: 1314310
Change-Id: I27ce12730d7598bc84d01adf421923af9a53dc67
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3593092
Reviewed-by: Danil Somsikov <dsv@chromium.org>
Reviewed-by: Changhao Han <changhaohan@chromium.org>
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Commit-Queue: Alex Rudenko <alexrudenko@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#994866}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3604262
Auto-Submit: Alex Rudenko <alexrudenko@chromium.org>
Reviewed-by: Simon Zünd <szuend@chromium.org>
Commit-Queue: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/branch-heads/5005@{#163}
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
diff --git a/third_party/blink/renderer/core/inspector/inspect_tools.cc b/third_party/blink/renderer/core/inspector/inspect_tools.cc
index b3c08bd77e792481ee7ce5754e48a0d429ef0595..6a43cc041e1adefcfca56dac86eb138c0b118127 100644
--- a/third_party/blink/renderer/core/inspector/inspect_tools.cc
+++ b/third_party/blink/renderer/core/inspector/inspect_tools.cc
@@ -477,14 +477,14 @@ bool PersistentTool::HideOnMouseMove() {
void PersistentTool::Draw(float scale) {
for (auto& entry : grid_node_highlights_) {
std::unique_ptr<protocol::Value> highlight =
- InspectorGridHighlight(entry.first.Get(), *(entry.second));
+ InspectorGridHighlight(entry.key, *(entry.value));
if (!highlight)
continue;
overlay_->EvaluateInOverlay("drawGridHighlight", std::move(highlight));
}
for (auto& entry : flex_container_configs_) {
std::unique_ptr<protocol::Value> highlight =
- InspectorFlexContainerHighlight(entry.first.Get(), *(entry.second));
+ InspectorFlexContainerHighlight(entry.key, *(entry.value));
if (!highlight)
continue;
overlay_->EvaluateInOverlay("drawFlexContainerHighlight",
@@ -492,7 +492,7 @@ void PersistentTool::Draw(float scale) {
}
for (auto& entry : scroll_snap_configs_) {
std::unique_ptr<protocol::Value> highlight =
- InspectorScrollSnapHighlight(entry.first.Get(), *(entry.second));
+ InspectorScrollSnapHighlight(entry.key, *(entry.value));
if (!highlight)
continue;
overlay_->EvaluateInOverlay("drawScrollSnapHighlight",
@@ -500,17 +500,15 @@ void PersistentTool::Draw(float scale) {
}
for (auto& entry : container_query_configs_) {
std::unique_ptr<protocol::Value> highlight =
- InspectorContainerQueryHighlight(entry.first.Get(), *(entry.second));
+ InspectorContainerQueryHighlight(entry.key, *(entry.value));
if (!highlight)
continue;
overlay_->EvaluateInOverlay("drawContainerQueryHighlight",
std::move(highlight));
}
- for (wtf_size_t i = 0; i < isolated_element_configs_.size(); ++i) {
- auto& entry = isolated_element_configs_.at(i);
+ for (auto& entry : isolated_element_configs_) {
std::unique_ptr<protocol::Value> highlight =
- InspectorIsolatedElementHighlight(entry.first.Get(), *(entry.second),
- i);
+ InspectorIsolatedElementHighlight(entry.key, *(entry.value));
if (!highlight)
continue;
overlay_->EvaluateInOverlay("drawIsolatedElementHighlight",
@@ -548,9 +546,11 @@ void PersistentTool::Dispatch(const ScriptValue& message,
Element* element = nullptr;
if (highlight_type == "isolatedElement") {
- if (index < static_cast<int>(isolated_element_configs_.size()) &&
- index >= 0) {
- element = isolated_element_configs_.at(index).first.Get();
+ for (auto& entry : isolated_element_configs_) {
+ if (entry.value->highlight_index == index) {
+ element = entry.key;
+ break;
+ }
}
}
@@ -571,7 +571,7 @@ PersistentTool::GetGridInspectorHighlightsAsJson() const {
protocol::ListValue::create();
for (auto& entry : grid_node_highlights_) {
std::unique_ptr<protocol::Value> highlight =
- InspectorGridHighlight(entry.first.Get(), *(entry.second));
+ InspectorGridHighlight(entry.key, *(entry.value));
if (!highlight)
continue;
highlights->pushValue(std::move(highlight));
@@ -584,6 +584,15 @@ PersistentTool::GetGridInspectorHighlightsAsJson() const {
return result;
}
+void PersistentTool::Trace(Visitor* visitor) const {
+ InspectTool::Trace(visitor);
+ visitor->Trace(grid_node_highlights_);
+ visitor->Trace(flex_container_configs_);
+ visitor->Trace(scroll_snap_configs_);
+ visitor->Trace(container_query_configs_);
+ visitor->Trace(isolated_element_configs_);
+}
+
// SourceOrderTool -----------------------------------------------------------
SourceOrderTool::SourceOrderTool(
diff --git a/third_party/blink/renderer/core/inspector/inspect_tools.h b/third_party/blink/renderer/core/inspector/inspect_tools.h
index dd55eacf154da0895e04b6113d28e52085dcff6d..92abdca382bd8edd95714be6194eddc84bee7e06 100644
--- a/third_party/blink/renderer/core/inspector/inspect_tools.h
+++ b/third_party/blink/renderer/core/inspector/inspect_tools.h
@@ -145,23 +145,20 @@ class SourceOrderTool : public InspectTool {
};
// -----------------------------------------------------------------------------
-
-using GridConfigs = Vector<
- std::pair<Member<Node>, std::unique_ptr<InspectorGridHighlightConfig>>>;
+using GridConfigs = HeapHashMap<WeakMember<Node>,
+ std::unique_ptr<InspectorGridHighlightConfig>>;
using FlexContainerConfigs =
- Vector<std::pair<Member<Node>,
- std::unique_ptr<InspectorFlexContainerHighlightConfig>>>;
-using ScrollSnapConfigs = Vector<
- std::pair<Member<Node>,
- std::unique_ptr<InspectorScrollSnapContainerHighlightConfig>>>;
-
-using ContainerQueryConfigs = Vector<std::pair<
- Member<Node>,
- std::unique_ptr<InspectorContainerQueryContainerHighlightConfig>>>;
-
+ HeapHashMap<WeakMember<Node>,
+ std::unique_ptr<InspectorFlexContainerHighlightConfig>>;
+using ScrollSnapConfigs =
+ HeapHashMap<WeakMember<Node>,
+ std::unique_ptr<InspectorScrollSnapContainerHighlightConfig>>;
+using ContainerQueryConfigs = HeapHashMap<
+ WeakMember<Node>,
+ std::unique_ptr<InspectorContainerQueryContainerHighlightConfig>>;
using IsolatedElementConfigs =
- Vector<std::pair<Member<Element>,
- std::unique_ptr<InspectorIsolationModeHighlightConfig>>>;
+ HeapHashMap<WeakMember<Element>,
+ std::unique_ptr<InspectorIsolationModeHighlightConfig>>;
class PersistentTool : public InspectTool {
using InspectTool::InspectTool;
@@ -181,6 +178,8 @@ class PersistentTool : public InspectTool {
std::unique_ptr<protocol::DictionaryValue> GetGridInspectorHighlightsAsJson()
const;
+ void Trace(Visitor* visitor) const override;
+
private:
bool ForwardEventsToOverlay() override;
bool HideOnMouseMove() override;
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index 3ad3081f4f6143c1b38fa1ffc5892eb810344d93..1abb20541977adcece43f7e61db8d34f5170e6c6 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -2415,8 +2415,7 @@ std::unique_ptr<protocol::DictionaryValue> InspectorContainerQueryHighlight(
std::unique_ptr<protocol::DictionaryValue> InspectorIsolatedElementHighlight(
Element* element,
- const InspectorIsolationModeHighlightConfig& config,
- int highlight_index) {
+ const InspectorIsolationModeHighlightConfig& config) {
LocalFrameView* frame_view = element->GetDocument().View();
if (!frame_view)
return nullptr;
@@ -2428,7 +2427,7 @@ std::unique_ptr<protocol::DictionaryValue> InspectorIsolatedElementHighlight(
if (!isolated_element_info)
return nullptr;
- isolated_element_info->setInteger("highlightIndex", highlight_index);
+ isolated_element_info->setInteger("highlightIndex", config.highlight_index);
return isolated_element_info;
}
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.h b/third_party/blink/renderer/core/inspector/inspector_highlight.h
index ba4e3f9487b05140aef0068bbafc209a2047235b..4c6423063a4f3ee4ace988fd4e45b41592308c03 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.h
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.h
@@ -145,6 +145,7 @@ struct CORE_EXPORT InspectorIsolationModeHighlightConfig {
Color resizer_color;
Color resizer_handle_color;
Color mask_color;
+ int highlight_index = 0;
};
struct CORE_EXPORT InspectorHighlightConfig {
@@ -300,8 +301,7 @@ std::unique_ptr<protocol::DictionaryValue> InspectorContainerQueryHighlight(
std::unique_ptr<protocol::DictionaryValue> InspectorIsolatedElementHighlight(
Element* element,
- const InspectorIsolationModeHighlightConfig& config,
- int highlight_index);
+ const InspectorIsolationModeHighlightConfig& config);
// CORE_EXPORT is required to make these functions available for unit tests.
std::unique_ptr<protocol::DictionaryValue> CORE_EXPORT
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 6bdf410f2d7606ebc946dba903ed6094d8c7cfb4..17ba8a9fc32d6d8d4573aec0d352889dce8fe15e 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -707,7 +707,7 @@ Response InspectorOverlayAgent::setShowGridOverlays(
MakeGarbageCollected<PersistentTool>(this, GetFrontend());
}
- Vector<std::pair<Member<Node>, std::unique_ptr<InspectorGridHighlightConfig>>>
+ HeapHashMap<WeakMember<Node>, std::unique_ptr<InspectorGridHighlightConfig>>
configs;
for (std::unique_ptr<protocol::Overlay::GridNodeHighlightConfig>& config :
*grid_node_highlight_configs) {
@@ -715,9 +715,8 @@ Response InspectorOverlayAgent::setShowGridOverlays(
Response response = dom_agent_->AssertNode(config->getNodeId(), node);
if (!response.IsSuccess())
return response;
- configs.push_back(
- std::make_pair(node, InspectorOverlayAgent::ToGridHighlightConfig(
- config->getGridHighlightConfig())));
+ configs.insert(node, InspectorOverlayAgent::ToGridHighlightConfig(
+ config->getGridHighlightConfig()));
}
persistent_tool_->SetGridConfigs(std::move(configs));
@@ -735,8 +734,8 @@ Response InspectorOverlayAgent::setShowFlexOverlays(
MakeGarbageCollected<PersistentTool>(this, GetFrontend());
}
- Vector<std::pair<Member<Node>,
- std::unique_ptr<InspectorFlexContainerHighlightConfig>>>
+ HeapHashMap<WeakMember<Node>,
+ std::unique_ptr<InspectorFlexContainerHighlightConfig>>
configs;
for (std::unique_ptr<protocol::Overlay::FlexNodeHighlightConfig>& config :
@@ -745,9 +744,8 @@ Response InspectorOverlayAgent::setShowFlexOverlays(
Response response = dom_agent_->AssertNode(config->getNodeId(), node);
if (!response.IsSuccess())
return response;
- configs.push_back(std::make_pair(
- node, InspectorOverlayAgent::ToFlexContainerHighlightConfig(
- config->getFlexContainerHighlightConfig())));
+ configs.insert(node, InspectorOverlayAgent::ToFlexContainerHighlightConfig(
+ config->getFlexContainerHighlightConfig()));
}
persistent_tool_->SetFlexContainerConfigs(std::move(configs));
@@ -766,9 +764,8 @@ Response InspectorOverlayAgent::setShowScrollSnapOverlays(
MakeGarbageCollected<PersistentTool>(this, GetFrontend());
}
- Vector<
- std::pair<Member<Node>,
- std::unique_ptr<InspectorScrollSnapContainerHighlightConfig>>>
+ HeapHashMap<WeakMember<Node>,
+ std::unique_ptr<InspectorScrollSnapContainerHighlightConfig>>
configs;
for (std::unique_ptr<protocol::Overlay::ScrollSnapHighlightConfig>& config :
@@ -777,9 +774,9 @@ Response InspectorOverlayAgent::setShowScrollSnapOverlays(
Response response = dom_agent_->AssertNode(config->getNodeId(), node);
if (!response.IsSuccess())
return response;
- configs.push_back(std::make_pair(
- node, InspectorOverlayAgent::ToScrollSnapContainerHighlightConfig(
- config->getScrollSnapContainerHighlightConfig())));
+ configs.insert(node,
+ InspectorOverlayAgent::ToScrollSnapContainerHighlightConfig(
+ config->getScrollSnapContainerHighlightConfig()));
}
persistent_tool_->SetScrollSnapConfigs(std::move(configs));
@@ -798,9 +795,8 @@ Response InspectorOverlayAgent::setShowContainerQueryOverlays(
MakeGarbageCollected<PersistentTool>(this, GetFrontend());
}
- Vector<std::pair<
- Member<Node>,
- std::unique_ptr<InspectorContainerQueryContainerHighlightConfig>>>
+ HeapHashMap<WeakMember<Node>,
+ std::unique_ptr<InspectorContainerQueryContainerHighlightConfig>>
configs;
for (std::unique_ptr<protocol::Overlay::ContainerQueryHighlightConfig>&
@@ -809,9 +805,9 @@ Response InspectorOverlayAgent::setShowContainerQueryOverlays(
Response response = dom_agent_->AssertNode(config->getNodeId(), node);
if (!response.IsSuccess())
return response;
- configs.push_back(std::make_pair(
+ configs.insert(
node, InspectorOverlayAgent::ToContainerQueryContainerHighlightConfig(
- config->getContainerQueryContainerHighlightConfig())));
+ config->getContainerQueryContainerHighlightConfig()));
}
persistent_tool_->SetContainerQueryConfigs(std::move(configs));
@@ -830,10 +826,11 @@ Response InspectorOverlayAgent::setShowIsolatedElements(
MakeGarbageCollected<PersistentTool>(this, GetFrontend());
}
- Vector<std::pair<Member<Element>,
- std::unique_ptr<InspectorIsolationModeHighlightConfig>>>
+ HeapHashMap<WeakMember<Element>,
+ std::unique_ptr<InspectorIsolationModeHighlightConfig>>
configs;
+ int idx = 0;
for (std::unique_ptr<protocol::Overlay::IsolatedElementHighlightConfig>&
config : *isolated_element_highlight_configs) {
Element* element = nullptr;
@@ -841,9 +838,10 @@ Response InspectorOverlayAgent::setShowIsolatedElements(
Response response = dom_agent_->AssertElement(config->getNodeId(), element);
if (!response.IsSuccess())
return response;
- configs.push_back(std::make_pair(
- element, InspectorOverlayAgent::ToIsolationModeHighlightConfig(
- config->getIsolationModeHighlightConfig())));
+ configs.insert(element,
+ InspectorOverlayAgent::ToIsolationModeHighlightConfig(
+ config->getIsolationModeHighlightConfig(), idx));
+ idx++;
}
persistent_tool_->SetIsolatedElementConfigs(std::move(configs));
@@ -947,16 +945,16 @@ Response InspectorOverlayAgent::getGridHighlightObjectsForTest(
std::unique_ptr<protocol::Array<int>> node_ids,
std::unique_ptr<protocol::DictionaryValue>* highlights) {
PersistentTool persistent_tool(this, GetFrontend());
- Vector<std::pair<Member<Node>, std::unique_ptr<InspectorGridHighlightConfig>>>
+
+ HeapHashMap<WeakMember<Node>, std::unique_ptr<InspectorGridHighlightConfig>>
configs;
for (const int node_id : *node_ids) {
Node* node = nullptr;
Response response = dom_agent_->AssertNode(node_id, node);
if (!response.IsSuccess())
return response;
- configs.push_back(
- std::make_pair(node, std::make_unique<InspectorGridHighlightConfig>(
- InspectorHighlight::DefaultGridConfig())));
+ configs.insert(node, std::make_unique<InspectorGridHighlightConfig>(
+ InspectorHighlight::DefaultGridConfig()));
}
persistent_tool.SetGridConfigs(std::move(configs));
*highlights = persistent_tool.GetGridInspectorHighlightsAsJson();
@@ -1748,7 +1746,8 @@ InspectorOverlayAgent::ToFlexItemHighlightConfig(
// static
std::unique_ptr<InspectorIsolationModeHighlightConfig>
InspectorOverlayAgent::ToIsolationModeHighlightConfig(
- protocol::Overlay::IsolationModeHighlightConfig* config) {
+ protocol::Overlay::IsolationModeHighlightConfig* config,
+ int idx) {
if (!config) {
return nullptr;
}
@@ -1760,6 +1759,7 @@ InspectorOverlayAgent::ToIsolationModeHighlightConfig(
InspectorDOMAgent::ParseColor(config->getResizerHandleColor(nullptr));
highlight_config->mask_color =
InspectorDOMAgent::ParseColor(config->getMaskColor(nullptr));
+ highlight_config->highlight_index = idx;
return highlight_config;
}
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
index 00226ea419fbd6c8004635c5adf205e8e1bbbd4e..7cb62900eedcb7c6de9666a92b701a57ec31f52d 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
@@ -161,7 +161,8 @@ class CORE_EXPORT InspectorOverlayAgent final
ToFlexItemHighlightConfig(protocol::Overlay::FlexItemHighlightConfig*);
static std::unique_ptr<InspectorIsolationModeHighlightConfig>
ToIsolationModeHighlightConfig(
- protocol::Overlay::IsolationModeHighlightConfig*);
+ protocol::Overlay::IsolationModeHighlightConfig*,
+ int highlight_index);
static absl::optional<LineStyle> ToLineStyle(protocol::Overlay::LineStyle*);
static absl::optional<BoxStyle> ToBoxStyle(protocol::Overlay::BoxStyle*);
static std::unique_ptr<InspectorHighlightConfig> ToHighlightConfig(

View File

@@ -0,0 +1,414 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Keren Zhu <kerenzhu@chromium.org>
Date: Thu, 9 Jun 2022 14:50:26 +0000
Subject: Fix use-after-free vulnerability in ComboboxModel
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Stop the Combobox from observing the model when the model gets
destroyed. This is done by adding a virtual OnComboboxModelDestroying()
to the base observer class and calling it in ~ComboboxModel().
This fix will prevent the use-after-free that happens when the combobox outlives the model, usually because the model lifetime
is managed by non-UI component.
Bug: 1264288
Change-Id: Ia00881a9b674bbc83bbf54dd228490c1cc1290bc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3693825
Commit-Queue: Keren Zhu <kerenzhu@chromium.org>
Reviewed-by: Matthias Körber <koerber@google.com>
Auto-Submit: Keren Zhu <kerenzhu@chromium.org>
Reviewed-by: Peter Boström <pbos@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1012504}
diff --git a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
index 323f85651611ff98e45884e3b0b7211eaa25039b..0d0bb0d21b1d96841a30bfea336af918b6da83f8 100644
--- a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
+++ b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.cc
@@ -130,16 +130,6 @@ int RecentlyUsedFoldersComboModel::GetDefaultIndex() const {
return it == items_.end() ? 0 : static_cast<int>(it - items_.begin());
}
-void RecentlyUsedFoldersComboModel::AddObserver(
- ui::ComboboxModelObserver* observer) {
- observers_.AddObserver(observer);
-}
-
-void RecentlyUsedFoldersComboModel::RemoveObserver(
- ui::ComboboxModelObserver* observer) {
- observers_.RemoveObserver(observer);
-}
-
void RecentlyUsedFoldersComboModel::BookmarkModelLoaded(BookmarkModel* model,
bool ids_reassigned) {}
@@ -176,7 +166,7 @@ void RecentlyUsedFoldersComboModel::OnWillRemoveBookmarks(
}
}
if (changed) {
- for (ui::ComboboxModelObserver& observer : observers_)
+ for (ui::ComboboxModelObserver& observer : observers())
observer.OnComboboxModelChanged(this);
}
}
@@ -219,7 +209,7 @@ void RecentlyUsedFoldersComboModel::BookmarkAllUserNodesRemoved(
}
}
if (changed) {
- for (ui::ComboboxModelObserver& observer : observers_)
+ for (ui::ComboboxModelObserver& observer : observers())
observer.OnComboboxModelChanged(this);
}
}
diff --git a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h
index 8682e750f4407e615264986024e2a7dd0bae038f..0c02c685a02875c7b91ca9e6228864bc04e45c10 100644
--- a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h
+++ b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h
@@ -38,8 +38,6 @@ class RecentlyUsedFoldersComboModel : public ui::ComboboxModel,
std::u16string GetItemAt(int index) const override;
bool IsItemSeparatorAt(int index) const override;
int GetDefaultIndex() const override;
- void AddObserver(ui::ComboboxModelObserver* observer) override;
- void RemoveObserver(ui::ComboboxModelObserver* observer) override;
// Overriden from bookmarks::BookmarkModelObserver:
void BookmarkModelLoaded(bookmarks::BookmarkModel* model,
@@ -90,8 +88,6 @@ class RecentlyUsedFoldersComboModel : public ui::ComboboxModel,
const raw_ptr<bookmarks::BookmarkModel> bookmark_model_;
const raw_ptr<const bookmarks::BookmarkNode> parent_node_;
-
- base::ObserverList<ui::ComboboxModelObserver> observers_;
};
#endif // CHROME_BROWSER_UI_BOOKMARKS_RECENTLY_USED_FOLDERS_COMBO_MODEL_H_
diff --git a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model_unittest.cc b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model_unittest.cc
index 60455386b7d5f1da72c92424ccf95fd971e7590a..3bfe6852b8a54a53aafea5eeec2da850097b2f0a 100644
--- a/chrome/browser/ui/bookmarks/recently_used_folders_combo_model_unittest.cc
+++ b/chrome/browser/ui/bookmarks/recently_used_folders_combo_model_unittest.cc
@@ -41,6 +41,8 @@ class TestComboboxModelObserver : public ui::ComboboxModelObserver {
changed_ = true;
}
+ void OnComboboxModelDestroying(ui::ComboboxModel* model) override {}
+
private:
bool changed_;
};
diff --git a/components/autofill/core/browser/ui/address_combobox_model.cc b/components/autofill/core/browser/ui/address_combobox_model.cc
index 2f33fb3f5e12e034c8f716fa28d35b6aebc76751..5e425ae742404830948fbadd60b21570155a176a 100644
--- a/components/autofill/core/browser/ui/address_combobox_model.cc
+++ b/components/autofill/core/browser/ui/address_combobox_model.cc
@@ -83,14 +83,6 @@ int AddressComboboxModel::GetDefaultIndex() const {
return ui::ComboboxModel::GetDefaultIndex();
}
-void AddressComboboxModel::AddObserver(ui::ComboboxModelObserver* observer) {
- observers_.AddObserver(observer);
-}
-
-void AddressComboboxModel::RemoveObserver(ui::ComboboxModelObserver* observer) {
- observers_.RemoveObserver(observer);
-}
-
int AddressComboboxModel::AddNewProfile(const AutofillProfile& profile) {
profiles_cache_.push_back(std::make_unique<AutofillProfile>(profile));
UpdateAddresses();
@@ -130,7 +122,7 @@ void AddressComboboxModel::UpdateAddresses() {
for (size_t i = 0; i < profiles_cache_.size(); ++i)
addresses_.emplace_back(profiles_cache_[i]->guid(), labels[i]);
- for (auto& observer : observers_) {
+ for (auto& observer : observers()) {
observer.OnComboboxModelChanged(this);
}
}
diff --git a/components/autofill/core/browser/ui/address_combobox_model.h b/components/autofill/core/browser/ui/address_combobox_model.h
index 02d65209d2729deae608481411e1539ba08f5f7e..3a41b30c92543633da29e40e2c64cc4597e322d2 100644
--- a/components/autofill/core/browser/ui/address_combobox_model.h
+++ b/components/autofill/core/browser/ui/address_combobox_model.h
@@ -40,8 +40,6 @@ class AddressComboboxModel : public ui::ComboboxModel {
std::u16string GetItemAt(int index) const override;
bool IsItemSeparatorAt(int index) const override;
int GetDefaultIndex() const override;
- void AddObserver(ui::ComboboxModelObserver* observer) override;
- void RemoveObserver(ui::ComboboxModelObserver* observer) override;
// Adds |profile| to model and return its combobox index. The lifespan of
// |profile| beyond this call is undefined so a copy must be made.
@@ -72,9 +70,6 @@ class AddressComboboxModel : public ui::ComboboxModel {
// If non empty, the guid of the address that should be selected by default.
std::string default_selected_guid_;
-
- // To be called when the data for the given country code was loaded.
- base::ObserverList<ui::ComboboxModelObserver> observers_;
};
} // namespace autofill
diff --git a/components/autofill/core/browser/ui/region_combobox_model.cc b/components/autofill/core/browser/ui/region_combobox_model.cc
index e17a040aeca5163553a998d6c43e98f595c42779..b23c7b9eeb9a93c3ceda6622daa1a8c4377605e2 100644
--- a/components/autofill/core/browser/ui/region_combobox_model.cc
+++ b/components/autofill/core/browser/ui/region_combobox_model.cc
@@ -66,14 +66,6 @@ bool RegionComboboxModel::IsItemSeparatorAt(int index) const {
return regions_[index].first.empty();
}
-void RegionComboboxModel::AddObserver(ui::ComboboxModelObserver* observer) {
- observers_.AddObserver(observer);
-}
-
-void RegionComboboxModel::RemoveObserver(ui::ComboboxModelObserver* observer) {
- observers_.RemoveObserver(observer);
-}
-
void RegionComboboxModel::OnRegionDataLoaded(
const std::vector<const ::i18n::addressinput::RegionData*>& regions) {
// The RegionDataLoader will eventually self destruct after this call.
@@ -94,7 +86,7 @@ void RegionComboboxModel::OnRegionDataLoaded(
failed_to_load_data_ = true;
}
- for (auto& observer : observers_) {
+ for (auto& observer : observers()) {
observer.OnComboboxModelChanged(this);
}
}
diff --git a/components/autofill/core/browser/ui/region_combobox_model.h b/components/autofill/core/browser/ui/region_combobox_model.h
index d1b5d8b9db0c4d9d15e4f2a50a6cba38b33f1be8..52cffd5d00649f8e2be7189bca128ba4c3e8a97d 100644
--- a/components/autofill/core/browser/ui/region_combobox_model.h
+++ b/components/autofill/core/browser/ui/region_combobox_model.h
@@ -54,8 +54,6 @@ class RegionComboboxModel : public ui::ComboboxModel {
int GetItemCount() const override;
std::u16string GetItemAt(int index) const override;
bool IsItemSeparatorAt(int index) const override;
- void AddObserver(ui::ComboboxModelObserver* observer) override;
- void RemoveObserver(ui::ComboboxModelObserver* observer) override;
private:
// Callback for the RegionDataLoader.
@@ -72,9 +70,6 @@ class RegionComboboxModel : public ui::ComboboxModel {
// List of <code, name> pairs for ADDRESS_HOME_STATE combobox values;
std::vector<std::pair<std::string, std::string>> regions_;
- // To be called when the data for the given country code was loaded.
- base::ObserverList<ui::ComboboxModelObserver> observers_;
-
// Weak pointer factory.
base::WeakPtrFactory<RegionComboboxModel> weak_factory_{this};
};
diff --git a/ui/base/models/combobox_model.cc b/ui/base/models/combobox_model.cc
index d307ee0ec592ae01a1d04cef85c0a492b7f7e29e..413adebd0349e304006de0cc0c4e2a4536ebbf06 100644
--- a/ui/base/models/combobox_model.cc
+++ b/ui/base/models/combobox_model.cc
@@ -4,10 +4,18 @@
#include "ui/base/models/combobox_model.h"
+#include "ui/base/models/combobox_model_observer.h"
#include "ui/base/models/image_model.h"
namespace ui {
+ComboboxModel::ComboboxModel() = default;
+
+ComboboxModel::~ComboboxModel() {
+ for (auto& observer : observers_)
+ observer.OnComboboxModelDestroying(this);
+}
+
std::u16string ComboboxModel::GetDropDownTextAt(int index) const {
return GetItemAt(index);
}
@@ -36,4 +44,12 @@ bool ComboboxModel::IsItemEnabledAt(int index) const {
return true;
}
+void ComboboxModel::AddObserver(ComboboxModelObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void ComboboxModel::RemoveObserver(ComboboxModelObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
} // namespace ui
diff --git a/ui/base/models/combobox_model.h b/ui/base/models/combobox_model.h
index 5264aaa5550859fce39a8485feff16c8fd83d897..eeff7dc8444e77fd598eb7ce05ab18259a9d7ef5 100644
--- a/ui/base/models/combobox_model.h
+++ b/ui/base/models/combobox_model.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/component_export.h"
+#include "base/observer_list.h"
namespace ui {
@@ -17,7 +18,8 @@ class ImageModel;
// A data model for a combo box.
class COMPONENT_EXPORT(UI_BASE) ComboboxModel {
public:
- virtual ~ComboboxModel() {}
+ ComboboxModel();
+ virtual ~ComboboxModel();
// Returns the number of items in the combo box.
virtual int GetItemCount() const = 0;
@@ -52,9 +54,17 @@ class COMPONENT_EXPORT(UI_BASE) ComboboxModel {
// Returns true if the item at |index| is enabled.
virtual bool IsItemEnabledAt(int index) const;
- // Adds/removes an observer. Override if model supports mutation.
- virtual void AddObserver(ComboboxModelObserver* observer) {}
- virtual void RemoveObserver(ComboboxModelObserver* observer) {}
+ // Adds/removes an observer.
+ void AddObserver(ComboboxModelObserver* observer);
+ void RemoveObserver(ComboboxModelObserver* observer);
+
+ protected:
+ base::ObserverList<ui::ComboboxModelObserver>& observers() {
+ return observers_;
+ }
+
+ private:
+ base::ObserverList<ui::ComboboxModelObserver> observers_;
};
} // namespace ui
diff --git a/ui/base/models/combobox_model_observer.h b/ui/base/models/combobox_model_observer.h
index 5e00ab68a0c14d0b48317ca287e3f105351a102c..44325ff0aa94b75eb662be1637f44aa5524a33e8 100644
--- a/ui/base/models/combobox_model_observer.h
+++ b/ui/base/models/combobox_model_observer.h
@@ -16,10 +16,13 @@ class ComboboxModel;
class COMPONENT_EXPORT(UI_BASE) ComboboxModelObserver
: public base::CheckedObserver {
public:
- // Invoked when |model| has changed in some way. The observer should assume
+ // Invoked when `model` has changed in some way. The observer should assume
// everything changed.
virtual void OnComboboxModelChanged(ComboboxModel* model) = 0;
+ // Invoked when `model` is destroyed. The observer should stop observing.
+ virtual void OnComboboxModelDestroying(ComboboxModel* model) = 0;
+
protected:
~ComboboxModelObserver() override = default;
};
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index 1e6140cda4daa61036e408347f5a35458cb28121..b49521a46640673b43e428e6c8cd04678326d322 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -579,6 +579,10 @@ void Combobox::OnComboboxModelChanged(ui::ComboboxModel* model) {
OnContentSizeMaybeChanged();
}
+void Combobox::OnComboboxModelDestroying(ui::ComboboxModel* model) {
+ SetModel(nullptr);
+}
+
const base::RepeatingClosure& Combobox::GetCallback() const {
return callback_;
}
diff --git a/ui/views/controls/combobox/combobox.h b/ui/views/controls/combobox/combobox.h
index fc77f8e49cead5fb689fba3d56c65c1b8e69f298..1a3f533256eca91e4fd4552ee5425b7ca8e7ad7e 100644
--- a/ui/views/controls/combobox/combobox.h
+++ b/ui/views/controls/combobox/combobox.h
@@ -131,6 +131,7 @@ class VIEWS_EXPORT Combobox : public View,
protected:
// Overridden from ComboboxModelObserver:
void OnComboboxModelChanged(ui::ComboboxModel* model) override;
+ void OnComboboxModelDestroying(ui::ComboboxModel* model) override;
// Getters to be used by metadata.
const base::RepeatingClosure& GetCallback() const;
diff --git a/ui/views/controls/combobox/combobox_unittest.cc b/ui/views/controls/combobox/combobox_unittest.cc
index 7ce20acc4b3ecd6470d5f017f9171d0be35c09d2..91661f33074d3453b4f28a410ecc918b793f86c6 100644
--- a/ui/views/controls/combobox/combobox_unittest.cc
+++ b/ui/views/controls/combobox/combobox_unittest.cc
@@ -84,13 +84,6 @@ class TestComboboxModel : public ui::ComboboxModel {
return 0;
}
- void AddObserver(ui::ComboboxModelObserver* observer) override {
- observers_.AddObserver(observer);
- }
- void RemoveObserver(ui::ComboboxModelObserver* observer) override {
- observers_.RemoveObserver(observer);
- }
-
void SetSeparators(const std::set<int>& separators) {
separators_ = separators;
OnModelChanged();
@@ -103,11 +96,10 @@ class TestComboboxModel : public ui::ComboboxModel {
private:
void OnModelChanged() {
- for (auto& observer : observers_)
+ for (auto& observer : observers())
observer.OnComboboxModelChanged(this);
}
- base::ObserverList<ui::ComboboxModelObserver> observers_;
std::set<int> separators_;
int item_count_ = kItemCount;
};
@@ -134,20 +126,13 @@ class VectorComboboxModel : public ui::ComboboxModel {
}
bool IsItemSeparatorAt(int index) const override { return false; }
int GetDefaultIndex() const override { return default_index_; }
- void AddObserver(ui::ComboboxModelObserver* observer) override {
- observers_.AddObserver(observer);
- }
- void RemoveObserver(ui::ComboboxModelObserver* observer) override {
- observers_.RemoveObserver(observer);
- }
void ValuesChanged() {
- for (auto& observer : observers_)
+ for (auto& observer : observers())
observer.OnComboboxModelChanged(this);
}
private:
- base::ObserverList<ui::ComboboxModelObserver> observers_;
int default_index_ = 0;
const raw_ptr<std::vector<std::string>> values_;
};
@@ -884,6 +869,15 @@ TEST_F(ComboboxTest, SetTooltipTextNotifiesAccessibilityEvent) {
EXPECT_EQ(test_tooltip_text, ASCIIToUTF16(name));
}
+// Regression test for crbug.com/1264288.
+// Should fail in ASan build before the fix.
+TEST_F(ComboboxTest, NoCrashWhenComboboxOutlivesModel) {
+ auto model = std::make_unique<TestComboboxModel>();
+ auto combobox = std::make_unique<TestCombobox>(model.get());
+ model.reset();
+ combobox.reset();
+}
+
namespace {
using ComboboxDefaultTest = ViewsTestBase;
diff --git a/ui/views/controls/editable_combobox/editable_combobox.cc b/ui/views/controls/editable_combobox/editable_combobox.cc
index 997036a7b11bfe202abfac3c0cc9c4dc8fe0ca8c..34f3f3857c48ff36670e640ea27d64aed1643494 100644
--- a/ui/views/controls/editable_combobox/editable_combobox.cc
+++ b/ui/views/controls/editable_combobox/editable_combobox.cc
@@ -190,10 +190,15 @@ class EditableCombobox::EditableComboboxMenuModel
return combobox_model_->GetDropDownIconAt(items_shown_[index].index);
}
+ // ComboboxModelObserver:
void OnComboboxModelChanged(ui::ComboboxModel* model) override {
UpdateItemsShown();
}
+ void OnComboboxModelDestroying(ui::ComboboxModel* model) override {
+ observation_.Reset();
+ }
+
int GetItemCount() const override { return items_shown_.size(); }
private:

View File

@@ -0,0 +1,195 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Xiaocheng Hu <xiaochengh@chromium.org>
Date: Mon, 2 May 2022 23:43:40 +0000
Subject: Markup sanitization should iterate until markup is stable
There are cases where parsing a markup and then serializing it does not
round trip, which can be used to inject XSS. Current sanitization code
only does one round of parsing and serializing, which does not remove
XSS injections that hide deeper.
Hence this patch makes sanitization algorithm iterate until the markup
is stable, or declares failure if it doesn't stabilize after many tries.
(cherry picked from commit 19280353fb92d0ff7d048d7cec28d6a23befbce0)
Fixed: 1315563
Change-Id: I4a3ebe1fda6df0e04a24d863b2b48df2110af209
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3611826
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Reviewed-by: Yoshifumi Inoue <yosin@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#997032}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3621618
Auto-Submit: Xiaocheng Hu <xiaochengh@chromium.org>
Reviewed-by: Joey Arhar <jarhar@chromium.org>
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Cr-Commit-Position: refs/branch-heads/5005@{#363}
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc
index 4ee332fa16a1bda54e6ba99c8063876fb046adaa..463fd9a37671b92c9af68ff5f3c0e8ddd5f1c62c 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.cc
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
@@ -835,6 +835,12 @@ static bool StripSVGUseDataURLs(Node& node) {
return stripped;
}
+namespace {
+
+constexpr unsigned kMaxSanitizationIterations = 16;
+
+} // namespace
+
DocumentFragment* CreateSanitizedFragmentFromMarkupWithContext(
Document& document,
const String& raw_markup,
@@ -844,42 +850,56 @@ DocumentFragment* CreateSanitizedFragmentFromMarkupWithContext(
if (raw_markup.IsEmpty())
return nullptr;
- Document* staging_document = CreateStagingDocumentForMarkupSanitization(
- *document.GetFrame()->GetFrameScheduler()->GetAgentGroupScheduler());
- Element* body = staging_document->body();
-
- DocumentFragment* fragment = CreateFragmentFromMarkupWithContext(
- *staging_document, raw_markup, fragment_start, fragment_end, KURL(),
- kDisallowScriptingAndPluginContent);
- if (!fragment) {
- staging_document->GetPage()->WillBeDestroyed();
- return nullptr;
- }
+ // Iterate on parsing, sanitization and serialization until the markup is
+ // stable, or if we have exceeded the maximum allowed number of iterations.
+ String last_markup;
+ String markup = raw_markup;
+ for (unsigned iteration = 0;
+ iteration < kMaxSanitizationIterations && last_markup != markup;
+ ++iteration) {
+ last_markup = markup;
+
+ Document* staging_document = CreateStagingDocumentForMarkupSanitization(
+ *document.GetFrame()->GetFrameScheduler()->GetAgentGroupScheduler());
+ Element* body = staging_document->body();
+
+ DocumentFragment* fragment = CreateFragmentFromMarkupWithContext(
+ *staging_document, last_markup, fragment_start, fragment_end, KURL(),
+ kDisallowScriptingAndPluginContent);
+ if (!fragment) {
+ staging_document->GetPage()->WillBeDestroyed();
+ return nullptr;
+ }
- bool needs_sanitization = false;
- if (ContainsStyleElements(*fragment))
- needs_sanitization = true;
- if (StripSVGUseDataURLs(*fragment))
- needs_sanitization = true;
+ bool needs_sanitization = false;
+ if (ContainsStyleElements(*fragment))
+ needs_sanitization = true;
+ if (StripSVGUseDataURLs(*fragment))
+ needs_sanitization = true;
- if (!needs_sanitization) {
+ if (!needs_sanitization) {
+ markup = CreateMarkup(fragment);
+ } else {
+ body->appendChild(fragment);
+ staging_document->UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
+
+ // This sanitizes stylesheets in the markup into element inline styles
+ markup = CreateMarkup(Position::FirstPositionInNode(*body),
+ Position::LastPositionInNode(*body),
+ CreateMarkupOptions::Builder()
+ .SetShouldAnnotateForInterchange(true)
+ .SetIsForMarkupSanitization(true)
+ .Build());
+ }
staging_document->GetPage()->WillBeDestroyed();
- return CreateFragmentFromMarkupWithContext(
- document, raw_markup, fragment_start, fragment_end, base_url,
- kDisallowScriptingAndPluginContent);
+
+ fragment_start = 0;
+ fragment_end = markup.length();
}
- body->appendChild(fragment);
- staging_document->UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
-
- // This sanitizes stylesheets in the markup into element inline styles
- String markup = CreateMarkup(Position::FirstPositionInNode(*body),
- Position::LastPositionInNode(*body),
- CreateMarkupOptions::Builder()
- .SetShouldAnnotateForInterchange(true)
- .SetIsForMarkupSanitization(true)
- .Build());
- staging_document->GetPage()->WillBeDestroyed();
+ // Sanitization failed because markup can't stabilize.
+ if (last_markup != markup)
+ return nullptr;
return CreateFragmentFromMarkup(document, markup, base_url,
kDisallowScriptingAndPluginContent);
diff --git a/third_party/blink/web_tests/editing/pasteboard/paste-svg-use.html b/third_party/blink/web_tests/editing/pasteboard/paste-svg-use.html
index c03ca90d492a5e2af883f68811baa7660ef1cdc2..1af77f8faa4501f577e28297f013e170775bc86a 100644
--- a/third_party/blink/web_tests/editing/pasteboard/paste-svg-use.html
+++ b/third_party/blink/web_tests/editing/pasteboard/paste-svg-use.html
@@ -32,6 +32,6 @@ selection_test(
<use href=data:application/xml;base64,PHN2ZyBpZD0neCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJz4KPGEgaHJlZj0namF2YXNjcmlwdDphbGVydCgxMjMpJz4KICAgIDxyZWN0IHdpZHRoPScxMDAlJyBoZWlnaHQ9JzEwMCUnIGZpbGw9J2xpZ2h0Ymx1ZScgLz4KICAgICA8dGV4dCB4PScwJyB5PScwJyBmaWxsPSdibGFjayc+CiAgICAgICA8dHNwYW4geD0nMCcgZHk9JzEuMmVtJz5Pb3BzLCB0aGVyZSdzIHNvbWV0aGluZyB3cm9uZyB3aXRoIHRoZSBwYWdlITwvdHNwYW4+CiAgICAgPHRzcGFuIHg9JzAnIGR5PScxLjJlbSc+UGxlYXNlIGNsaWNrIGhlcmUgdG8gcmVsb2FkLjwvdHNwYW4+Cjwvc3ZnPg==#x>"></noscript>asdasd`);
selection.document.execCommand('paste');
},
- '<div contenteditable>|<noscript>&lt;u title="</noscript><div contenteditable="false"><svg></svg></div></div>',
+ '<div contenteditable><div><br></div><div>      <u title="</div><div>      </div><div>      ">asdasd|</div></div>',
'Paste blocks data URI in SVG use element injection via <noscript>');
</script>
diff --git a/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html
new file mode 100644
index 0000000000000000000000000000000000000000..9e0ab2ee740f85ea4e9de9d3f1d2ac43bee5a985
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard.read() should sanitize text/html</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#dom-clipboard-read">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1315563">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<p><button id="button">Put payload in the clipboard</button></p>
+<div id="output"></div>
+
+<script>
+let testFailed = false;
+function fail() {
+ testFailed = true;
+}
+
+button.onclick = () => document.execCommand('copy');
+document.oncopy = ev => {
+ ev.preventDefault();
+ ev.clipboardData.setData(
+ 'text/html',
+ `<form><math><mtext></form><form><mglyph><xmp></math><img src=invalid onerror=fail()></xmp>`);
+};
+
+promise_test(async test => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.click(button);
+
+ const items = await navigator.clipboard.read();
+ const htmlBlob = await items[0].getType("text/html");
+ const html = await htmlBlob.text();
+
+ // This inserts an image with `onerror` handler if `html` is not properly sanitized
+ output.innerHTML = html;
+
+ // Allow the 'error' event to be dispatched asynchronously
+ await new Promise(resolve => test.step_timeout(resolve, 100));
+
+ assert_false(testFailed);
+});
+</script>

View File

@@ -0,0 +1,25 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Rayan Kanso <rayankans@google.com>
Date: Tue, 7 Jun 2022 13:13:36 +0000
Subject: Don't expose URL chain in case of CO redirect
Bug: 1278255
Change-Id: If853327b853e29792e5c8d1dfaeecf21d6fec004
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3693143
Reviewed-by: Susanne Westphal <swestphal@google.com>
Commit-Queue: Rayan Kanso <rayankans@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1011409}
diff --git a/content/browser/background_fetch/storage/mark_request_complete_task.cc b/content/browser/background_fetch/storage/mark_request_complete_task.cc
index 9036ec9a92212fb33b526f64d5fc02afcde38726..76cc94dd0ea8fe515d22c41a3d8b8017f1bdfd6b 100644
--- a/content/browser/background_fetch/storage/mark_request_complete_task.cc
+++ b/content/browser/background_fetch/storage/mark_request_complete_task.cc
@@ -103,6 +103,8 @@ void MarkRequestCompleteTask::StoreResponse(base::OnceClosure done_closure) {
BackgroundFetchCrossOriginFilter filter(
registration_id_.storage_key().origin(), *request_info_);
if (!filter.CanPopulateBody()) {
+ // Don't expose the initial URL in case of cross-origin redirects.
+ response_->url_list.resize(1);
failure_reason_ = proto::BackgroundFetchRegistration::FETCH_ERROR;
// No point writing the response to the cache since it won't be exposed.
CreateAndStoreCompletedRequest(std::move(done_closure));

View File

@@ -0,0 +1,65 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shrek Shao <shrekshao@google.com>
Date: Thu, 14 Jul 2022 22:27:14 +0000
Subject: Fix dawn write handle data update OOB check
(cherry picked from commit 0ba6ae3d447de7bc599a191f6792a4e6676f10a3)
Bug: chromium:1340654
Change-Id: I9d87cb868eccc380f707ab6c3c6bdc26c386fbfc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3738662
Commit-Queue: Shrek Shao <shrekshao@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1021911}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3758974
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
Auto-Submit: Shrek Shao <shrekshao@google.com>
Cr-Commit-Position: refs/branch-heads/5005@{#1246}
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
diff --git a/gpu/command_buffer/service/dawn_service_memory_transfer_service.cc b/gpu/command_buffer/service/dawn_service_memory_transfer_service.cc
index 40a990ce6dc4be540d1d1309485a4b7779c93a3b..579cd3cbdfcd5990db02960413bcac86e41c69b2 100644
--- a/gpu/command_buffer/service/dawn_service_memory_transfer_service.cc
+++ b/gpu/command_buffer/service/dawn_service_memory_transfer_service.cc
@@ -30,7 +30,8 @@ class ReadHandleImpl
size_t offset,
size_t size,
void* serializePointer) override {
- DCHECK_LE(size + offset, size_);
+ DCHECK_LE(offset, size_);
+ DCHECK_LE(size, size_ - offset);
// Copy the data into the shared memory allocation.
// In the case of buffer mapping, this is the mapped GPU memory which we
// copy into client-visible shared memory.
@@ -57,10 +58,16 @@ class WriteHandleImpl
size_t size) override {
// Nothing is serialized because we're using shared memory.
DCHECK_EQ(deserialize_size, 0u);
- DCHECK_LE(size + offset, size_);
DCHECK(mTargetData);
DCHECK(ptr_);
+ if (offset > mDataLength || size > mDataLength - offset) {
+ return false;
+ }
+ if (offset > size_ || size > size_ - offset) {
+ return false;
+ }
+
// Copy from shared memory into the target buffer.
// mTargetData will always be the starting address
// of the backing buffer after the dawn side change.
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 126c04fce185749e3c20be6db160141a987f46ce..387e034a42d653acdb0bb0b768cc3c46a5114ee5 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -1604,7 +1604,6 @@ error::Error WebGPUDecoderImpl::HandleDawnCommands(
"WebGPUDecoderImpl::HandleDawnCommands", "bytes", size);
if (!wire_server_->HandleCommands(shm_commands, size)) {
- NOTREACHED();
return error::kLostContext;
}

View File

@@ -0,0 +1,139 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ted Meyer <tmathmeyer@chromium.org>
Date: Wed, 8 Jun 2022 04:33:20 +0000
Subject: Add Stop method to BatchingMediaLog
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Now that ~MediaLog is posted for a later destruction due to garbage
collector ownership of CodecLogger, it's possible for the
SendQueuedMediaEvents call from ~BatchingMediaLog to reference
InspectorMediaEventHandler::inspector_context_ after it has been freed.
This fix forces BatchingMediaLog to shut down it's logging capabilities
when the destruction call is caused by the garbage collector deletion
phase
R=liberato
(cherry picked from commit 1bbfaf23cd8a1e977cb445a82a4caae107632a59)
Bug: 1333333
Change-Id: I0bdca72a71177c4c5a6a9dc692aad3de4c25f4e2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3689639
Commit-Queue: Ted (Chromium) Meyer <tmathmeyer@chromium.org>
Reviewed-by: Eugene Zemtsov <eugene@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1011247}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3694435
Auto-Submit: Ted (Chromium) Meyer <tmathmeyer@chromium.org>
Reviewed-by: Eugene Zemtsov <ezemtsov@google.com>
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
Cr-Commit-Position: refs/branch-heads/5060@{#672}
Cr-Branched-From: b83393d0f4038aeaf67f970a024d8101df7348d1-refs/heads/main@{#1002911}
diff --git a/content/renderer/media/batching_media_log.cc b/content/renderer/media/batching_media_log.cc
index c7263471a3b352fe329c26dc258ccf7af2049820..fbd8f5c7720d3636870d63bfcefbdb55d862fef7 100644
--- a/content/renderer/media/batching_media_log.cc
+++ b/content/renderer/media/batching_media_log.cc
@@ -75,6 +75,11 @@ BatchingMediaLog::~BatchingMediaLog() {
SendQueuedMediaEvents();
}
+void BatchingMediaLog::Stop() {
+ base::AutoLock lock(lock_);
+ event_handlers_.clear();
+}
+
void BatchingMediaLog::OnWebMediaPlayerDestroyedLocked() {
base::AutoLock lock(lock_);
for (const auto& handler : event_handlers_)
diff --git a/content/renderer/media/batching_media_log.h b/content/renderer/media/batching_media_log.h
index eb757a4ca922bd5456d0e436d10cc540ee9134ae..6c2df688caee4d2fc17e973cdad12f20342e56fe 100644
--- a/content/renderer/media/batching_media_log.h
+++ b/content/renderer/media/batching_media_log.h
@@ -45,6 +45,8 @@ class CONTENT_EXPORT BatchingMediaLog : public media::MediaLog {
~BatchingMediaLog() override;
+ void Stop() override;
+
// Will reset |last_ipc_send_time_| with the value of NowTicks().
void SetTickClockForTesting(const base::TickClock* tick_clock);
diff --git a/media/base/media_log.cc b/media/base/media_log.cc
index 3728433c5eb473cc9c82b6cf33b22bbf89471eea..46887da3dc21c504b26d6ff174eb14d68298c6b1 100644
--- a/media/base/media_log.cc
+++ b/media/base/media_log.cc
@@ -49,6 +49,9 @@ std::string MediaLog::GetErrorMessageLocked() {
return "";
}
+// Default implementation.
+void MediaLog::Stop() {}
+
void MediaLog::AddMessage(MediaLogMessageLevel level, std::string message) {
std::unique_ptr<MediaLogRecord> record(
CreateRecord(MediaLogRecord::Type::kMessage));
diff --git a/media/base/media_log.h b/media/base/media_log.h
index e70a95b72f75f9bbfb114a94396c048bce2a3647..cce6a266327b43837d7a6ccaca7f2969c06fef4b 100644
--- a/media/base/media_log.h
+++ b/media/base/media_log.h
@@ -131,6 +131,10 @@ class MEDIA_EXPORT MediaLog {
// even if this occurs, in the "won't crash" sense.
virtual std::unique_ptr<MediaLog> Clone();
+ // Can be used for stopping a MediaLog during a garbage-collected destruction
+ // sequence.
+ virtual void Stop();
+
protected:
// Ensures only subclasses and factories (e.g. Clone()) can create MediaLog.
MediaLog();
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_logger.h b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
index 2d389c1835365e423d6289fb883cb6cccd4d8e84..fae0d8a4fb41b61316c5a61a1ba3d777562d206d 100644
--- a/third_party/blink/renderer/modules/webcodecs/codec_logger.h
+++ b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
@@ -7,6 +7,10 @@
#include <memory>
+#include "base/check.h"
+#include "base/location.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequence_checker.h"
#include "media/base/media_log.h"
#include "media/base/media_util.h"
#include "media/base/status.h"
@@ -69,9 +73,20 @@ class MODULES_EXPORT CodecLogger final {
// This allows us to destroy |parent_media_log_| and stop logging,
// without causing problems to |media_log_| users.
media_log_ = parent_media_log_->Clone();
+
+ task_runner_ = task_runner;
}
- ~CodecLogger() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
+ ~CodecLogger() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ // media logs must be posted for destruction, since they can cause the
+ // garbage collector to trigger an immediate cleanup and delete the owning
+ // instance of |CodecLogger|.
+ if (parent_media_log_) {
+ parent_media_log_->Stop();
+ task_runner_->DeleteSoon(FROM_HERE, std::move(parent_media_log_));
+ }
+ }
void SendPlayerNameInformation(const ExecutionContext& context,
std::string loadedAs) {
@@ -132,6 +147,9 @@ class MODULES_EXPORT CodecLogger final {
// can be safely accessed, and whose raw pointer can be given callbacks.
std::unique_ptr<media::MediaLog> media_log_;
+ // Keep task runner around for posting the media log to upon destruction.
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
SEQUENCE_CHECKER(sequence_checker_);
};

View File

@@ -0,0 +1,68 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Justin Novosad <junov@chromium.org>
Date: Thu, 2 Jun 2022 19:35:57 +0000
Subject: PaintOpReader: Harden PaintImage deserialization
This fix prevents the deserialization of PaintImage pixel data from
reading data out of bounds when the block of serialized pixel data isn't
large enough to cover the expected amount of data, given the size and
format of the image.
(cherry picked from commit e89ea1489429a9a9e49e70d5d4e8d018fbafb6ac)
Bug: 1325298
Change-Id: Icbeb405d2031d7d8ce4537836d7996ce7885f6d1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3669596
Commit-Queue: Justin Novosad <junov@chromium.org>
Reviewed-by: Jonathan Ross <jonross@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1007804}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3687975
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: Justin Novosad <junov@chromium.org>
Auto-Submit: Srinivas Sista <srinivassista@chromium.org>
Commit-Queue: Srinivas Sista <srinivassista@chromium.org>
Cr-Commit-Position: refs/branch-heads/5005@{#1093}
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
diff --git a/cc/paint/paint_op_reader.cc b/cc/paint/paint_op_reader.cc
index c56e2ea7a641d679feb348b808519dd17b861081..ecc736b61defabd7cea081b3017bbeda9aeaf0e9 100644
--- a/cc/paint/paint_op_reader.cc
+++ b/cc/paint/paint_op_reader.cc
@@ -329,6 +329,10 @@ void PaintOpReader::Read(PaintImage* image) {
SkImageInfo image_info =
SkImageInfo::Make(width, height, color_type, kPremul_SkAlphaType);
+ if (pixel_size < image_info.computeMinByteSize()) {
+ SetInvalid(DeserializationError::kInsufficientPixelData);
+ return;
+ }
const volatile void* pixel_data = ExtractReadableMemory(pixel_size);
if (!valid_)
return;
diff --git a/cc/paint/paint_op_reader.h b/cc/paint/paint_op_reader.h
index 201cdfde5eea3a07e31a3d6a50a5119485d5c1fc..af784145a9365ea2f776a1020145c0b83c63f16d 100644
--- a/cc/paint/paint_op_reader.h
+++ b/cc/paint/paint_op_reader.h
@@ -180,8 +180,9 @@ class CC_PAINT_EXPORT PaintOpReader {
kSharedImageProviderNoAccess = 50,
kSharedImageProviderSkImageCreationFailed = 51,
kZeroSkColorFilterBytes = 52,
+ kInsufficientPixelData = 53,
- kMaxValue = kZeroSkColorFilterBytes,
+ kMaxValue = kInsufficientPixelData
};
template <typename T>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 0a822d8a7d59debfb250b98f2a2079f256715b52..34569a0fcd539bca6b465e0555c8d44677f57447 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -68766,6 +68766,7 @@ Called by update_net_trust_anchors.py.-->
<int value="50" label="SharedImageProvider no access"/>
<int value="51" label="SharedImageProvider SkImage creation failed"/>
<int value="52" label="Zero SkColorFilter bytes"/>
+ <int value="53" label="Insufficient Pixel Data"/>
</enum>
<enum name="PaletteModeCancelType">

View File

@@ -0,0 +1,230 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Austin Sullivan <asully@chromium.org>
Date: Mon, 16 May 2022 18:20:27 +0000
Subject: M102: FSA: Sanitize .scf files
.scf files can be used to execute code without opening the file.
Sanitize these files the same way we sanitize .lnk files.
Also updates filename sanitization logic to be consistent in blocking
.lnk and .local extensions on all OSes.
(cherry picked from commit 988164c6c4a563c3d4c0dedba295d09472dfc15f)
Bug: 1227995
Change-Id: I4b018f1ba524c783547e18630db9addc9fb126e6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3089422
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1002147}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3648322
Auto-Submit: Austin Sullivan <asully@chromium.org>
Commit-Queue: Austin Sullivan <asully@chromium.org>
Cr-Commit-Position: refs/branch-heads/5005@{#759}
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
diff --git a/content/browser/file_system_access/file_system_access_directory_handle_impl.cc b/content/browser/file_system_access/file_system_access_directory_handle_impl.cc
index 56863d50d543470de597e8df62e2f5b43b7b0fbe..df922b5a6cb917457570bb0f11a48d7a1710c627 100644
--- a/content/browser/file_system_access/file_system_access_directory_handle_impl.cc
+++ b/content/browser/file_system_access/file_system_access_directory_handle_impl.cc
@@ -438,10 +438,13 @@ namespace {
bool IsShellIntegratedExtension(const base::FilePath::StringType& extension) {
base::FilePath::StringType extension_lower = base::ToLowerASCII(extension);
- // .lnk files may be used to execute arbitrary code (see
- // https://nvd.nist.gov/vuln/detail/CVE-2010-2568).
- if (extension_lower == FILE_PATH_LITERAL("lnk"))
+ // .lnk and .scf files may be used to execute arbitrary code (see
+ // https://nvd.nist.gov/vuln/detail/CVE-2010-2568 and
+ // https://crbug.com/1227995, respectively).
+ if (extension_lower == FILE_PATH_LITERAL("lnk") ||
+ extension_lower == FILE_PATH_LITERAL("scf")) {
return true;
+ }
// Setting a file's extension to a CLSID may conceal its actual file type on
// some Windows versions (see https://nvd.nist.gov/vuln/detail/CVE-2004-0420).
diff --git a/content/browser/file_system_access/file_system_access_manager_impl.cc b/content/browser/file_system_access/file_system_access_manager_impl.cc
index b265ba0f405cecc03cbcc1c0b217204d3e7b25f6..689f7fc7093b5483fa36af5fd7832a25adf9c23b 100644
--- a/content/browser/file_system_access/file_system_access_manager_impl.cc
+++ b/content/browser/file_system_access/file_system_access_manager_impl.cc
@@ -530,6 +530,16 @@ void FileSystemAccessManagerImpl::SetDefaultPathAndShowPicker(
suggested_name_path =
net::GenerateFileName(GURL(), std::string(), std::string(),
suggested_name, std::string(), std::string());
+
+ auto suggested_extension = suggested_name_path.Extension();
+ // Our version of `IsShellIntegratedExtension()` is more stringent than
+ // the version used in `net::GenerateFileName()`. See
+ // `FileSystemChooser::IsShellIntegratedExtension()` for details.
+ if (FileSystemChooser::IsShellIntegratedExtension(suggested_extension)) {
+ suggested_extension = FILE_PATH_LITERAL("download");
+ suggested_name_path =
+ suggested_name_path.ReplaceExtension(suggested_extension);
+ }
}
FileSystemChooser::Options file_system_chooser_options(
diff --git a/content/browser/file_system_access/file_system_chooser.cc b/content/browser/file_system_access/file_system_chooser.cc
index 6f0acccd3458ed758dbe14c8a3008a52569d8055..86b9af148a86e64f9f4aedb6e39998bf83668745 100644
--- a/content/browser/file_system_access/file_system_chooser.cc
+++ b/content/browser/file_system_access/file_system_chooser.cc
@@ -71,33 +71,6 @@ base::FilePath::StringType GetLastExtension(
: extension;
}
-// Returns whether the specified extension receives special handling by the
-// Windows shell.
-bool IsShellIntegratedExtension(const base::FilePath::StringType& extension) {
- // TODO(https://crbug.com/1154757): Figure out some way to unify this with
- // net::IsSafePortablePathComponent, with the result probably ending up in
- // base/i18n/file_util_icu.h.
- base::FilePath::StringType extension_lower = base::ToLowerASCII(extension);
-
- // .lnk files may be used to execute arbitrary code (see
- // https://nvd.nist.gov/vuln/detail/CVE-2010-2568). .local files are used by
- // Windows to determine which DLLs to load for an application.
- if ((extension_lower == FILE_PATH_LITERAL("local")) ||
- (extension_lower == FILE_PATH_LITERAL("lnk"))) {
- return true;
- }
-
- // Setting a file's extension to a CLSID may conceal its actual file type on
- // some Windows versions (see https://nvd.nist.gov/vuln/detail/CVE-2004-0420).
- if (!extension_lower.empty() &&
- (extension_lower.front() == FILE_PATH_LITERAL('{')) &&
- (extension_lower.back() == FILE_PATH_LITERAL('}'))) {
- return true;
- }
-
- return false;
-}
-
// Extension validation primarily takes place in the renderer. This checks for a
// subset of invalid extensions in the event the renderer is compromised.
bool IsInvalidExtension(base::FilePath::StringType& extension) {
@@ -105,7 +78,7 @@ bool IsInvalidExtension(base::FilePath::StringType& extension) {
auto extension16 = base::UTF8ToUTF16(component8);
return !base::i18n::IsFilenameLegal(extension16) ||
- IsShellIntegratedExtension(GetLastExtension(extension));
+ FileSystemChooser::IsShellIntegratedExtension(extension);
}
// Converts the accepted mime types and extensions from `option` into a list
@@ -290,6 +263,40 @@ void FileSystemChooser::CreateAndShow(
/*params=*/nullptr);
}
+// static
+bool FileSystemChooser::IsShellIntegratedExtension(
+ const base::FilePath::StringType& extension) {
+ // TODO(https://crbug.com/1154757): Figure out some way to unify this with
+ // net::IsSafePortablePathComponent, with the result probably ending up in
+ // base/i18n/file_util_icu.h.
+ // - For the sake of consistency across platforms, we sanitize '.lnk' and
+ // '.local' files on all platforms (not just Windows)
+ // - There are some extensions (i.e. '.scf') we would like to sanitize which
+ // `net::GenerateFileName()` does not
+ base::FilePath::StringType extension_lower =
+ base::ToLowerASCII(GetLastExtension(extension));
+
+ // .lnk and .scf files may be used to execute arbitrary code (see
+ // https://nvd.nist.gov/vuln/detail/CVE-2010-2568 and
+ // https://crbug.com/1227995, respectively). .local files are used by Windows
+ // to determine which DLLs to load for an application.
+ if ((extension_lower == FILE_PATH_LITERAL("lnk")) ||
+ (extension_lower == FILE_PATH_LITERAL("local")) ||
+ (extension_lower == FILE_PATH_LITERAL("scf"))) {
+ return true;
+ }
+
+ // Setting a file's extension to a CLSID may conceal its actual file type on
+ // some Windows versions (see https://nvd.nist.gov/vuln/detail/CVE-2004-0420).
+ if (!extension_lower.empty() &&
+ (extension_lower.front() == FILE_PATH_LITERAL('{')) &&
+ (extension_lower.back() == FILE_PATH_LITERAL('}'))) {
+ return true;
+ }
+
+ return false;
+}
+
FileSystemChooser::FileSystemChooser(ui::SelectFileDialog::Type type,
ResultCallback callback,
base::ScopedClosureRunner fullscreen_block)
diff --git a/content/browser/file_system_access/file_system_chooser.h b/content/browser/file_system_access/file_system_chooser.h
index 07c8e7fda7d96496f58ed9a9e6cba6558c8c37df..925df7f5ef1d1bb94926afbe29beda248ae5aabc 100644
--- a/content/browser/file_system_access/file_system_chooser.h
+++ b/content/browser/file_system_access/file_system_chooser.h
@@ -68,6 +68,12 @@ class CONTENT_EXPORT FileSystemChooser : public ui::SelectFileDialog::Listener {
ResultCallback callback,
base::ScopedClosureRunner fullscreen_block);
+ // Returns whether the specified extension receives special handling by the
+ // Windows shell. These extensions should be sanitized before being shown in
+ // the "save as" file picker.
+ static bool IsShellIntegratedExtension(
+ const base::FilePath::StringType& extension);
+
FileSystemChooser(ui::SelectFileDialog::Type type,
ResultCallback callback,
base::ScopedClosureRunner fullscreen_block);
diff --git a/content/browser/file_system_access/file_system_chooser_browsertest.cc b/content/browser/file_system_access/file_system_chooser_browsertest.cc
index dc310d04fb29ed4fa9c9cdac7ab726d4ca9f9f37..9ea4db7807f6bbac799452fd138848b2a650d6fd 100644
--- a/content/browser/file_system_access/file_system_chooser_browsertest.cc
+++ b/content/browser/file_system_access/file_system_chooser_browsertest.cc
@@ -1556,22 +1556,28 @@ IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SuggestedName) {
name_infos.push_back({"not_matching.jpg", ListValueOf(".txt"), false,
"not_matching.jpg", false});
-#if BUILDFLAG(IS_WIN)
- // ".local" and ".lnk" extensions should be sanitized on Windows.
+ // ".lnk", ".local", and ".scf" extensions should be sanitized.
name_infos.push_back({"dangerous_extension.local", ListValueOf(".local"),
true, "dangerous_extension.download", false});
name_infos.push_back({"dangerous_extension.lnk", ListValueOf(".lnk"), true,
"dangerous_extension.download", false});
-#else
- // ".local" and ".lnk" extensions should be allowed on other OSes.
- // TODO(https://crbug.com/1154757): `expected_exclude_accept_all_option` is
- // false here because ".local" and ".lnk" extensions are not allowed in
- // `accepts`, but are only sanitized by net::GenerateSafeFileName on Windows.
- name_infos.push_back({"dangerous_extension.local", ListValueOf(".local"),
- true, "dangerous_extension.local", false});
- name_infos.push_back({"dangerous_extension.lnk", ListValueOf(".lnk"), true,
- "dangerous_extension.lnk", false});
-#endif
+ name_infos.push_back({"dangerous_extension.scf", ListValueOf(".scf"), true,
+ "dangerous_extension.download", false});
+ // Compound extensions ending in a dangerous extension should be sanitized.
+ name_infos.push_back({"dangerous_extension.png.local", ListValueOf(".local"),
+ true, "dangerous_extension.png.download", false});
+ name_infos.push_back({"dangerous_extension.png.lnk", ListValueOf(".lnk"),
+ true, "dangerous_extension.png.download", false});
+ name_infos.push_back({"dangerous_extension.png.scf", ListValueOf(".scf"),
+ true, "dangerous_extension.png.download", false});
+ // Compound extensions not ending in a dangerous extension should not be
+ // sanitized.
+ name_infos.push_back({"dangerous_extension.local.png", ListValueOf(".png"),
+ true, "dangerous_extension.local.png", true});
+ name_infos.push_back({"dangerous_extension.lnk.png", ListValueOf(".png"),
+ true, "dangerous_extension.lnk.png", true});
+ name_infos.push_back({"dangerous_extension.scf.png", ListValueOf(".png"),
+ true, "dangerous_extension.scf.png", true});
// Invalid characters should be sanitized.
name_infos.push_back({R"(inv*l:d\\ch%rבאמת!a<ters🤓.txt)",
ListValueOf(".txt"), true,
diff --git a/content/browser/file_system_access/file_system_chooser_unittest.cc b/content/browser/file_system_access/file_system_chooser_unittest.cc
index 373de41cf5ddead27b5e036c1cc14448a731250a..9b27d6305bd00a19d94b5ec49f21a7ecff7ddc48 100644
--- a/content/browser/file_system_access/file_system_chooser_unittest.cc
+++ b/content/browser/file_system_access/file_system_chooser_unittest.cc
@@ -189,7 +189,7 @@ TEST_F(FileSystemChooserTest, IgnoreShellIntegratedExtensions) {
accepts.emplace_back(blink::mojom::ChooseFileSystemEntryAcceptsOption::New(
u"", std::vector<std::string>({}),
std::vector<std::string>(
- {"lnk", "foo.lnk", "foo.bar.local", "text", "local"})));
+ {"lnk", "foo.lnk", "foo.bar.local", "text", "local", "scf"})));
SyncShowDialog(std::move(accepts), /*include_accepts_all=*/false);
ASSERT_TRUE(dialog_params.file_types);

View File

@@ -0,0 +1,123 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Alvin Ji <alvinji@chromium.org>
Date: Thu, 28 Apr 2022 00:44:28 +0000
Subject: Introduce isLowEnergyDevice() for safely downward static_cast from
BluetoothDeviceMac to BluetoothLowEnergyDeviceMac
device/bluetooth/bluetooth_adapter_mac.mm has two non safely downward static_cast BluetoothDevice to BluetoothLowEnergyDeviceMac
To avoid it, we introduce isLowEnergyDevice() to BluetoothDeviceMac so we could identify LE bluetooth device from classic bluetooth device then safely cast it downward.
(cherry picked from commit 2582158fc555edee390a050a64c1b89994a6b349)
Bug: 1318610
Change-Id: Iaf082fc2c40270237bbc0b000b80faa7f94b1026
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3601727
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Alvin Ji <alvinji@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#995419}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3606585
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/5005@{#217}
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
index 11d8419791f3e45d5242081422d452d4fc703833..012f9ce97d9ed6b00deb718a88f432e053cb3bd1 100644
--- a/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -774,6 +774,11 @@ CBCentralManagerState GetCBManagerState(CBCentralManager* manager) {
DVLOG(1)
<< "Central no longer powered on. Notifying of device disconnection.";
for (BluetoothDevice* device : GetDevices()) {
+ // GetDevices() returns instances of BluetoothClassicDeviceMac and
+ // BluetoothLowEnergyDeviceMac. The DidDisconnectPeripheral() method is
+ // only available on BluetoothLowEnergyDeviceMac.
+ if (!static_cast<BluetoothDeviceMac*>(device)->IsLowEnergyDevice())
+ continue;
BluetoothLowEnergyDeviceMac* device_mac =
static_cast<BluetoothLowEnergyDeviceMac*>(device);
if (device_mac->IsGattConnected()) {
@@ -911,9 +916,16 @@ CBCentralManagerState GetCBManagerState(CBCentralManager* manager) {
BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
auto iter = devices_.find(device_address);
if (iter == devices_.end()) {
- return nil;
- }
- return static_cast<BluetoothLowEnergyDeviceMac*>(iter->second.get());
+ return nullptr;
+ }
+ // device_mac can be BluetoothClassicDeviceMac* or
+ // BluetoothLowEnergyDeviceMac* To return valid BluetoothLowEnergyDeviceMac*
+ // we need to first check with IsLowEnergyDevice()
+ BluetoothDeviceMac* device_mac =
+ static_cast<BluetoothDeviceMac*>(iter->second.get());
+ return device_mac->IsLowEnergyDevice()
+ ? static_cast<BluetoothLowEnergyDeviceMac*>(device_mac)
+ : nullptr;
}
bool BluetoothAdapterMac::DoesCollideWithKnownDevice(
diff --git a/device/bluetooth/bluetooth_classic_device_mac.h b/device/bluetooth/bluetooth_classic_device_mac.h
index b11dbbdec75ca276c6e9df50a00e4e3a97d5be8c..a7deb96690e2b64ed82ede5ade5591935ec0f308 100644
--- a/device/bluetooth/bluetooth_classic_device_mac.h
+++ b/device/bluetooth/bluetooth_classic_device_mac.h
@@ -82,6 +82,7 @@ class BluetoothClassicDeviceMac : public BluetoothDeviceMac {
// Returns the Bluetooth address for the |device|. The returned address has a
// normalized format (see below).
static std::string GetDeviceAddress(IOBluetoothDevice* device);
+ bool IsLowEnergyDevice() override;
protected:
// BluetoothDevice override
diff --git a/device/bluetooth/bluetooth_classic_device_mac.mm b/device/bluetooth/bluetooth_classic_device_mac.mm
index 210802eec420807c36f5cd95408bb00d9052ea1b..ceeeab4f102ceb1078e9a2b463170a04773404cf 100644
--- a/device/bluetooth/bluetooth_classic_device_mac.mm
+++ b/device/bluetooth/bluetooth_classic_device_mac.mm
@@ -305,4 +305,8 @@ BluetoothUUID ExtractUuid(IOBluetoothSDPDataElement* service_class_data) {
base::SysNSStringToUTF8([device addressString]));
}
+bool BluetoothClassicDeviceMac::IsLowEnergyDevice() {
+ return false;
+}
+
} // namespace device
diff --git a/device/bluetooth/bluetooth_device_mac.h b/device/bluetooth/bluetooth_device_mac.h
index 10245db6516b08a8e50e6637011a3a960440625c..0213e20aee170aae0a4b73e93f47c418110ef289 100644
--- a/device/bluetooth/bluetooth_device_mac.h
+++ b/device/bluetooth/bluetooth_device_mac.h
@@ -30,6 +30,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothDeviceMac : public BluetoothDevice {
BluetoothGattService::GattErrorCode error_code);
static BluetoothGattService::GattErrorCode GetGattErrorCodeFromNSError(
NSError* error);
+ virtual bool IsLowEnergyDevice() = 0;
protected:
BluetoothDeviceMac(BluetoothAdapterMac* adapter);
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.h b/device/bluetooth/bluetooth_low_energy_device_mac.h
index 6db4f57489d65f3363b716c49037c4fac1389f0c..1e5ce4777b37c6a65a64ba5f7d5bb630b2ebe687 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.h
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.h
@@ -83,6 +83,7 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothLowEnergyDeviceMac
const device::BluetoothUUID& uuid,
ConnectToServiceCallback callback,
ConnectToServiceErrorCallback error_callback) override;
+ bool IsLowEnergyDevice() override;
protected:
// BluetoothDevice override.
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.mm b/device/bluetooth/bluetooth_low_energy_device_mac.mm
index 9a89728b0fc47cd7ca6e20d2ffbe7483010cbe39..73b1bee8e64664bdc98e45869accdf0ade2250b0 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.mm
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.mm
@@ -207,6 +207,10 @@ @interface CBPeripheral (HighSierraSDK)
NOTIMPLEMENTED();
}
+bool BluetoothLowEnergyDeviceMac::IsLowEnergyDevice() {
+ return true;
+}
+
void BluetoothLowEnergyDeviceMac::CreateGattConnectionImpl(
absl::optional<BluetoothUUID> serivce_uuid) {
if (!IsGattConnected()) {

View File

@@ -0,0 +1,338 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Cheng <dcheng@chromium.org>
Date: Tue, 14 Jun 2022 19:11:17 +0000
Subject: Ensure raw_ptr<T> and T* are treated identically in //base callback.
There are safety checks associated with raw pointers (e.g. ensuring
receiver pointers are not raw pointers). Make sure these checks are
applied whether the input type is T* or raw_ptr<T>.
- Implement base::IsPointer<T> and base::RemovePointer<T>, which are
similar to std::is_pointer<T> and std::remove_pointer<T>, except they
also consider raw_ptr<T> a raw pointer type.
- Fix failures from the strengthened asserts: WebAppInstallFinalizer
does not need a callback at all, while the privacy sandbox dialog
tests can safely use base::Unretained().
- Add test cases to cover this in the //base callback nocompile test
suite.
- Fix the existing nocompile tests, which did not escape `||` and
inadvertently matched any error text.
(cherry picked from commit 00c072a2c7f24921af3bbf8441abb34ecb0551a6)
Bug: 1335458
Change-Id: I470e3d5bc35ed52bf125136db738a868ef90b7e7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3700700
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1013266}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3703779
Cr-Commit-Position: refs/branch-heads/5005@{#1173}
Cr-Branched-From: 5b4d9450fee01f821b6400e947b3839727643a71-refs/heads/main@{#992738}
diff --git a/base/bind_internal.h b/base/bind_internal.h
index ea2ab97fae8adb9d171f9bf4dd367a12498112ec..a14ba7865c3581ee467fec39bd94bd58b6d07f3f 100644
--- a/base/bind_internal.h
+++ b/base/bind_internal.h
@@ -859,8 +859,8 @@ bool QueryCancellationTraits(const BindStateBase* base,
template <typename Functor, typename Receiver, typename... Unused>
std::enable_if_t<
!(MakeFunctorTraits<Functor>::is_method &&
- std::is_pointer_v<std::decay_t<Receiver>> &&
- IsRefCountedType<std::remove_pointer_t<std::decay_t<Receiver>>>::value)>
+ IsPointerV<std::decay_t<Receiver>> &&
+ IsRefCountedType<RemovePointerT<std::decay_t<Receiver>>>::value)>
BanUnconstructedRefCountedReceiver(const Receiver& receiver, Unused&&...) {}
template <typename Functor>
@@ -870,8 +870,8 @@ void BanUnconstructedRefCountedReceiver() {}
template <typename Functor, typename Receiver, typename... Unused>
std::enable_if_t<
MakeFunctorTraits<Functor>::is_method &&
- std::is_pointer_v<std::decay_t<Receiver>> &&
- IsRefCountedType<std::remove_pointer_t<std::decay_t<Receiver>>>::value>
+ IsPointerV<std::decay_t<Receiver>> &&
+ IsRefCountedType<RemovePointerT<std::decay_t<Receiver>>>::value>
BanUnconstructedRefCountedReceiver(const Receiver& receiver, Unused&&...) {
DCHECK(receiver);
@@ -1006,19 +1006,20 @@ struct MakeBindStateTypeImpl<true, Functor, Receiver, BoundArgs...> {
static_assert(!std::is_array_v<std::remove_reference_t<Receiver>>,
"First bound argument to a method cannot be an array.");
static_assert(
- !std::is_pointer_v<DecayedReceiver> ||
- IsRefCountedType<std::remove_pointer_t<DecayedReceiver>>::value,
+ !IsPointerV<DecayedReceiver> ||
+ IsRefCountedType<RemovePointerT<DecayedReceiver>>::value,
"Receivers may not be raw pointers. If using a raw pointer here is safe"
" and has no lifetime concerns, use base::Unretained() and document why"
" it's safe.");
+
static_assert(!HasRefCountedTypeAsRawPtr<std::decay_t<BoundArgs>...>::value,
"A parameter is a refcounted type and needs scoped_refptr.");
public:
using Type = BindState<
std::decay_t<Functor>,
- std::conditional_t<std::is_pointer_v<DecayedReceiver>,
- scoped_refptr<std::remove_pointer_t<DecayedReceiver>>,
+ std::conditional_t<IsPointerV<DecayedReceiver>,
+ scoped_refptr<RemovePointerT<DecayedReceiver>>,
DecayedReceiver>,
MakeStorageType<BoundArgs>...>;
};
diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc
index a5f681fe53b97a5a98f5918ac5cbc9e6cbbf790b..6844b6796d9f19e98baf13adae772d01eedf6846 100644
--- a/base/bind_unittest.cc
+++ b/base/bind_unittest.cc
@@ -1169,6 +1169,28 @@ TYPED_TEST(BindVariantsTest, UniquePtrReceiver) {
TypeParam::Bind(&NoRef::VoidMethod0, std::move(no_ref)).Run();
}
+TYPED_TEST(BindVariantsTest, ImplicitRefPtrReceiver) {
+ StrictMock<HasRef> has_ref;
+ EXPECT_CALL(has_ref, AddRef()).Times(1);
+ EXPECT_CALL(has_ref, Release()).Times(1);
+ EXPECT_CALL(has_ref, HasAtLeastOneRef()).WillRepeatedly(Return(true));
+
+ HasRef* ptr = &has_ref;
+ auto ptr_cb = TypeParam::Bind(&HasRef::HasAtLeastOneRef, ptr);
+ EXPECT_EQ(1, std::move(ptr_cb).Run());
+}
+
+TYPED_TEST(BindVariantsTest, RawPtrReceiver) {
+ StrictMock<HasRef> has_ref;
+ EXPECT_CALL(has_ref, AddRef()).Times(1);
+ EXPECT_CALL(has_ref, Release()).Times(1);
+ EXPECT_CALL(has_ref, HasAtLeastOneRef()).WillRepeatedly(Return(true));
+
+ raw_ptr<HasRef> rawptr(&has_ref);
+ auto rawptr_cb = TypeParam::Bind(&HasRef::HasAtLeastOneRef, rawptr);
+ EXPECT_EQ(1, std::move(rawptr_cb).Run());
+}
+
// Tests for Passed() wrapper support:
// - Passed() can be constructed from a pointer to scoper.
// - Passed() can be constructed from a scoper rvalue.
@@ -1751,6 +1773,12 @@ TEST(BindDeathTest, BanFirstOwnerOfRefCountedType) {
EXPECT_CALL(has_ref, HasAtLeastOneRef()).WillOnce(Return(false));
base::BindOnce(&HasRef::VoidMethod0, &has_ref);
});
+
+ EXPECT_DCHECK_DEATH({
+ raw_ptr<HasRef> rawptr(&has_ref);
+ EXPECT_CALL(has_ref, HasAtLeastOneRef()).WillOnce(Return(false));
+ base::BindOnce(&HasRef::VoidMethod0, rawptr);
+ });
}
} // namespace
diff --git a/base/bind_unittest.nc b/base/bind_unittest.nc
index 20b0e0ba2ceea9576cae13bf08cdb0e979f70018..29807298ca3c13fd54c45c14d496bebdb3123816 100644
--- a/base/bind_unittest.nc
+++ b/base/bind_unittest.nc
@@ -93,7 +93,7 @@ void WontCompile() {
method_to_const_cb.Run();
}
-#elif defined(NCTEST_METHOD_BIND_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!std::is_pointer_v<base::NoRef *> || IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained() and document why it's safe.\""]
+#elif defined(NCTEST_METHOD_BIND_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!IsPointerV<base::NoRef \*> \|\| IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained\(\) and document why it's safe.\""]
// Method bound to non-refcounted object.
@@ -106,7 +106,7 @@ void WontCompile() {
no_ref_cb.Run();
}
-#elif defined(NCTEST_CONST_METHOD_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!std::is_pointer_v<base::NoRef *> || IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained() and document why it's safe.\""]
+#elif defined(NCTEST_CONST_METHOD_BIND_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!IsPointerV<base::NoRef \*> \|\| IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained\(\) and document why it's safe.\""]
// Const Method bound to non-refcounted object.
//
@@ -118,6 +118,33 @@ void WontCompile() {
no_ref_const_cb.Run();
}
+#elif defined(NCTEST_METHOD_BIND_RAW_PTR_RECEIVER_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!IsPointerV<base::raw_ptr<base::NoRef, [^>]+>> \|\| IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained\(\) and document why it's safe.\""]
+
+
+// Method bound to non-refcounted object.
+//
+// We require refcounts unless you have Unretained().
+void WontCompile() {
+ NoRef no_ref;
+ raw_ptr<NoRef> rawptr(&no_ref);
+ RepeatingCallback<void()> no_ref_cb =
+ BindRepeating(&NoRef::VoidMethod0, rawptr);
+ no_ref_cb.Run();
+}
+
+#elif defined(NCTEST_CONST_METHOD_BIND_RAW_PTR_RECEIVER_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed due to requirement '!IsPointerV<base::raw_ptr<base::NoRef, [^>]+>> \|\| IsRefCountedType<base::NoRef, void>::value' \"Receivers may not be raw pointers. If using a raw pointer here is safe and has no lifetime concerns, use base::Unretained\(\) and document why it's safe.\""]
+
+// Const Method bound to non-refcounted object.
+//
+// We require refcounts unless you have Unretained().
+void WontCompile() {
+ NoRef no_ref;
+ raw_ptr<NoRef> rawptr(&no_ref);
+ RepeatingCallback<void()> no_ref_const_cb =
+ BindRepeating(&NoRef::VoidConstMethod0, rawptr);
+ no_ref_const_cb.Run();
+}
+
#elif defined(NCTEST_CONST_POINTER) // [r"static_assert failed.+?BindArgument<0>::ForwardedAs<.+?>::ToParamWithType<.+?>::kCanBeForwardedToBoundFunctor.+?Type mismatch between bound argument and bound functor's parameter\."]
// Const argument used with non-const pointer parameter of same type.
//
diff --git a/base/memory/raw_ptr.h b/base/memory/raw_ptr.h
index 9172af8ffe03bd038825b885ca46b7ffe8a3f90f..045c397fafb676eacd9b1030e114bd3875f918cd 100644
--- a/base/memory/raw_ptr.h
+++ b/base/memory/raw_ptr.h
@@ -882,6 +882,37 @@ RAW_PTR_FUNC_ATTRIBUTES bool operator>=(const raw_ptr<U, I>& lhs,
return lhs.GetForComparison() >= rhs.GetForComparison();
}
+// Template helpers for working with T* or raw_ptr<T>.
+template <typename T>
+struct IsPointer : std::false_type {};
+
+template <typename T>
+struct IsPointer<T*> : std::true_type {};
+
+template <typename T, typename I>
+struct IsPointer<raw_ptr<T, I>> : std::true_type {};
+
+template <typename T>
+inline constexpr bool IsPointerV = IsPointer<T>::value;
+
+template <typename T>
+struct RemovePointer {
+ using type = T;
+};
+
+template <typename T>
+struct RemovePointer<T*> {
+ using type = T;
+};
+
+template <typename T, typename I>
+struct RemovePointer<raw_ptr<T, I>> {
+ using type = T;
+};
+
+template <typename T>
+using RemovePointerT = typename RemovePointer<T>::type;
+
} // namespace base
using base::raw_ptr;
diff --git a/base/memory/raw_scoped_refptr_mismatch_checker.h b/base/memory/raw_scoped_refptr_mismatch_checker.h
index 9e50458ec98bb7e9041355fa06d14c6ba703b85c..7afae066fa3e318b346fac6b60ee55873b65f909 100644
--- a/base/memory/raw_scoped_refptr_mismatch_checker.h
+++ b/base/memory/raw_scoped_refptr_mismatch_checker.h
@@ -7,6 +7,7 @@
#include <type_traits>
+#include "base/memory/raw_ptr.h"
#include "base/template_util.h"
// It is dangerous to post a task with a T* argument where T is a subtype of
@@ -35,8 +36,8 @@ struct IsRefCountedType<T,
// pointer type and are convertible to a RefCounted(Base|ThreadSafeBase) type.
template <typename T>
struct NeedsScopedRefptrButGetsRawPtr
- : conjunction<std::is_pointer<T>,
- IsRefCountedType<std::remove_pointer_t<T>>> {
+ : conjunction<base::IsPointer<T>,
+ IsRefCountedType<base::RemovePointerT<T>>> {
static_assert(!std::is_reference<T>::value,
"NeedsScopedRefptrButGetsRawPtr requires non-reference type.");
};
diff --git a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc
index 47d3cc7579b8375e7e356385c0537eec59dd591a..a2808c7d198d7b2e469e2c639e028cbf9cf9b583 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc
+++ b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc
@@ -67,9 +67,7 @@ class PrivacySandboxDialogHandlerTest : public testing::Test {
content::TestWebUI* web_ui() { return web_ui_.get(); }
PrivacySandboxDialogHandler* handler() { return handler_.get(); }
TestingProfile* profile() { return &profile_; }
- raw_ptr<MockPrivacySandboxDialogView> dialog_mock() {
- return dialog_mock_.get();
- }
+ MockPrivacySandboxDialogView* dialog_mock() { return dialog_mock_.get(); }
MockPrivacySandboxService* mock_privacy_sandbox_service() {
return mock_privacy_sandbox_service_;
}
@@ -93,13 +91,16 @@ class PrivacySandboxConsentDialogHandlerTest
: public PrivacySandboxDialogHandlerTest {
protected:
std::unique_ptr<PrivacySandboxDialogHandler> CreateHandler() override {
+ // base::Unretained is safe because the created handler does not outlive the
+ // mock.
return std::make_unique<PrivacySandboxDialogHandler>(
- base::BindOnce(&MockPrivacySandboxDialogView::Close, dialog_mock()),
+ base::BindOnce(&MockPrivacySandboxDialogView::Close,
+ base::Unretained(dialog_mock())),
base::BindOnce(&MockPrivacySandboxDialogView::ResizeNativeView,
- dialog_mock()),
+ base::Unretained(dialog_mock())),
base::BindOnce(
&MockPrivacySandboxDialogView::OpenPrivacySandboxSettings,
- dialog_mock()),
+ base::Unretained(dialog_mock())),
PrivacySandboxService::DialogType::kConsent);
}
};
@@ -193,13 +194,16 @@ class PrivacySandboxNoticeDialogHandlerTest
: public PrivacySandboxDialogHandlerTest {
protected:
std::unique_ptr<PrivacySandboxDialogHandler> CreateHandler() override {
+ // base::Unretained is safe because the created handler does not outlive the
+ // mock.
return std::make_unique<PrivacySandboxDialogHandler>(
- base::BindOnce(&MockPrivacySandboxDialogView::Close, dialog_mock()),
+ base::BindOnce(&MockPrivacySandboxDialogView::Close,
+ base::Unretained(dialog_mock())),
base::BindOnce(&MockPrivacySandboxDialogView::ResizeNativeView,
- dialog_mock()),
+ base::Unretained(dialog_mock())),
base::BindOnce(
&MockPrivacySandboxDialogView::OpenPrivacySandboxSettings,
- dialog_mock()),
+ base::Unretained(dialog_mock())),
PrivacySandboxService::DialogType::kNotice);
}
};
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index 7f36ec57e697b871b677c09c255cbce9bcb09b3a..b4e6af317aa25d7f8eb11bfda1d81c999a93e1fb 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -568,10 +568,6 @@ void WebAppInstallFinalizer::SetWebAppManifestFieldsAndWriteData(
web_app_info.shortcuts_menu_icon_bitmaps;
IconsMap other_icon_bitmaps = web_app_info.other_icon_bitmaps;
- auto write_icons_callback = base::BindOnce(
- &WebAppIconManager::WriteData, icon_manager_, app_id,
- std::move(icon_bitmaps), std::move(shortcuts_menu_icon_bitmaps),
- std::move(other_icon_bitmaps));
auto write_translations_callback = base::BindOnce(
&WebAppInstallFinalizer::WriteTranslations,
weak_ptr_factory_.GetWeakPtr(), app_id, std::move(web_app_info));
@@ -579,11 +575,12 @@ void WebAppInstallFinalizer::SetWebAppManifestFieldsAndWriteData(
base::BindOnce(&WebAppInstallFinalizer::CommitToSyncBridge,
weak_ptr_factory_.GetWeakPtr(), std::move(web_app));
- std::move(write_icons_callback)
- .Run(base::BindOnce(
- std::move(write_translations_callback),
- base::BindOnce(std::move(commit_to_sync_bridge_callback),
- std::move(commit_callback))));
+ icon_manager_->WriteData(
+ app_id, std::move(icon_bitmaps), std::move(shortcuts_menu_icon_bitmaps),
+ std::move(other_icon_bitmaps),
+ base::BindOnce(std::move(write_translations_callback),
+ base::BindOnce(std::move(commit_to_sync_bridge_callback),
+ std::move(commit_callback))));
}
void WebAppInstallFinalizer::WriteTranslations(

View File

@@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <sattard@salesforce.com>
Date: Tue, 26 Jul 2022 00:05:29 -0700
Subject: chore: add electron deps to gitignores
Makes things like "git status" quicker when developing electron locally
diff --git a/.gitignore b/.gitignore
index 5a473b051598d15aee36550a2a393912f155f534..0f09c61758711b46838031b98e76c4cf5ca9fda8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -218,6 +218,7 @@ vs-chromium-project.txt
/delegate_execute
/device/serial/device_serial_mojo.xml
/docs/website
+/electron
/google_apis/gcm/gcm.xml
/google_apis/internal
/googleurl
diff --git a/third_party/.gitignore b/third_party/.gitignore
index 9163a971d3a0ac13e8e3b8620cd9bfd196d7fd78..6feb071b816c26e8da9080393eaa52ef4c6d802a 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -78,6 +78,7 @@
/directxsdk
/dom_distiller_js/dist
/eigen3/src
+/electron_node
/elfutils/src
/emoji-segmenter/src
/emoji-metadata/src
@@ -172,6 +173,7 @@
/mocha
/mockito/src
/nacl_sdk_binaries/
+/nan
/nasm
/nearby/src
/neon_2_sse/src
@@ -235,6 +237,7 @@
/speex
/sqlite/src
/sqlite4java/lib/
+/squirrel.mac
/subresource-filter-ruleset/data/UnindexedRules
/swiftshader/
/syzygy

View File

@@ -0,0 +1,76 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: deepak1556 <hop2deep@gmail.com>
Date: Fri, 29 Jul 2022 00:29:35 +0900
Subject: chore: allow chromium to handle synthetic mouse events for touch
With WCO, allow chromium to handle synthetic mouse events generated for touch
actions in the non-client caption area.
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index 65b9f5e5f81e8ef8b591ef2e027e095df11c0d7b..b5d764db353e758e8feefd5fd0a045be27cf09c6 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -1169,6 +1169,10 @@ void DesktopWindowTreeHostWin::HandleWindowScaleFactorChanged(
}
}
+bool DesktopWindowTreeHostWin::HandleMouseEventForCaption(UINT message) const {
+ return false;
+}
+
DesktopNativeCursorManager*
DesktopWindowTreeHostWin::GetSingletonDesktopNativeCursorManager() {
return new DesktopNativeCursorManagerWin();
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
index 444581249014a8ce301591f269dbb194f0520732..9377f26b081b717db6b50c13ce3795907cf2fcd2 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -262,6 +262,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin
void HandleWindowSizeChanging() override;
void HandleWindowSizeUnchanged() override;
void HandleWindowScaleFactorChanged(float window_scale_factor) override;
+ bool HandleMouseEventForCaption(UINT message) const override;
Widget* GetWidget();
const Widget* GetWidget() const;
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 86f06d2a2c9588a2210a9f78f47e73f1b7c5e329..f943179d0071ed77fe49f5e4e9cff05eeff665e0 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -3049,15 +3049,19 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message,
SetMsgHandled(FALSE);
// We must let Windows handle the caption buttons if it's drawing them, or
// they won't work.
+ bool simulate_mouse_event_for_caption = false;
if (delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN &&
(hittest == HTCLOSE || hittest == HTMINBUTTON ||
hittest == HTMAXBUTTON)) {
- SetMsgHandled(FALSE);
+ simulate_mouse_event_for_caption =
+ delegate_->HandleMouseEventForCaption(message);
+ if (!simulate_mouse_event_for_caption)
+ SetMsgHandled(FALSE);
}
// Let resize events fall through. Ignore everything else, as we're either
// letting Windows handle it above or we've already handled the equivalent
// touch message.
- if (!IsHitTestOnResizeHandle(hittest))
+ if (!IsHitTestOnResizeHandle(hittest) && !simulate_mouse_event_for_caption)
return 0;
}
diff --git a/ui/views/win/hwnd_message_handler_delegate.h b/ui/views/win/hwnd_message_handler_delegate.h
index 5dbb192d0840ca0ded61397c399b774a8cb05cce..098a9c3140e9e140fdc8f0dc9cf4e8ec84451221 100644
--- a/ui/views/win/hwnd_message_handler_delegate.h
+++ b/ui/views/win/hwnd_message_handler_delegate.h
@@ -258,6 +258,10 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate {
// Called when the window scale factor has changed.
virtual void HandleWindowScaleFactorChanged(float window_scale_factor) = 0;
+ // Called when synthetic mouse event is generated for touch event on
+ // caption buttons.
+ virtual bool HandleMouseEventForCaption(UINT message) const = 0;
+
protected:
virtual ~HWNDMessageHandlerDelegate() = default;
};

View File

@@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: deepak1556 <hop2deep@gmail.com>
Date: Fri, 20 May 2022 00:29:34 +0900
Subject: custom_protocols_plzserviceworker.patch
Allow registering custom protocols to handle service worker main script fetching with PlzServiceWorker.
Refs https://bugs.chromium.org/p/chromium/issues/detail?id=996511
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 4ce769314c6dddae41d915c7f05916f77f26a86e..d8f6882f2dee93fc127e7617abbcc7b71a65775d 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -1600,6 +1600,29 @@ ServiceWorkerContextWrapper::GetLoaderFactoryForBrowserInitiatedRequest(
std::unique_ptr<network::PendingSharedURLLoaderFactory>
loader_factory_bundle_info =
context()->loader_factory_bundle_for_update_check()->Clone();
+
+ // Give the embedder a chance to register custom schemes that can
+ // handle loading the service worker main script.
+ // Previous registration triggered by
+ // ServiceWorkerContextWrapper::CreateNonNetworkPendingURLLoaderFactoryBundleForUpdateCheck
+ // happens early on browser startup before the JS in the main process
+ // is run by the embedder.
+ auto* factory_bundle = static_cast<blink::PendingURLLoaderFactoryBundle*>(
+ loader_factory_bundle_info.get());
+ ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories;
+ GetContentClient()
+ ->browser()
+ ->RegisterNonNetworkServiceWorkerUpdateURLLoaderFactories(
+ storage_partition_->browser_context(), &non_network_factories);
+ for (auto& pair : non_network_factories) {
+ const std::string& scheme = pair.first;
+ mojo::PendingRemote<network::mojom::URLLoaderFactory>& factory_remote =
+ pair.second;
+
+ factory_bundle->pending_scheme_specific_factories().emplace(
+ scheme, std::move(factory_remote));
+ }
+
static_cast<blink::PendingURLLoaderFactoryBundle*>(
loader_factory_bundle_info.get())
->pending_default_factory() = std::move(remote);

View File

@@ -20,14 +20,15 @@ to deal with color spaces. That is being tracked at
https://crbug.com/634542 and https://crbug.com/711107.
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 3824e500fd88105e9c6d459f23097b2b4f0686ed..927ede7608a5c6992e9f9fb05e4b8e43b7e10cf8 100644
index 3824e500fd88105e9c6d459f23097b2b4f0686ed..958c592de85a194d5c2ba640a64549afedbb73c1 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1889,6 +1889,9 @@ void LayerTreeHostImpl::SetIsLikelyToRequireADraw(
@@ -1889,6 +1889,10 @@ void LayerTreeHostImpl::SetIsLikelyToRequireADraw(
TargetColorParams LayerTreeHostImpl::GetTargetColorParams(
gfx::ContentColorUsage content_color_usage) const {
TargetColorParams params;
+ if (!settings_.enable_color_correct_rendering) {
+ params.color_space = gfx::ColorSpace();
+ return params;
+ }
@@ -260,7 +261,7 @@ index 5f2451eefad211c85460eb457ad3d6e184540d59..d9d8352c6b1b8db8d86ad1ed1d4a3d30
sandbox::policy::switches::kDisableSeccompFilterSandbox,
sandbox::policy::switches::kNoSandbox,
diff --git a/third_party/blink/renderer/platform/graphics/canvas_color_params.cc b/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
index 6260d73068636f4a8d4c73c161a4ffe165b267d0..2686ef0dfb7d8b667647d11fece5aa8e1be76fe5 100644
index 6260d73068636f4a8d4c73c161a4ffe165b267d0..2d86f6829b9ad8927a03f288cc78d6ce65d7c5c0 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_color_params.cc
@@ -4,6 +4,7 @@
@@ -279,7 +280,18 @@ index 6260d73068636f4a8d4c73c161a4ffe165b267d0..2686ef0dfb7d8b667647d11fece5aa8e
namespace blink {
@@ -118,6 +120,11 @@ uint8_t CanvasColorParams::BytesPerPixel() const {
@@ -21,6 +23,10 @@ namespace {
// Level 4 specification.
gfx::ColorSpace PredefinedColorSpaceToGfxColorSpace(
PredefinedColorSpace color_space) {
+ auto* cmd_line = base::CommandLine::ForCurrentProcess();
+ if (cmd_line->HasSwitch(switches::kDisableColorCorrectRendering)) {
+ return gfx::ColorSpace();
+ }
switch (color_space) {
case PredefinedColorSpace::kSRGB:
return gfx::ColorSpace::CreateSRGB();
@@ -118,6 +124,11 @@ uint8_t CanvasColorParams::BytesPerPixel() const {
}
gfx::ColorSpace CanvasColorParams::GetStorageGfxColorSpace() const {

View File

@@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dominik=20Inf=C3=BChr?= <dinfuehr@chromium.org>
Date: Tue, 22 Mar 2022 17:33:03 +0000
Subject: Do not reduce page size from 64K to 4K on Linux/ARM64
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This fixes build on Asahi Linux which uses 16K pages.
Change-Id: I8cf3664849d98bcb984f339ebf9076d1cfaf5701
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3542265
Reviewed-by: Andrew Grieve <agrieve@chromium.org>
Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#983900}
diff --git a/build/config/android/BUILD.gn b/build/config/android/BUILD.gn
index 4fc5565e27a596fe4aa1a093cfc0567fbd24b2a0..69341cea35d0c44aeb5f09b39783bd2abfcdcc43 100644
--- a/build/config/android/BUILD.gn
+++ b/build/config/android/BUILD.gn
@@ -47,6 +47,13 @@ config("compiler") {
"-Wl,--exclude-libs=libvpx_assembly_arm.a",
]
+ if (current_cpu == "arm64") {
+ # Reduce the page size from 65536 in order to reduce binary size slightly
+ # by shrinking the alignment gap between segments. This also causes all
+ # segments to be mapped adjacently, which breakpad relies on.
+ ldflags += [ "-Wl,-z,max-page-size=4096" ]
+ }
+
if (current_cpu == "arm64") {
if (arm_control_flow_integrity == "standard") {
cflags += [ "-mbranch-protection=standard" ]
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 7b9ce0172eedf2b55fe7f3de7baee2f2028f4a7b..8f932d6493ec84f89c69078f95db7fc7ad03ef05 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -454,14 +454,7 @@ config("compiler") {
# Linux-specific compiler flags setup.
# ------------------------------------
- if ((is_posix || is_fuchsia) && !is_apple && use_lld) {
- if (current_cpu == "arm64") {
- # Reduce the page size from 65536 in order to reduce binary size slightly
- # by shrinking the alignment gap between segments. This also causes all
- # segments to be mapped adjacently, which breakpad relies on.
- ldflags += [ "-Wl,-z,max-page-size=4096" ]
- }
- } else if (use_gold) {
+ if (use_gold) {
ldflags += [ "-fuse-ld=gold" ]
if (!is_android) {
# On Android, this isn't needed. gcc in the NDK knows to look next to

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 7d3a441bdb64268ed5fbfa7bf589fb35a2fd1b75..a3e45e9baa09bfc87be5b7ff589ac76841c686d4 100644
--- a/chrome/browser/process_singleton_posix.cc
+++ b/chrome/browser/process_singleton_posix.cc
@@ -614,6 +614,7 @@ class ProcessSingleton::LinuxWatcher
// |reader| is for sending back ACK message.
void HandleMessage(const std::string& current_dir,
const std::vector<std::string>& argv,
+ const std::vector<const uint8_t> additional_data,
SocketReader* reader);
private:
@@ -668,13 +669,16 @@ void ProcessSingleton::LinuxWatcher::StartListening(int socket) {
}
void ProcessSingleton::LinuxWatcher::HandleMessage(
- const std::string& current_dir, const std::vector<std::string>& argv,
+ const std::string& current_dir,
+ const std::vector<std::string>& argv,
+ const std::vector<const uint8_t> additional_data,
SocketReader* reader) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
DCHECK(reader);
if (parent_->notification_callback_.Run(base::CommandLine(argv),
- base::FilePath(current_dir))) {
+ base::FilePath(current_dir),
+ std::move(additional_data))) {
// Send back "ACK" message to prevent the client process from starting up.
reader->FinishWithACK(kACKToken, base::size(kACKToken) - 1);
} else {
@@ -722,7 +726,8 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
}
}
- // Validate the message. The shortest message is kStartToken\0x\0x
+ // Validate the message. The shortest message kStartToken\0\00
+ // The shortest message with additional data is kStartToken\0\00\00\0.
const size_t kMinMessageLength = base::size(kStartToken) + 4;
if (bytes_read_ < kMinMessageLength) {
buf_[bytes_read_] = 0;
@@ -752,10 +757,28 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
tokens.erase(tokens.begin());
tokens.erase(tokens.begin());
+ size_t num_args;
+ base::StringToSizeT(tokens[0], &num_args);
+ std::vector<std::string> command_line(tokens.begin() + 1, tokens.begin() + 1 + num_args);
+
+ std::vector<const uint8_t> additional_data;
+ if (tokens.size() >= 3 + num_args) {
+ size_t additional_data_size;
+ base::StringToSizeT(tokens[1 + num_args], &additional_data_size);
+ std::string remaining_args = base::JoinString(
+ base::make_span(tokens.begin() + 2 + num_args, tokens.end()),
+ std::string(1, kTokenDelimiter));
+ const uint8_t* additional_data_bits =
+ reinterpret_cast<const uint8_t*>(remaining_args.c_str());
+ additional_data = std::vector<const uint8_t>(
+ additional_data_bits, additional_data_bits + additional_data_size);
+ }
+
// Return to the UI thread to handle opening a new browser tab.
ui_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::HandleMessage,
- parent_, current_dir, tokens, this));
+ parent_, current_dir, command_line,
+ std::move(additional_data), this));
fd_watch_controller_.reset();
// LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
@@ -784,8 +807,10 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
//
ProcessSingleton::ProcessSingleton(
const base::FilePath& user_data_dir,
+ const base::span<const uint8_t> additional_data,
const NotificationCallback& notification_callback)
: notification_callback_(notification_callback),
+ additional_data_(additional_data),
current_pid_(base::GetCurrentProcId()),
watcher_(new LinuxWatcher(this)) {
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
@@ -904,7 +929,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
sizeof(socket_timeout));
// Found another process, prepare our command line
- // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>".
+ // format is "START\0<current-dir>\0<n-args>\0<argv[0]>\0...\0<argv[n]>
+ // \0<additional-data-length>\0<additional-data>".
std::string to_send(kStartToken);
to_send.push_back(kTokenDelimiter);
@@ -914,11 +940,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
to_send.append(current_dir.value());
const std::vector<std::string>& argv = cmd_line.argv();
+ to_send.push_back(kTokenDelimiter);
+ to_send.append(base::NumberToString(argv.size()));
for (auto it = argv.begin(); it != argv.end(); ++it) {
to_send.push_back(kTokenDelimiter);
to_send.append(*it);
}
+ size_t data_to_send_size = additional_data_.size_bytes();
+ if (data_to_send_size) {
+ to_send.push_back(kTokenDelimiter);
+ to_send.append(base::NumberToString(data_to_send_size));
+ to_send.push_back(kTokenDelimiter);
+ to_send.append(reinterpret_cast<const char*>(additional_data_.data()), data_to_send_size);
+ }
+
// Send the message
if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
// Try to kill the other process, because it might have been dead.
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
index 0ea5eb3e3cf055d981ab73486115bac53287f2d7..fe68beb4b2522d27e07dbbb3341f100f14494680 100644
--- a/chrome/browser/process_singleton_win.cc
+++ b/chrome/browser/process_singleton_win.cc
@@ -80,10 +80,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
bool ParseCommandLine(const COPYDATASTRUCT* cds,
base::CommandLine* parsed_command_line,
- base::FilePath* current_directory) {
+ base::FilePath* current_directory,
+ std::vector<const uint8_t>* parsed_additional_data) {
// We should have enough room for the shortest command (min_message_size)
// and also be a multiple of wchar_t bytes. The shortest command
- // possible is L"START\0\0" (empty current directory and command line).
+ // possible is L"START\0\0" (empty command line, current directory,
+ // and additional data).
static const int min_message_size = 7;
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
cds->cbData % sizeof(wchar_t) != 0) {
@@ -133,6 +135,37 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
const std::wstring cmd_line =
msg.substr(second_null + 1, third_null - second_null);
*parsed_command_line = base::CommandLine::FromString(cmd_line);
+
+ const std::wstring::size_type fourth_null =
+ msg.find_first_of(L'\0', third_null + 1);
+ if (fourth_null == std::wstring::npos ||
+ fourth_null == msg.length()) {
+ // No additional data was provided.
+ return true;
+ }
+
+ // Get length of the additional data.
+ const std::wstring additional_data_length_string =
+ msg.substr(third_null + 1, fourth_null - third_null);
+ size_t additional_data_length;
+ base::StringToSizeT(additional_data_length_string, &additional_data_length);
+
+ const std::wstring::size_type fifth_null =
+ msg.find_first_of(L'\0', fourth_null + 1);
+ if (fifth_null == std::wstring::npos ||
+ fifth_null == msg.length()) {
+ LOG(WARNING) << "Invalid format for start command, we need a string in 6 "
+ "parts separated by NULLs";
+ }
+
+ // Get the actual additional data.
+ const std::wstring additional_data =
+ msg.substr(fourth_null + 1, fifth_null - fourth_null);
+ const uint8_t* additional_data_bytes =
+ reinterpret_cast<const uint8_t*>(additional_data.c_str());
+ *parsed_additional_data = std::vector<const uint8_t>(additional_data_bytes,
+ additional_data_bytes + additional_data_length);
+
return true;
}
return false;
@@ -154,13 +187,14 @@ bool ProcessLaunchNotification(
base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
base::FilePath current_directory;
- if (!ParseCommandLine(cds, &parsed_command_line, &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;
}
@@ -254,9 +288,11 @@ bool ProcessSingleton::EscapeVirtualization(
ProcessSingleton::ProcessSingleton(
const std::string& program_name,
const base::FilePath& user_data_dir,
+ const base::span<const uint8_t> additional_data,
bool is_app_sandboxed,
const NotificationCallback& notification_callback)
: notification_callback_(notification_callback),
+ additional_data_(additional_data),
program_name_(program_name),
is_app_sandboxed_(is_app_sandboxed),
is_virtualized_(false),
@@ -283,7 +319,7 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
return PROCESS_NONE;
}
- switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
+ switch (chrome::AttemptToNotifyRunningChrome(remote_window_, additional_data_)) {
case chrome::NOTIFY_SUCCESS:
return PROCESS_NOTIFIED;
case chrome::NOTIFY_FAILED:
diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..cfdb2d75532d270e3dd548eb7475a6cdbddf1016 100644
--- a/chrome/browser/win/chrome_process_finder.cc
+++ b/chrome/browser/win/chrome_process_finder.cc
@@ -36,7 +36,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
return base::win::MessageWindow::FindWindow(user_data_dir.value());
}
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
+NotifyChromeResult AttemptToNotifyRunningChrome(
+ HWND remote_window,
+ const base::span<const uint8_t> additional_data) {
TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome");
DCHECK(remote_window);
@@ -50,7 +52,8 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
}
// Send the command line to the remote chrome window.
- // Format is "START\0<<<current directory>>>\0<<<commandline>>>".
+ // Format is
+ // "START\0<current-directory>\0<command-line>\0<additional-data-length>\0<additional-data>".
std::wstring to_send(L"START\0", 6); // want the NULL in the string.
base::FilePath cur_dir;
if (!base::GetCurrentDirectory(&cur_dir)) {
@@ -64,6 +67,22 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
base::CommandLine::ForCurrentProcess()->GetCommandLineString());
to_send.append(L"\0", 1); // Null separator.
+ size_t additional_data_size = additional_data.size_bytes();
+ if (additional_data_size) {
+ // Send over the size, because the reinterpret cast to wchar_t could
+ // add padding.
+ to_send.append(base::UTF8ToWide(base::NumberToString(additional_data_size)));
+ to_send.append(L"\0", 1); // Null separator.
+
+ size_t padded_size = additional_data_size / sizeof(wchar_t);
+ if (additional_data_size % sizeof(wchar_t) != 0) {
+ padded_size++;
+ }
+ to_send.append(reinterpret_cast<const wchar_t*>(additional_data.data()),
+ padded_size);
+ to_send.append(L"\0", 1); // Null separator.
+ }
+
// Allow the current running browser window to make itself the foreground
// window (otherwise it will just flash in the taskbar).
::AllowSetForegroundWindow(process_id);
diff --git a/chrome/browser/win/chrome_process_finder.h b/chrome/browser/win/chrome_process_finder.h
index 5516673cee019f6060077091e59498bf9038cd6e..8edea5079b46c2cba67833114eb9c21d85cfc22d 100644
--- a/chrome/browser/win/chrome_process_finder.h
+++ b/chrome/browser/win/chrome_process_finder.h
@@ -7,6 +7,7 @@
#include <windows.h>
+#include "base/containers/span.h"
#include "base/time/time.h"
namespace base {
@@ -27,7 +28,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir);
// Attempts to send the current command line to an already running instance of
// Chrome via a WM_COPYDATA message.
// Returns true if a running Chrome is found and successfully notified.
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window);
+NotifyChromeResult AttemptToNotifyRunningChrome(
+ HWND remote_window,
+ const base::span<const uint8_t> additional_data);
// Changes the notification timeout to |new_timeout|, returns the old timeout.
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout);

View File

@@ -1,639 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Raymond Zhao <raymondzhao@microsoft.com>
Date: Tue, 7 Sep 2021 14:54:25 -0700
Subject: feat: Add data transfer mechanism to requestSingleInstanceLock flow
This patch adds code that allows for the second instance to send
additional data to the first instance, and for the first instance
to send additional data back to the second instance, during the
app.requestSingleInstanceLock call.
Firstly, this patch adds an additional_data parameter
to the constructor of ProcessSingleton, so that the second instance
can send additional data over to the first instance
while requesting the ProcessSingleton lock.
Then, we add additional processing to the second-instance event, both
so the first instance can receive additional data from the second
instance, but also so the second instance can send back additional
data to the first instance if needed.
diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
index 5a64220aaf1309832dc0ad543e353de67fe0a779..a568dd10d1ef8679d66f4cdc6a471c251cbcd4eb 100644
--- a/chrome/browser/process_singleton.h
+++ b/chrome/browser/process_singleton.h
@@ -18,6 +18,7 @@
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/process/process.h"
+#include "base/containers/span.h"
#include "ui/gfx/native_widget_types.h"
#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
@@ -93,6 +94,9 @@ class ProcessSingleton {
static constexpr int kNumNotifyResults = LAST_VALUE + 1;
+ using NotificationAckCallback =
+ base::RepeatingCallback<void(const base::span<const uint8_t>* ack_data)>;
+
// Implement this callback to handle notifications from other processes. The
// callback will receive the command line and directory with which the other
// Chrome process was launched. Return true if the command line will be
@@ -100,21 +104,27 @@ class ProcessSingleton {
// should handle it (i.e., because the current process is shutting down).
using NotificationCallback =
base::RepeatingCallback<bool(const base::CommandLine& command_line,
- const base::FilePath& current_directory)>;
+ const base::FilePath& current_directory,
+ const std::vector<const uint8_t> additional_data,
+ const NotificationAckCallback& ack_callback)>;
#if BUILDFLAG(IS_WIN)
ProcessSingleton(const std::string& program_name,
const base::FilePath& user_data_dir,
+ const base::span<const uint8_t> additional_data,
bool is_sandboxed,
- const NotificationCallback& notification_callback);
+ const NotificationCallback& notification_callback,
+ const NotificationAckCallback& ack_notification_callback);
#else
ProcessSingleton(const base::FilePath& user_data_dir,
- const NotificationCallback& notification_callback);
+ const base::span<const uint8_t> additional_data,
+ const NotificationCallback& notification_callback,
+ const NotificationAckCallback& ack_notification_callback);
+#endif
ProcessSingleton(const ProcessSingleton&) = delete;
ProcessSingleton& operator=(const ProcessSingleton&) = delete;
-#endif
~ProcessSingleton();
// Notify another process, if available. Otherwise sets ourselves as the
@@ -177,7 +187,13 @@ class ProcessSingleton {
#endif
private:
- NotificationCallback notification_callback_; // Handler for notifications.
+ // A callback to run when the first instance receives data from the second.
+ NotificationCallback notification_callback_;
+ // A callback to run when the second instance
+ // receives an acknowledgement from the first.
+ NotificationAckCallback notification_ack_callback_;
+ // Custom data to pass to the other instance during notify.
+ base::span<const uint8_t> additional_data_;
#if BUILDFLAG(IS_WIN)
bool EscapeVirtualization(const base::FilePath& user_data_dir);
@@ -190,6 +206,7 @@ class ProcessSingleton {
HANDLE lock_file_;
base::FilePath user_data_dir_;
ShouldKillRemoteProcessCallback should_kill_remote_process_callback_;
+ HANDLE ack_pipe_;
#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
// Return true if the given pid is one of our child processes.
// Assumes that the current pid is the root of all pids of the current
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
index 7d3a441bdb64268ed5fbfa7bf589fb35a2fd1b75..b23c16fde275fdba559abb1f30e42f65ddbfc332 100644
--- a/chrome/browser/process_singleton_posix.cc
+++ b/chrome/browser/process_singleton_posix.cc
@@ -148,7 +148,7 @@ const char kACKToken[] = "ACK";
const char kShutdownToken[] = "SHUTDOWN";
const char kTokenDelimiter = '\0';
const int kMaxMessageLength = 32 * 1024;
-const int kMaxACKMessageLength = base::size(kShutdownToken) - 1;
+const int kMaxACKMessageLength = kMaxMessageLength;
bool g_disable_prompt = false;
bool g_skip_is_chrome_process_check = false;
@@ -614,6 +614,7 @@ class ProcessSingleton::LinuxWatcher
// |reader| is for sending back ACK message.
void HandleMessage(const std::string& current_dir,
const std::vector<std::string>& argv,
+ const std::vector<const uint8_t> additional_data,
SocketReader* reader);
private:
@@ -638,6 +639,9 @@ class ProcessSingleton::LinuxWatcher
// The ProcessSingleton that owns us.
ProcessSingleton* const parent_;
+ bool ack_callback_called_ = false;
+ void AckCallback(SocketReader* reader, const base::span<const uint8_t>* response);
+
std::set<std::unique_ptr<SocketReader>, base::UniquePtrComparator> readers_;
};
@@ -668,16 +672,21 @@ void ProcessSingleton::LinuxWatcher::StartListening(int socket) {
}
void ProcessSingleton::LinuxWatcher::HandleMessage(
- const std::string& current_dir, const std::vector<std::string>& argv,
+ const std::string& current_dir,
+ const std::vector<std::string>& argv,
+ const std::vector<const uint8_t> additional_data,
SocketReader* reader) {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
DCHECK(reader);
- if (parent_->notification_callback_.Run(base::CommandLine(argv),
- base::FilePath(current_dir))) {
- // Send back "ACK" message to prevent the client process from starting up.
- reader->FinishWithACK(kACKToken, base::size(kACKToken) - 1);
- } else {
+ auto wrapped_ack_callback =
+ base::BindRepeating(&ProcessSingleton::LinuxWatcher::AckCallback,
+ base::Unretained(this), reader);
+ ack_callback_called_ = false;
+ if (!parent_->notification_callback_.Run(base::CommandLine(argv),
+ base::FilePath(current_dir),
+ std::move(additional_data),
+ wrapped_ack_callback)) {
LOG(WARNING) << "Not handling interprocess notification as browser"
" is shutting down";
// Send back "SHUTDOWN" message, so that the client process can start up
@@ -687,6 +696,22 @@ void ProcessSingleton::LinuxWatcher::HandleMessage(
}
}
+void ProcessSingleton::LinuxWatcher::AckCallback(
+ SocketReader* reader,
+ const base::span<const uint8_t>* response) {
+ // Send back "ACK" message to prevent the client process from starting up.
+ if (!ack_callback_called_) {
+ ack_callback_called_ = true;
+ std::string ack_message;
+ ack_message.append(kACKToken, base::size(kACKToken) - 1);
+ if (response && response->size_bytes()) {
+ ack_message.append(reinterpret_cast<const char*>(response->data()),
+ response->size_bytes());
+ }
+ reader->FinishWithACK(ack_message.c_str(), ack_message.size());
+ }
+}
+
void ProcessSingleton::LinuxWatcher::RemoveSocketReader(SocketReader* reader) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(reader);
@@ -722,7 +747,8 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
}
}
- // Validate the message. The shortest message is kStartToken\0x\0x
+ // Validate the message. The shortest message kStartToken\0\00
+ // The shortest message with additional data is kStartToken\0\00\00\0.
const size_t kMinMessageLength = base::size(kStartToken) + 4;
if (bytes_read_ < kMinMessageLength) {
buf_[bytes_read_] = 0;
@@ -752,10 +778,28 @@ void ProcessSingleton::LinuxWatcher::SocketReader::
tokens.erase(tokens.begin());
tokens.erase(tokens.begin());
+ size_t num_args;
+ base::StringToSizeT(tokens[0], &num_args);
+ std::vector<std::string> command_line(tokens.begin() + 1, tokens.begin() + 1 + num_args);
+
+ std::vector<const uint8_t> additional_data;
+ if (tokens.size() >= 3 + num_args) {
+ size_t additional_data_size;
+ base::StringToSizeT(tokens[1 + num_args], &additional_data_size);
+ std::string remaining_args = base::JoinString(
+ base::make_span(tokens.begin() + 2 + num_args, tokens.end()),
+ std::string(1, kTokenDelimiter));
+ const uint8_t* additional_data_bits =
+ reinterpret_cast<const uint8_t*>(remaining_args.c_str());
+ additional_data = std::vector<const uint8_t>(additional_data_bits,
+ additional_data_bits + additional_data_size);
+ }
+
// Return to the UI thread to handle opening a new browser tab.
ui_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::HandleMessage,
- parent_, current_dir, tokens, this));
+ parent_, current_dir, command_line,
+ std::move(additional_data), this));
fd_watch_controller_.reset();
// LinuxWatcher::HandleMessage() is in charge of destroying this SocketReader
@@ -784,8 +828,12 @@ void ProcessSingleton::LinuxWatcher::SocketReader::FinishWithACK(
//
ProcessSingleton::ProcessSingleton(
const base::FilePath& user_data_dir,
- const NotificationCallback& notification_callback)
+ const base::span<const uint8_t> additional_data,
+ const NotificationCallback& notification_callback,
+ const NotificationAckCallback& notification_ack_callback)
: notification_callback_(notification_callback),
+ notification_ack_callback_(notification_ack_callback),
+ additional_data_(additional_data),
current_pid_(base::GetCurrentProcId()),
watcher_(new LinuxWatcher(this)) {
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
@@ -904,7 +952,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
sizeof(socket_timeout));
// Found another process, prepare our command line
- // format is "START\0<current dir>\0<argv[0]>\0...\0<argv[n]>".
+ // format is "START\0<current-dir>\0<n-args>\0<argv[0]>\0...\0<argv[n]>
+ // \0<additional-data-length>\0<additional-data>".
std::string to_send(kStartToken);
to_send.push_back(kTokenDelimiter);
@@ -914,11 +963,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
to_send.append(current_dir.value());
const std::vector<std::string>& argv = cmd_line.argv();
+ to_send.push_back(kTokenDelimiter);
+ to_send.append(base::NumberToString(argv.size()));
for (auto it = argv.begin(); it != argv.end(); ++it) {
to_send.push_back(kTokenDelimiter);
to_send.append(*it);
}
+ size_t data_to_send_size = additional_data_.size_bytes();
+ if (data_to_send_size) {
+ to_send.push_back(kTokenDelimiter);
+ to_send.append(base::NumberToString(data_to_send_size));
+ to_send.push_back(kTokenDelimiter);
+ to_send.append(reinterpret_cast<const char*>(additional_data_.data()), data_to_send_size);
+ }
+
// Send the message
if (!WriteToSocket(socket.fd(), to_send.data(), to_send.length())) {
// Try to kill the other process, because it might have been dead.
@@ -960,6 +1019,17 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
linux_ui->NotifyWindowManagerStartupComplete();
#endif
+ size_t ack_data_len = len - (base::size(kACKToken) - 1);
+ if (ack_data_len) {
+ const uint8_t* raw_ack_data =
+ reinterpret_cast<const uint8_t*>(buf + base::size(kACKToken) - 1);
+ base::span<const uint8_t> ack_data =
+ base::make_span(raw_ack_data, raw_ack_data + ack_data_len);
+ notification_ack_callback_.Run(&ack_data);
+ } else {
+ notification_ack_callback_.Run(nullptr);
+ }
+
// Assume the other process is handling the request.
return PROCESS_NOTIFIED;
}
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
index 0ea5eb3e3cf055d981ab73486115bac53287f2d7..d9a47fc419b7d42e31498cc2d803560984f00810 100644
--- a/chrome/browser/process_singleton_win.cc
+++ b/chrome/browser/process_singleton_win.cc
@@ -13,14 +13,17 @@
#include "base/command_line.h"
#include "base/debug/activity_tracker.h"
#include "base/files/file_path.h"
+#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/process/process.h"
#include "base/process/process_info.h"
+#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
+#include "base/timer/timer.h"
#include "base/trace_event/base_tracing.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
@@ -45,6 +48,13 @@
namespace {
const char kLockfile[] = "lockfile";
+const DWORD kPipeTimeout = 10000;
+const DWORD kMaxMessageLength = 32 * 1024;
+
+std::unique_ptr<std::vector<const uint8_t>> g_ack_data;
+base::OneShotTimer g_ack_timer;
+HANDLE g_write_ack_pipe;
+bool g_write_ack_callback_called = false;
// A helper class that acquires the given |mutex| while the AutoLockMutex is in
// scope.
@@ -80,10 +90,12 @@ BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
bool ParseCommandLine(const COPYDATASTRUCT* cds,
base::CommandLine* parsed_command_line,
- base::FilePath* current_directory) {
+ base::FilePath* current_directory,
+ std::vector<const uint8_t>* parsed_additional_data) {
// We should have enough room for the shortest command (min_message_size)
// and also be a multiple of wchar_t bytes. The shortest command
- // possible is L"START\0\0" (empty current directory and command line).
+ // possible is L"START\0\0" (empty command line, current directory,
+ // and additional data).
static const int min_message_size = 7;
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
cds->cbData % sizeof(wchar_t) != 0) {
@@ -133,11 +145,82 @@ bool ParseCommandLine(const COPYDATASTRUCT* cds,
const std::wstring cmd_line =
msg.substr(second_null + 1, third_null - second_null);
*parsed_command_line = base::CommandLine::FromString(cmd_line);
+
+ const std::wstring::size_type fourth_null =
+ msg.find_first_of(L'\0', third_null + 1);
+ if (fourth_null == std::wstring::npos ||
+ fourth_null == msg.length()) {
+ // No additional data was provided.
+ return true;
+ }
+
+ // Get length of the additional data.
+ const std::wstring additional_data_length_string =
+ msg.substr(third_null + 1, fourth_null - third_null);
+ size_t additional_data_length;
+ base::StringToSizeT(additional_data_length_string, &additional_data_length);
+
+ const std::wstring::size_type fifth_null =
+ msg.find_first_of(L'\0', fourth_null + 1);
+ if (fifth_null == std::wstring::npos ||
+ fifth_null == msg.length()) {
+ LOG(WARNING) << "Invalid format for start command, we need a string in 6 "
+ "parts separated by NULLs";
+ }
+
+ // Get the actual additional data.
+ const std::wstring additional_data =
+ msg.substr(fourth_null + 1, fifth_null - fourth_null);
+ const uint8_t* additional_data_bytes =
+ reinterpret_cast<const uint8_t*>(additional_data.c_str());
+ *parsed_additional_data = std::vector<const uint8_t>(additional_data_bytes,
+ additional_data_bytes + additional_data_length);
+
return true;
}
return false;
}
+void StoreAck(const base::span<const uint8_t>* ack_data) {
+ if (ack_data) {
+ g_ack_data = std::make_unique<std::vector<const uint8_t>>(ack_data->begin(),
+ ack_data->end());
+ } else {
+ g_ack_data = nullptr;
+ }
+}
+
+void SendBackAck() {
+ // This is the first instance sending the ack back to the second instance.
+ if (!g_write_ack_callback_called) {
+ g_write_ack_callback_called = true;
+ const uint8_t* data_buffer = nullptr;
+ DWORD data_to_send_size = 0;
+ if (g_ack_data) {
+ data_buffer = g_ack_data->data();
+ DWORD ack_data_size = g_ack_data->size() * sizeof(uint8_t);
+ data_to_send_size = (ack_data_size < kMaxMessageLength) ? ack_data_size : kMaxMessageLength;
+ }
+
+ ::ConnectNamedPipe(g_write_ack_pipe, NULL);
+
+ DWORD bytes_written = 0;
+ ::WriteFile(g_write_ack_pipe,
+ (LPCVOID)data_buffer,
+ data_to_send_size,
+ &bytes_written,
+ NULL);
+ DCHECK(bytes_written == data_to_send_size);
+
+ ::FlushFileBuffers(g_write_ack_pipe);
+ ::DisconnectNamedPipe(g_write_ack_pipe);
+
+ if (g_ack_data) {
+ g_ack_data.reset();
+ }
+ }
+}
+
bool ProcessLaunchNotification(
const ProcessSingleton::NotificationCallback& notification_callback,
UINT message,
@@ -151,16 +234,35 @@ bool ProcessLaunchNotification(
// Handle the WM_COPYDATA message from another process.
const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
-
base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
base::FilePath current_directory;
- if (!ParseCommandLine(cds, &parsed_command_line, &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;
+ // notification_callback.Run waits for StoreAck to
+ // run to completion before moving onwards.
+ // Therefore, we cannot directly send the SendBackAck
+ // callback instead, as it would hang the program
+ // during the ConnectNamedPipe call.
+ g_write_ack_callback_called = false;
+ *result = notification_callback.Run(parsed_command_line, current_directory,
+ std::move(additional_data),
+ base::BindRepeating(&StoreAck))
+ ? TRUE
+ : FALSE;
+ if (*result) {
+ // If *result is TRUE, we return NOTIFY_SUCCESS.
+ // Only for that case does the second process read
+ // the acknowledgement. Therefore, only send back
+ // the acknowledgement if *result is TRUE,
+ // otherwise the program hangs during the ConnectNamedPipe call.
+ g_ack_timer.Start(FROM_HERE, base::Seconds(0),
+ base::BindOnce(&SendBackAck));
+ }
return true;
}
@@ -254,9 +356,13 @@ bool ProcessSingleton::EscapeVirtualization(
ProcessSingleton::ProcessSingleton(
const std::string& program_name,
const base::FilePath& user_data_dir,
+ const base::span<const uint8_t> additional_data,
bool is_app_sandboxed,
- const NotificationCallback& notification_callback)
+ const NotificationCallback& notification_callback,
+ const NotificationAckCallback& notification_ack_callback)
: notification_callback_(notification_callback),
+ notification_ack_callback_(notification_ack_callback),
+ additional_data_(additional_data),
program_name_(program_name),
is_app_sandboxed_(is_app_sandboxed),
is_virtualized_(false),
@@ -271,6 +377,47 @@ ProcessSingleton::~ProcessSingleton() {
::CloseHandle(lock_file_);
}
+void ReadAck(const ProcessSingleton::NotificationAckCallback& ack_callback,
+ const std::string program_name,
+ base::FilePath& user_data_dir) {
+ // We are reading the ack from the first instance.
+ // First, wait for the pipe.
+ HWND remote_window = chrome::FindRunningChromeWindow(user_data_dir);
+ DWORD process_id;
+ DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
+ std::string identifier = base::NumberToString(process_id) +
+ base::NumberToString(thread_id);
+ std::wstring pipe_name = base::UTF8ToWide("\\\\.\\pipe\\" + identifier +
+ program_name);
+ const LPCWSTR w_pipe_name = pipe_name.c_str();
+ ::WaitNamedPipe(w_pipe_name, NMPWAIT_USE_DEFAULT_WAIT);
+
+ HANDLE read_ack_pipe = ::CreateFile(w_pipe_name,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ CHECK(read_ack_pipe != INVALID_HANDLE_VALUE);
+
+ DWORD bytes_read;
+ uint8_t read_ack_buffer[kMaxMessageLength];
+ ::ReadFile(read_ack_pipe,
+ (LPVOID)read_ack_buffer,
+ kMaxMessageLength,
+ &bytes_read,
+ NULL);
+
+ if (!bytes_read) {
+ ack_callback.Run(nullptr);
+ } else {
+ base::span<const uint8_t> out_span(read_ack_buffer, read_ack_buffer + bytes_read);
+ ack_callback.Run(&out_span);
+ }
+ ::CloseHandle(read_ack_pipe);
+}
+
// Code roughly based on Mozilla.
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
TRACE_EVENT0("startup", "ProcessSingleton::NotifyOtherProcess");
@@ -283,8 +430,9 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
return PROCESS_NONE;
}
- switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
+ switch (chrome::AttemptToNotifyRunningChrome(remote_window_, additional_data_)) {
case chrome::NOTIFY_SUCCESS:
+ ReadAck(notification_ack_callback_, program_name_, user_data_dir_);
return PROCESS_NOTIFIED;
case chrome::NOTIFY_FAILED:
remote_window_ = NULL;
@@ -422,6 +570,26 @@ bool ProcessSingleton::Create() {
<< "Lock file can not be created! Error code: " << error;
if (lock_file_ != INVALID_HANDLE_VALUE) {
+ // We are the first instance. Create a pipe to send out ack data.
+ // Create a per-process pipename using a combination of the
+ // username, process id, thread id, and program name. Pipe names max
+ // at 256 characters, can include any character other than a backslash
+ std::string identifier = base::NumberToString(::GetCurrentProcessId()) +
+ base::NumberToString(::GetCurrentThreadId());
+ std::wstring pipe_name = base::UTF8ToWide("\\\\.\\pipe\\" + identifier +
+ program_name_);
+ const LPCWSTR w_pipe_name = pipe_name.c_str();
+ ack_pipe_ = ::CreateNamedPipe(w_pipe_name,
+ PIPE_ACCESS_OUTBOUND,
+ PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS,
+ PIPE_UNLIMITED_INSTANCES,
+ kMaxMessageLength,
+ 0,
+ kPipeTimeout,
+ NULL);
+ CHECK(ack_pipe_ != INVALID_HANDLE_VALUE);
+ g_write_ack_pipe = ack_pipe_;
+
// Set the window's title to the path of our user data directory so
// other Chrome instances can decide if they should forward to us.
TRACE_EVENT0("startup", "ProcessSingleton::Create:CreateWindow");
@@ -449,6 +617,7 @@ bool ProcessSingleton::Create() {
}
void ProcessSingleton::Cleanup() {
+ ::CloseHandle(ack_pipe_);
}
void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
index b64ed1d155a30582e48c9cdffcee9d0f25a53a6a..ce851d09d501ebcc6d6c4065e746e869d5275b2b 100644
--- a/chrome/browser/win/chrome_process_finder.cc
+++ b/chrome/browser/win/chrome_process_finder.cc
@@ -36,9 +36,10 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
return base::win::MessageWindow::FindWindow(user_data_dir.value());
}
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
+NotifyChromeResult AttemptToNotifyRunningChrome(
+ HWND remote_window,
+ const base::span<const uint8_t> additional_data) {
TRACE_EVENT0("startup", "AttemptToNotifyRunningChrome");
-
DCHECK(remote_window);
DWORD process_id = 0;
DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
@@ -50,7 +51,8 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
}
// Send the command line to the remote chrome window.
- // Format is "START\0<<<current directory>>>\0<<<commandline>>>".
+ // Format is
+ // "START\0<current-directory>\0<command-line>\0<additional-data-length>\0<additional-data>".
std::wstring to_send(L"START\0", 6); // want the NULL in the string.
base::FilePath cur_dir;
if (!base::GetCurrentDirectory(&cur_dir)) {
@@ -64,6 +66,22 @@ NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window) {
base::CommandLine::ForCurrentProcess()->GetCommandLineString());
to_send.append(L"\0", 1); // Null separator.
+ size_t additional_data_size = additional_data.size_bytes();
+ if (additional_data_size) {
+ // Send over the size, because the reinterpret cast to wchar_t could
+ // add padding.
+ to_send.append(base::UTF8ToWide(base::NumberToString(additional_data_size)));
+ to_send.append(L"\0", 1); // Null separator.
+
+ size_t padded_size = additional_data_size / sizeof(wchar_t);
+ if (additional_data_size % sizeof(wchar_t) != 0) {
+ padded_size++;
+ }
+ to_send.append(reinterpret_cast<const wchar_t*>(additional_data.data()),
+ padded_size);
+ to_send.append(L"\0", 1); // Null separator.
+ }
+
// Allow the current running browser window to make itself the foreground
// window (otherwise it will just flash in the taskbar).
::AllowSetForegroundWindow(process_id);
diff --git a/chrome/browser/win/chrome_process_finder.h b/chrome/browser/win/chrome_process_finder.h
index 5516673cee019f6060077091e59498bf9038cd6e..8edea5079b46c2cba67833114eb9c21d85cfc22d 100644
--- a/chrome/browser/win/chrome_process_finder.h
+++ b/chrome/browser/win/chrome_process_finder.h
@@ -7,6 +7,7 @@
#include <windows.h>
+#include "base/containers/span.h"
#include "base/time/time.h"
namespace base {
@@ -27,7 +28,9 @@ HWND FindRunningChromeWindow(const base::FilePath& user_data_dir);
// Attempts to send the current command line to an already running instance of
// Chrome via a WM_COPYDATA message.
// Returns true if a running Chrome is found and successfully notified.
-NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window);
+NotifyChromeResult AttemptToNotifyRunningChrome(
+ HWND remote_window,
+ const base::span<const uint8_t> additional_data);
// Changes the notification timeout to |new_timeout|, returns the old timeout.
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout);

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 9b2c91d39324b61afa49ccea6be2eda8308473ff..1652b52c5c752809348b3ab44d3703ac343c829d 100644
index 9b2c91d39324b61afa49ccea6be2eda8308473ff..6817a9cd22ebb8adba2118a1bd8a32cfc065e8ea 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller.cc
@@ -382,13 +382,9 @@ void FullscreenController::EnterFullscreenModeInternal(
@@ -49,3 +51,19 @@ index 9b2c91d39324b61afa49ccea6be2eda8308473ff..1652b52c5c752809348b3ab44d3703ac
if (option == BROWSER)
base::RecordAction(base::UserMetricsAction("ToggleFullscreen"));
@@ -439,12 +437,12 @@ void FullscreenController::ExitFullscreenModeInternal() {
RecordExitingUMA();
toggled_into_fullscreen_ = false;
-#if BUILDFLAG(IS_MAC)
- // Mac windows report a state change instantly, and so we must also clear
+
+ // Electron native windows report a state change instantly, and so we must also clear
// state_prior_to_tab_fullscreen_ to match them else other logic using
// state_prior_to_tab_fullscreen_ will be incorrect.
NotifyTabExclusiveAccessLost();
-#endif
+
exclusive_access_manager()->context()->ExitFullscreen();
extension_caused_fullscreen_ = GURL();

View File

@@ -0,0 +1,119 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lei Zhang <thestig@chromium.org>
Date: Thu, 23 Jun 2022 17:54:12 +0000
Subject: Fix Mac build with enable_plugins=false.
Remove spurious includes of plugin headers, and add appropriate #ifs.
Bug: 1027360
Change-Id: I445252f5de14dff8e89ab371429a24ad3e57ca97
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3719213
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Reviewed-by: Thomas Lukaszewicz <tluk@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1017248}
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 0c5bfe154c4df19d69a91b5a54ba9e3700aa1a5a..d556360a52f4a1edf0d5f5d4558b6661590a101d 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -109,7 +109,6 @@
#include "components/prefs/pref_service.h"
#include "components/sessions/core/tab_restore_service.h"
#include "content/public/browser/download_manager.h"
-#include "content/public/browser/plugin_service.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "net/base/filename_util.h"
diff --git a/content/browser/sandbox_mac_unittest.mm b/content/browser/sandbox_mac_unittest.mm
index a69810d9eb36ef940fb9a234d5f6610c4709516c..e7e40981b5ab965d79708e8de358d172dbe77378 100644
--- a/content/browser/sandbox_mac_unittest.mm
+++ b/content/browser/sandbox_mac_unittest.mm
@@ -28,6 +28,7 @@
#include "content/browser/sandbox_parameters_mac.h"
#include "content/common/mac/font_loader.h"
#include "crypto/openssl_util.h"
+#include "ppapi/buildflags/buildflags.h"
#include "sandbox/mac/seatbelt.h"
#include "sandbox/mac/seatbelt_exec.h"
#include "sandbox/policy/mac/sandbox_mac.h"
@@ -91,7 +92,9 @@ void ExecuteInAllSandboxTypes(const std::string& multiprocess_main,
sandbox::mojom::Sandbox::kCdm,
sandbox::mojom::Sandbox::kGpu,
sandbox::mojom::Sandbox::kNaClLoader,
+#if BUILDFLAG(ENABLE_PLUGINS)
sandbox::mojom::Sandbox::kPpapi,
+#endif
sandbox::mojom::Sandbox::kPrintBackend,
sandbox::mojom::Sandbox::kPrintCompositor,
sandbox::mojom::Sandbox::kRenderer,
diff --git a/content/browser/sandbox_parameters_mac.mm b/content/browser/sandbox_parameters_mac.mm
index 76a22420f605994ebe0a7ffa9a7f2a0535c2440f..cf8b251180177c3ea4f8ddb2450f0483d201ba2b 100644
--- a/content/browser/sandbox_parameters_mac.mm
+++ b/content/browser/sandbox_parameters_mac.mm
@@ -20,7 +20,6 @@
#include "base/strings/sys_string_conversions.h"
#include "base/system/sys_info.h"
#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/plugin_service.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
@@ -34,6 +33,7 @@
#include "third_party/abseil-cpp/absl/types/optional.h"
#if BUILDFLAG(ENABLE_PLUGINS)
+#include "content/public/browser/plugin_service.h"
#include "content/public/common/pepper_plugin_info.h"
#endif
@@ -229,11 +229,11 @@ void SetupSandboxParameters(sandbox::mojom::Sandbox sandbox_type,
case sandbox::mojom::Sandbox::kNetwork:
SetupNetworkSandboxParameters(client);
break;
- case sandbox::mojom::Sandbox::kPpapi:
#if BUILDFLAG(ENABLE_PLUGINS)
+ case sandbox::mojom::Sandbox::kPpapi:
SetupPPAPISandboxParameters(client);
-#endif
break;
+#endif
case sandbox::mojom::Sandbox::kNoSandbox:
CHECK(false) << "Unhandled parameters for sandbox_type "
<< static_cast<int>(sandbox_type);
diff --git a/sandbox/policy/mac/sandbox_mac.mm b/sandbox/policy/mac/sandbox_mac.mm
index 34f8b003c96b9fff00b666ae184b5076e5d6fd45..11392b99ecad98f2cf39e67d545fe1947ede1593 100644
--- a/sandbox/policy/mac/sandbox_mac.mm
+++ b/sandbox/policy/mac/sandbox_mac.mm
@@ -12,6 +12,7 @@
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
+#include "ppapi/buildflags/buildflags.h"
#include "printing/buildflags/buildflags.h"
#include "sandbox/policy/mac/audio.sb.h"
#include "sandbox/policy/mac/cdm.sb.h"
@@ -69,9 +70,11 @@
case sandbox::mojom::Sandbox::kNetwork:
profile += kSeatbeltPolicyString_network;
break;
+#if BUILDFLAG(ENABLE_PLUGINS)
case sandbox::mojom::Sandbox::kPpapi:
profile += kSeatbeltPolicyString_ppapi;
break;
+#endif
#if BUILDFLAG(ENABLE_PRINTING)
case sandbox::mojom::Sandbox::kPrintBackend:
profile += kSeatbeltPolicyString_print_backend;
diff --git a/ui/views_content_client/views_content_client_main_parts_mac.mm b/ui/views_content_client/views_content_client_main_parts_mac.mm
index 044600ad464fc48a805bf3e47811808983d43fef..df23f797e4f0898d9dc7742e05d286f540bfdacb 100644
--- a/ui/views_content_client/views_content_client_main_parts_mac.mm
+++ b/ui/views_content_client/views_content_client_main_parts_mac.mm
@@ -12,7 +12,6 @@
#include "base/mac/scoped_nsobject.h"
#include "base/path_service.h"
#include "content/public/browser/context_factory.h"
-#include "content/public/browser/plugin_service.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/result_codes.h"
#include "content/shell/browser/shell_application_mac.h"

View File

@@ -0,0 +1,108 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lei Zhang <thestig@chromium.org>
Date: Thu, 23 Jun 2022 18:52:27 +0000
Subject: Fix Windows build with enable_plugins=false.
Add some #if checks to Windows-only code so plugins enums are only
referenced when plugins are enabled. Also only build
plugin_list_unittest.cc when plugins are enabled.
Bug: 1027360
Change-Id: I0d265ae711e5e3401076dc89d1d49329f423ca64
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3719402
Reviewed-by: Alex Gough <ajgo@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1017281}
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 2d3d64f06e3301b42c78e5987b09c5216573900d..b488d9d4f57f7fd05f0283a999911ee35f3aaab6 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -3928,7 +3928,9 @@ std::wstring ChromeContentBrowserClient::GetAppContainerSidForSandboxType(
return std::wstring();
case sandbox::mojom::Sandbox::kGpu:
return std::wstring();
+#if BUILDFLAG(ENABLE_PLUGINS)
case sandbox::mojom::Sandbox::kPpapi:
+#endif
case sandbox::mojom::Sandbox::kNoSandbox:
case sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges:
case sandbox::mojom::Sandbox::kXrCompositing:
@@ -4000,7 +4002,9 @@ bool ChromeContentBrowserClient::PreSpawnChild(
break;
case sandbox::mojom::Sandbox::kUtility:
case sandbox::mojom::Sandbox::kGpu:
+#if BUILDFLAG(ENABLE_PLUGINS)
case sandbox::mojom::Sandbox::kPpapi:
+#endif
case sandbox::mojom::Sandbox::kNoSandbox:
case sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges:
case sandbox::mojom::Sandbox::kXrCompositing:
diff --git a/chrome/child/pdf_child_init.cc b/chrome/child/pdf_child_init.cc
index e53cfc60b41fe0d4eeb0362e9923f1e39e61a64d..8e0d72623bbb0c92c8fd8b97ab833c947806667b 100644
--- a/chrome/child/pdf_child_init.cc
+++ b/chrome/child/pdf_child_init.cc
@@ -14,6 +14,7 @@
#include "base/win/windows_version.h"
#include "content/public/child/child_thread.h"
#include "content/public/common/content_switches.h"
+#include "ppapi/buildflags/buildflags.h"
#include "sandbox/policy/mojom/sandbox.mojom.h"
#include "sandbox/policy/sandbox_type.h"
#include "sandbox/policy/switches.h"
@@ -57,7 +58,9 @@ void MaybePatchGdiGetFontData() {
auto service_sandbox_type =
sandbox::policy::SandboxTypeFromCommandLine(command_line);
bool need_gdi =
+#if BUILDFLAG(ENABLE_PLUGINS)
service_sandbox_type == sandbox::mojom::Sandbox::kPpapi ||
+#endif
service_sandbox_type == sandbox::mojom::Sandbox::kPrintCompositor ||
service_sandbox_type == sandbox::mojom::Sandbox::kPdfConversion ||
(service_sandbox_type == sandbox::mojom::Sandbox::kRenderer &&
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 27b5eb50bfcc042b00934021bc654cb9688b88b4..4593ba59f70fc7a65bf4bd14d1ba27953d804f3b 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2134,7 +2134,6 @@ test("content_unittests") {
"../browser/payments/payment_manager_unittest.cc",
"../browser/permissions/permission_controller_impl_unittest.cc",
"../browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc",
- "../browser/plugin_list_unittest.cc",
"../browser/ppapi_plugin_sandboxed_process_launcher_delegate_unittest.cc",
"../browser/prerender/prerender_host_registry_unittest.cc",
"../browser/prerender/prerender_host_unittest.cc",
@@ -2832,8 +2831,8 @@ test("content_unittests") {
deps += [ "//ui/events:test_support" ]
}
- if (!is_win && !is_mac) {
- sources -= [ "../browser/plugin_list_unittest.cc" ]
+ if (enable_plugins && (is_win || is_mac)) {
+ sources += [ "../browser/plugin_list_unittest.cc" ]
}
if (use_ozone) {
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index 197e66eeb5b3bd91efd5817a09d2ddbb29c34c25..3ac3ae04ee1d17bcfab1ad0e4fc258e6f12f951a 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -40,6 +40,7 @@
#include "base/win/sid.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
+#include "ppapi/buildflags/buildflags.h"
#include "printing/buildflags/buildflags.h"
#include "sandbox/policy/features.h"
#include "sandbox/policy/mojom/sandbox.mojom.h"
@@ -1244,8 +1245,10 @@ std::string SandboxWin::GetSandboxTypeInEnglish(Sandbox sandbox_type) {
return "Utility";
case Sandbox::kGpu:
return "GPU";
+#if BUILDFLAG(ENABLE_PLUGINS)
case Sandbox::kPpapi:
return "PPAPI";
+#endif
case Sandbox::kNetwork:
return "Network";
case Sandbox::kCdm:

View File

@@ -0,0 +1,203 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Min Qin <qinmin@chromium.org>
Date: Tue, 28 Jun 2022 16:27:43 +0000
Subject: passing update_first_party_url_on_redirect=false for fetch
Background fetch doesn't work like regular download as it is not
considered a top frame navigation. This CL let background fetch to
pass update_first_party_url_on_redirect=false to DownloadURLParameters,
and handle it properly w.r.t samesite cookies.
BUG=1268580
(cherry picked from commit bf1e93c6af21dad12088b615feda07a90a85c158)
Change-Id: I3a1cc33be8578d5d8c796dbbb21fa35a47bdda36
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3712307
Reviewed-by: Rayan Kanso <rayankans@chromium.org>
Commit-Queue: Min Qin <qinmin@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1016316}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3727786
Cr-Commit-Position: refs/branch-heads/5112@{#397}
Cr-Branched-From: b13d3fe7b3c47a56354ef54b221008afa754412e-refs/heads/main@{#1012729}
diff --git a/components/background_fetch/background_fetch_delegate_base.cc b/components/background_fetch/background_fetch_delegate_base.cc
index 98d8977f1b18dd4d6fee6acbc2244063cb64fa24..1106781258375606ddc61ad9798eb90df9014c88 100644
--- a/components/background_fetch/background_fetch_delegate_base.cc
+++ b/components/background_fetch/background_fetch_delegate_base.cc
@@ -102,6 +102,7 @@ void BackgroundFetchDelegateBase::DownloadUrl(
weak_ptr_factory_.GetWeakPtr());
params.traffic_annotation =
net::MutableNetworkTrafficAnnotationTag(traffic_annotation);
+ params.request_params.update_first_party_url_on_redirect = false;
JobDetails* job_details = GetJobDetails(job_id);
if (job_details->job_state == JobDetails::State::kPendingWillStartPaused ||
diff --git a/components/download/content/internal/download_driver_impl.cc b/components/download/content/internal/download_driver_impl.cc
index d3c15b4852e80aeca68469715935af20f91ace96..ccff2b7ec99d214ddb9161059daf65d7b0000674 100644
--- a/components/download/content/internal/download_driver_impl.cc
+++ b/components/download/content/internal/download_driver_impl.cc
@@ -227,6 +227,8 @@ void DownloadDriverImpl::Start(
download_url_params->set_isolation_info(
request_params.isolation_info.value());
}
+ download_url_params->set_update_first_party_url_on_redirect(
+ request_params.update_first_party_url_on_redirect);
download_manager_coordinator_->DownloadUrl(std::move(download_url_params));
}
diff --git a/components/download/internal/common/download_utils.cc b/components/download/internal/common/download_utils.cc
index e5eb9aa02069682cc3bdba6125f604a885faca9b..1b02ed5599398f86f94df438767b6a7b45c36d47 100644
--- a/components/download/internal/common/download_utils.cc
+++ b/components/download/internal/common/download_utils.cc
@@ -349,8 +349,10 @@ std::unique_ptr<network::ResourceRequest> CreateResourceRequest(
// cross-site URL has been visited before.
url::Origin origin = url::Origin::Create(params->url());
request->trusted_params->isolation_info = net::IsolationInfo::Create(
- net::IsolationInfo::RequestType::kMainFrame, origin, origin,
- net::SiteForCookies::FromOrigin(origin));
+ params->update_first_party_url_on_redirect()
+ ? net::IsolationInfo::RequestType::kMainFrame
+ : net::IsolationInfo::RequestType::kOther,
+ origin, origin, net::SiteForCookies::FromOrigin(origin));
request->site_for_cookies = net::SiteForCookies::FromUrl(params->url());
}
@@ -358,7 +360,8 @@ std::unique_ptr<network::ResourceRequest> CreateResourceRequest(
request->referrer = params->referrer();
request->referrer_policy = params->referrer_policy();
request->is_main_frame = true;
- request->update_first_party_url_on_redirect = true;
+ request->update_first_party_url_on_redirect =
+ params->update_first_party_url_on_redirect();
// Downloads should be treated as navigations from Fetch spec perspective.
// See also:
diff --git a/components/download/public/background_service/download_params.h b/components/download/public/background_service/download_params.h
index e567fcc8aea307d73ec64fea47e0ac3733256340..ce6070e67bbfcf8164f64bd79e14fd4b1a6425b5 100644
--- a/components/download/public/background_service/download_params.h
+++ b/components/download/public/background_service/download_params.h
@@ -120,6 +120,12 @@ struct RequestParams {
// The isolation info of the request, this won't be persisted to db and will
// be invalidate during download resumption in new browser session.
absl::optional<net::IsolationInfo> isolation_info;
+
+ // First-party URL redirect policy: During server redirects, whether the
+ // first-party URL for cookies will need to be changed. Download is normally
+ // considered a main frame navigation. However, this is not true for
+ // background fetch.
+ bool update_first_party_url_on_redirect = true;
};
// The parameters that describe a download request made to the DownloadService.
diff --git a/components/download/public/common/download_url_parameters.cc b/components/download/public/common/download_url_parameters.cc
index 3dec7148d86cd3892670df8b95dcb78d49616e89..25ea6f13e0df9f23364b75c48b870d8ea6a53e92 100644
--- a/components/download/public/common/download_url_parameters.cc
+++ b/components/download/public/common/download_url_parameters.cc
@@ -34,7 +34,8 @@ DownloadUrlParameters::DownloadUrlParameters(
traffic_annotation_(traffic_annotation),
download_source_(DownloadSource::UNKNOWN),
require_safety_checks_(true),
- has_user_gesture_(false) {}
+ has_user_gesture_(false),
+ update_first_party_url_on_redirect_(true) {}
DownloadUrlParameters::~DownloadUrlParameters() = default;
diff --git a/components/download/public/common/download_url_parameters.h b/components/download/public/common/download_url_parameters.h
index ba0a03cb035a79651967ac2882f89f577bd0c012..61eb9af8a00c3e4adf700178994a9e6e864bcf0c 100644
--- a/components/download/public/common/download_url_parameters.h
+++ b/components/download/public/common/download_url_parameters.h
@@ -279,6 +279,11 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadUrlParameters {
has_user_gesture_ = has_user_gesture;
}
+ void set_update_first_party_url_on_redirect(
+ bool update_first_party_url_on_redirect) {
+ update_first_party_url_on_redirect_ = update_first_party_url_on_redirect;
+ }
+
OnStartedCallback& callback() { return callback_; }
bool content_initiated() const { return content_initiated_; }
const std::string& last_modified() const { return last_modified_; }
@@ -335,6 +340,9 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadUrlParameters {
return isolation_info_;
}
bool has_user_gesture() const { return has_user_gesture_; }
+ bool update_first_party_url_on_redirect() const {
+ return update_first_party_url_on_redirect_;
+ }
// STATE CHANGING: All save_info_ sub-objects will be in an indeterminate
// state following this call.
@@ -383,6 +391,7 @@ class COMPONENTS_DOWNLOAD_EXPORT DownloadUrlParameters {
bool require_safety_checks_;
absl::optional<net::IsolationInfo> isolation_info_;
bool has_user_gesture_;
+ bool update_first_party_url_on_redirect_;
};
} // namespace download
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index d6adfeb5aff596089ae3823248ff50c84ba94ad2..73cf82580555d620cfb7a3ba3aad3379d38e4608 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -3751,6 +3751,58 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, UpdateSiteForCookies) {
site_a.GetURL("a.test", "/")));
}
+// Tests that if `update_first_party_url_on_redirect` is set to false, download
+// will not behave like a top-level frame navigation and SameSite=Strict cookies
+// will not be set on a redirection.
+IN_PROC_BROWSER_TEST_F(
+ DownloadContentTest,
+ SiteForCookies_DownloadUrl_NotUpdateFirstPartyUrlOnRedirect) {
+ net::EmbeddedTestServer site_a;
+ net::EmbeddedTestServer site_b;
+
+ base::StringPairs cookie_headers;
+ cookie_headers.push_back(std::make_pair(
+ std::string("Set-Cookie"), std::string("A=strict; SameSite=Strict")));
+ cookie_headers.push_back(std::make_pair(std::string("Set-Cookie"),
+ std::string("B=lax; SameSite=Lax")));
+
+ // This will request a URL on b.test, which redirects to a url that sets the
+ // cookies on a.test.
+ site_a.RegisterRequestHandler(CreateBasicResponseHandler(
+ "/sets-samesite-cookies", net::HTTP_OK, cookie_headers,
+ "application/octet-stream", "abcd"));
+ ASSERT_TRUE(site_a.Start());
+ site_b.RegisterRequestHandler(
+ CreateRedirectHandler("/redirected-download",
+ site_a.GetURL("a.test", "/sets-samesite-cookies")));
+ ASSERT_TRUE(site_b.Start());
+
+ // Download the file.
+ SetupEnsureNoPendingDownloads();
+ std::unique_ptr<download::DownloadUrlParameters> download_parameters(
+ DownloadRequestUtils::CreateDownloadForWebContentsMainFrame(
+ shell()->web_contents(),
+ site_b.GetURL("b.test", "/redirected-download"),
+ TRAFFIC_ANNOTATION_FOR_TESTS));
+ download_parameters->set_update_first_party_url_on_redirect(false);
+ std::unique_ptr<DownloadTestObserver> observer(CreateWaiter(shell(), 1));
+ DownloadManagerForShell(shell())->DownloadUrl(std::move(download_parameters));
+ observer->WaitForFinished();
+
+ // Get the important info from other threads and check it.
+ EXPECT_TRUE(EnsureNoPendingDownloads());
+
+ std::vector<download::DownloadItem*> downloads;
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+ ASSERT_EQ(1u, downloads.size());
+ ASSERT_EQ(download::DownloadItem::COMPLETE, downloads[0]->GetState());
+
+ // Check that the cookies were not set on a.test.
+ EXPECT_EQ("",
+ content::GetCookies(shell()->web_contents()->GetBrowserContext(),
+ site_a.GetURL("a.test", "/")));
+}
+
// Verifies that isolation info set in DownloadUrlParameters can be populated.
IN_PROC_BROWSER_TEST_F(DownloadContentTest,
SiteForCookies_DownloadUrl_IsolationInfoPopulated) {

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 7104a0d5f4489c687f3cb9e63bc7cbef59d2fa62..f0b9ed834a3f7310da09377f0b2105cf635ffeb7 100644
index 7104a0d5f4489c687f3cb9e63bc7cbef59d2fa62..7449c0b7bdb0cac7340cb73292e70e6aa0336657 100644
--- a/ui/gtk/gtk_compat.cc
+++ b/ui/gtk/gtk_compat.cc
@@ -86,12 +86,6 @@ void* GetLibGtk4(bool check = true) {
@@ -66,11 +66,6 @@ void* GetLibGio() {
return libgio;
}
-void* GetLibGdkPixbuf() {
- static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
- return libgdk_pixbuf;
-}
-
void* GetLibGdk3() {
static void* libgdk3 = DlOpen("libgdk-3.so.0");
return libgdk3;
@@ -86,12 +81,6 @@ void* GetLibGtk4(bool check = true) {
return libgtk4;
}
@@ -23,10 +35,15 @@ index 7104a0d5f4489c687f3cb9e63bc7cbef59d2fa62..f0b9ed834a3f7310da09377f0b2105cf
bool LoadGtk3() {
if (!GetLibGtk3(false))
return false;
@@ -133,6 +127,12 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
@@ -133,6 +122,17 @@ gfx::Insets InsetsFromGtkBorder(const GtkBorder& border) {
} // namespace
+void* GetLibGdkPixbuf() {
+ static void* libgdk_pixbuf = DlOpen("libgdk_pixbuf-2.0.so.0");
+ return libgdk_pixbuf;
+}
+
+void* GetLibGtk() {
+ if (GtkCheckVersion(4))
+ return GetLibGtk4();
@@ -37,13 +54,16 @@ index 7104a0d5f4489c687f3cb9e63bc7cbef59d2fa62..f0b9ed834a3f7310da09377f0b2105cf
static bool loaded = LoadGtkImpl();
return loaded;
diff --git a/ui/gtk/gtk_compat.h b/ui/gtk/gtk_compat.h
index 72981270fe26579211afcaf3c596a412f69f5fac..b5dbfde5b011d57d26960d245e0dc61cac9341e4 100644
index 72981270fe26579211afcaf3c596a412f69f5fac..2ead29619e9f69f50699970546bf048686ca643f 100644
--- a/ui/gtk/gtk_compat.h
+++ b/ui/gtk/gtk_compat.h
@@ -37,6 +37,9 @@ using SkColor = uint32_t;
@@ -37,6 +37,12 @@ using SkColor = uint32_t;
namespace gtk {
+// Get handle to the currently loaded gdk_pixbuf library in the process.
+void* GetLibGdkPixbuf();
+
+// Get handle to the currently loaded gtk library in the process.
+void* GetLibGtk();
+

View File

@@ -0,0 +1,276 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aaron Leventhal <aleventhal@google.com>
Date: Tue, 26 Jul 2022 17:36:04 +0000
Subject: Merge 104: Speculative fix for IsValidCodePointInIndex() range crash
(cherry picked from commit b4d1ce62d7783a9b31a0a2bec1b886a9a6519a40)
Bug: 1333970
Change-Id: I5a4c78e708357074fdec1f7a18fa928e39f9c51a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3757922
Auto-Submit: Aaron Leventhal <aleventhal@chromium.org>
Reviewed-by: Nektarios Paisios <nektar@chromium.org>
Commit-Queue: Aaron Leventhal <aleventhal@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1025405}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3788215
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/5112@{#1206}
Cr-Branched-From: b13d3fe7b3c47a56354ef54b221008afa754412e-refs/heads/main@{#1012729}
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 29ed811d67ad6eaf9fc6b7c22521bebc74038258..4d39bc31167d266aa5866b44b420fe743fddca8d 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -394,6 +394,10 @@ const ui::AXTreeData& BrowserAccessibilityManager::GetTreeData() const {
return ax_tree()->data();
}
+std::string BrowserAccessibilityManager::ToString() const {
+ return GetTreeData().ToString();
+}
+
void BrowserAccessibilityManager::OnWindowFocused() {
if (IsRootTree())
FireFocusEventsIfNeeded();
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index 92d62b5832d4fc87830b1c2bad7c7b972f1de74e..ca696fd855dcfe45e80e6c070546b19b19d42f69 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -225,6 +225,8 @@ class CONTENT_EXPORT BrowserAccessibilityManager
// Get the AXTreeData for this frame.
const ui::AXTreeData& GetTreeData() const;
+ std::string ToString() const override;
+
// Called to notify the accessibility manager that its associated native
// view got focused.
virtual void OnWindowFocused();
diff --git a/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc b/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc
index 30000a5a65b1d344fda047f6fd592cdcb6ee0e72..e8b1e2f08db121069bf7042fd8ad704c5d4b7a0d 100644
--- a/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc
+++ b/extensions/renderer/api/automation/automation_ax_tree_wrapper.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "automation_ax_tree_wrapper.h"
#include "base/containers/contains.h"
#include "base/containers/cxx20_erase.h"
#include "base/no_destructor.h"
@@ -545,4 +546,8 @@ ui::AXNode* AutomationAXTreeWrapper::GetParentNodeFromParentTreeAsAXNode()
return owner_->GetParent(tree_.root(), &wrapper);
}
+std::string AutomationAXTreeWrapper::ToString() const {
+ return "<AutomationAXTreeWrapper>";
+}
+
} // namespace extensions
diff --git a/extensions/renderer/api/automation/automation_ax_tree_wrapper.h b/extensions/renderer/api/automation/automation_ax_tree_wrapper.h
index bd3fae05ab1f70fb26888d0ec9a8ab29a17f3f3f..d370dcee0eb277925ccfb48049d5c0197acc225b 100644
--- a/extensions/renderer/api/automation/automation_ax_tree_wrapper.h
+++ b/extensions/renderer/api/automation/automation_ax_tree_wrapper.h
@@ -116,6 +116,7 @@ class AutomationAXTreeWrapper : public ui::AXTreeObserver,
ui::AXTreeID GetParentTreeID() const override;
ui::AXNode* GetRootAsAXNode() const override;
ui::AXNode* GetParentNodeFromParentTreeAsAXNode() const override;
+ std::string ToString() const override;
private:
// AXTreeObserver overrides.
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
index b0f06a0912447e5fa9bad9e62b2523817490ea02..3081258496dacc4f26976dfd99ea005a70bcd210 100644
--- a/ui/accessibility/ax_node_position_unittest.cc
+++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -9105,8 +9105,12 @@ TEST_F(AXPositionTest,
EXPECT_TRUE(test_position->IsNullPosition());
}
+// TODO(crbug.com/1333970) It is not legal to call
+// AsLeafTextPositionBeforeCharacter or AsLeafTextPositionAfterCharacter with
+// a text position using out-of-range offsets. It's necessary to call
+// AsValidPosition() first. Therefore, this test currently triggers a DCHECK.
TEST_F(AXPositionTest,
- AsLeafTextPositionBeforeAndAfterCharacterWithInvalidPosition) {
+ DISABLED_AsLeafTextPositionBeforeAndAfterCharacterWithInvalidPosition) {
AXNodeData root_data;
root_data.id = 1;
root_data.role = ax::mojom::Role::kRootWebArea;
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index 07fd92a174f08acad1db1853ed4bae57c0505990..280774a1844fd78407a4025b92adb6f7fae9fd21 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -25,6 +25,7 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/accessibility/ax_common.h"
#include "ui/accessibility/ax_enum_util.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node.h"
@@ -382,16 +383,33 @@ class AXPosition {
return str + " annotated_text=" + base::UTF16ToUTF8(annotated_text);
}
+ // Helper for logging the position, the AXTreeManager and the anchor node.
+ std::string ToDebugString() const {
+ if (IsNullPosition()) {
+ return "* Position: null";
+ }
+ DCHECK(GetAnchor());
+ DCHECK(GetManager());
+ std::ostringstream str;
+ str << "* Position: " << ToString()
+ << "\n* Manager: " << GetManager()->ToString()
+ << "\n* Anchor node: " << *GetAnchor();
+ return str.str();
+ }
+
AXPositionKind kind() const { return kind_; }
AXTreeID tree_id() const { return tree_id_; }
AXNodeID anchor_id() const { return anchor_id_; }
+ AXTreeManager* GetManager() const {
+ return AXTreeManagerMap::GetInstance().GetManager(tree_id());
+ }
+
AXNode* GetAnchor() const {
if (tree_id_ == AXTreeIDUnknown() || anchor_id_ == kInvalidAXNodeID)
return nullptr;
- const AXTreeManager* manager =
- AXTreeManagerMap::GetInstance().GetManager(tree_id());
+ const AXTreeManager* manager = GetManager();
if (manager)
return manager->GetNodeFromTree(anchor_id());
@@ -2568,13 +2586,6 @@ class AXPosition {
return Clone();
AXPositionInstance text_position = AsTextPosition();
- // The following situation should not be possible but there are existing
- // crashes in the field.
- //
- // TODO(nektar): Remove this workaround as soon as the source of the bug
- // is identified.
- if (text_position->text_offset_ > text_position->MaxTextOffset())
- return CreateNullPosition();
// In case the input affinity is upstream, reset it to downstream.
//
@@ -2592,6 +2603,16 @@ class AXPosition {
if (!text_position->IsIgnored() && !text_position->AtEndOfAnchor()) {
std::unique_ptr<base::i18n::BreakIterator> grapheme_iterator =
text_position->GetGraphemeIterator();
+ // The following situation should not be possible but there are existing
+ // crashes in the field.
+ //
+ // TODO(nektar): Remove this workaround as soon as the source of the bug
+ // is identified.
+ if (text_position->text_offset_ < 0 ||
+ text_position->text_offset_ > text_position->MaxTextOffset()) {
+ SANITIZER_NOTREACHED() << "Offset range error:\n" << ToDebugString();
+ return CreateNullPosition();
+ }
DCHECK_GE(text_position->text_offset_, 0);
DCHECK_LE(text_position->text_offset_, text_position->MaxTextOffset());
while (!text_position->AtStartOfAnchor() &&
@@ -2644,9 +2665,11 @@ class AXPosition {
//
// TODO(nektar): Remove this workaround as soon as the source of the bug
// is identified.
- if (text_position->text_offset_ > text_position->MaxTextOffset())
+ if (text_position->text_offset_ < 0 ||
+ text_position->text_offset_ > text_position->MaxTextOffset()) {
+ SANITIZER_NOTREACHED() << "Offset range error:\n" << ToDebugString();
return CreateNullPosition();
-
+ }
DCHECK_GE(text_position->text_offset_, 0);
DCHECK_LE(text_position->text_offset_, text_position->MaxTextOffset());
while (!text_position->AtEndOfAnchor() &&
diff --git a/ui/accessibility/ax_tree_manager.h b/ui/accessibility/ax_tree_manager.h
index 1081ed8fc3388ae7b6f535f2cd7437ac2e3db184..52f892c1acf3a50a24716bb361dd1ca231bd57a1 100644
--- a/ui/accessibility/ax_tree_manager.h
+++ b/ui/accessibility/ax_tree_manager.h
@@ -51,6 +51,11 @@ class AX_EXPORT AXTreeManager {
// Called when the tree manager is about to be removed from the tree map,
// `AXTreeManagerMap`.
virtual void WillBeRemovedFromMap() {}
+
+ // For debugging.
+ // TODO(benjamin.beaudry) Instead of this, implement GetTreeData() on all
+ // AXTreeManager subclasses, and have callers use GetTreeData().ToString();
+ virtual std::string ToString() const = 0;
};
} // namespace ui
diff --git a/ui/accessibility/test_ax_tree_manager.cc b/ui/accessibility/test_ax_tree_manager.cc
index 15627a472481b46c66c3d40efa3895ab14cb89ef..5ee1750a265db3e9291c5f5a9beb1a4228278af2 100644
--- a/ui/accessibility/test_ax_tree_manager.cc
+++ b/ui/accessibility/test_ax_tree_manager.cc
@@ -116,4 +116,8 @@ AXNode* TestAXTreeManager::GetParentNodeFromParentTreeAsAXNode() const {
return nullptr;
}
+std::string TestAXTreeManager::ToString() const {
+ return "<TestAXTreeManager>";
+}
+
} // namespace ui
diff --git a/ui/accessibility/test_ax_tree_manager.h b/ui/accessibility/test_ax_tree_manager.h
index c155aeb4f164129a3b484386f0e4802a361df80a..dabc5650132d01f121f8a9763afc499800bfbd9c 100644
--- a/ui/accessibility/test_ax_tree_manager.h
+++ b/ui/accessibility/test_ax_tree_manager.h
@@ -53,6 +53,7 @@ class AX_EXPORT TestAXTreeManager : public AXTreeManager {
AXTreeID GetParentTreeID() const override;
AXNode* GetRootAsAXNode() const override;
AXNode* GetParentNodeFromParentTreeAsAXNode() const override;
+ std::string ToString() const override;
private:
std::unique_ptr<AXTree> tree_;
diff --git a/ui/views/accessibility/views_ax_tree_manager.cc b/ui/views/accessibility/views_ax_tree_manager.cc
index 53810d5b04bd1b0e61224a56229a8b83bbd60fba..fb1befba94c9cde34518bbddb7b5f309d892f369 100644
--- a/ui/views/accessibility/views_ax_tree_manager.cc
+++ b/ui/views/accessibility/views_ax_tree_manager.cc
@@ -4,8 +4,6 @@
#include "ui/views/accessibility/views_ax_tree_manager.h"
-#include <string>
-
#include "base/bind.h"
#include "base/callback.h"
#include "base/check.h"
@@ -99,6 +97,10 @@ ui::AXNode* ViewsAXTreeManager::GetParentNodeFromParentTreeAsAXNode() const {
return nullptr;
}
+std::string ViewsAXTreeManager::ToString() const {
+ return "<ViewsAXTreeManager>";
+}
+
void ViewsAXTreeManager::OnViewEvent(View* view, ax::mojom::Event event) {
DCHECK(view);
AXAuraObjWrapper* wrapper = cache_.GetOrCreate(view);
diff --git a/ui/views/accessibility/views_ax_tree_manager.h b/ui/views/accessibility/views_ax_tree_manager.h
index c46b23a8819cba1a4fd1950d6db66a55e1a08eec..7678983b31a9e852a05d4feed48f369ab7730c27 100644
--- a/ui/views/accessibility/views_ax_tree_manager.h
+++ b/ui/views/accessibility/views_ax_tree_manager.h
@@ -7,6 +7,7 @@
#include <memory>
#include <set>
+#include <string>
#include <vector>
#include "base/callback_forward.h"
@@ -89,6 +90,7 @@ class VIEWS_EXPORT ViewsAXTreeManager : public ui::AXTreeManager,
ui::AXTreeID GetParentTreeID() const override;
ui::AXNode* GetRootAsAXNode() const override;
ui::AXNode* GetParentNodeFromParentTreeAsAXNode() const override;
+ std::string ToString() const override;
// AXActionHandlerBase implementation.
void PerformAction(const ui::AXActionData& data) override;

View File

@@ -0,0 +1,250 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jorrit Jongma <jorrit@jongma.org>
Date: Tue, 12 Apr 2022 17:09:34 +0000
Subject: Support 16kb pagesize on Linux+ARM64
This makes the system pagesize a run-time property.
ARM64 supports 4kb, 16kb, and 64kb page sizes. Previously, only 4kb
was supported by Chromium. This patch adds 16kb support, as is used
for example by Asahi Linux on M1 Macs. The rare 64kb case is still
not supported due to further changes needed to SlotSpanMetadata.
The implementation follows the changes made to support run-time page
size on macOS. On macOS, the required constants are conveniently
injected before any code runs, while on Linux a function call is
needed, complicating initialization.
The new PageCharacteristics structure holds the page size and shift
as std::atomic<int> which are initialized on first use.
Bug: 1301788
Change-Id: I8ceead40de53ba7a2ec248bd6ef46f2a521dd29c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3545665
Reviewed-by: Benoit Lize <lizeb@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#991588}
diff --git a/AUTHORS b/AUTHORS
index 364df55a2888c221523023facd8873fa010d40d6..f2b8d8c0d2a4474ded579212327f00489da00623 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -572,6 +572,7 @@ Jongsoo Lee <leejongsoo@gmail.com>
Joone Hur <joone.hur@intel.com>
Joonghun Park <pjh0718@gmail.com>
Jorge Villatoro <jorge@tomatocannon.com>
+Jorrit Jongma <jorrit@jongma.org>
Joseph Gentle <josephg@gmail.com>
Joseph Lolak <joseph.lolak@samsung.com>
Josh Triplett <josh.triplett@intel.com>
diff --git a/base/allocator/partition_allocator/address_space_randomization.h b/base/allocator/partition_allocator/address_space_randomization.h
index 43033d728050a8d8b03f5e4a69fa1593190d30f1..e77757c3ad512f03cd21127ebd780e7a19f12672 100644
--- a/base/allocator/partition_allocator/address_space_randomization.h
+++ b/base/allocator/partition_allocator/address_space_randomization.h
@@ -121,6 +121,21 @@ AslrMask(uintptr_t bits) {
return AslrAddress(0x20000000ULL);
}
+ #elif BUILDFLAG(IS_LINUX)
+
+ // Linux on arm64 can use 39, 42, 48, or 52-bit user space, depending on
+ // page size and number of levels of translation pages used. We use
+ // 39-bit as base as all setups should support this, lowered to 38-bit
+ // as ASLROffset() could cause a carry.
+ PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
+ ASLRMask() {
+ return AslrMask(38);
+ }
+ PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE uintptr_t
+ ASLROffset() {
+ return AslrAddress(0x1000000000ULL);
+ }
+
#else
// ARM64 on Linux has 39-bit user space. Use 38 bits since ASLROffset()
diff --git a/base/allocator/partition_allocator/page_allocator_constants.h b/base/allocator/partition_allocator/page_allocator_constants.h
index 12515b9a02865eb8a4b2a7c15b0fcce06a8ec7f9..0a996cfdcc4a42005ffad8922f3cc55547c47d20 100644
--- a/base/allocator/partition_allocator/page_allocator_constants.h
+++ b/base/allocator/partition_allocator/page_allocator_constants.h
@@ -24,6 +24,31 @@
// elimination.
#define PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR __attribute__((const))
+#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
+// This should work for all POSIX (if needed), but currently all other
+// supported OS/architecture combinations use either hard-coded values
+// (such as x86) or have means to determine these values without needing
+// atomics (such as macOS on arm64).
+
+// Page allocator constants are run-time constant
+#define PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR __attribute__((const))
+
+#include <unistd.h>
+#include <atomic>
+
+namespace partition_alloc::internal {
+
+// Holds the current page size and shift, where size = 1 << shift
+// Use PageAllocationGranularity(), PageAllocationGranularityShift()
+// to initialize and retrieve these values safely.
+struct PageCharacteristics {
+ std::atomic<int> size;
+ std::atomic<int> shift;
+};
+extern PageCharacteristics page_characteristics;
+
+} // namespace partition_alloc::internal
+
#else
// When defined, page size constants are fixed at compile time. When not
@@ -38,6 +63,10 @@
namespace partition_alloc::internal {
+// Forward declaration, implementation below
+PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
+PageAllocationGranularity();
+
PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
PageAllocationGranularityShift() {
#if BUILDFLAG(IS_WIN) || defined(ARCH_CPU_PPC64)
@@ -50,6 +79,15 @@ PageAllocationGranularityShift() {
return 14; // 16kB
#elif BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)
return vm_page_shift;
+#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
+ // arm64 supports 4kb (shift = 12), 16kb (shift = 14), and 64kb (shift = 16)
+ // page sizes. Retrieve from or initialize cache.
+ int shift = page_characteristics.shift.load(std::memory_order_relaxed);
+ if (UNLIKELY(shift == 0)) {
+ shift = __builtin_ctz((int)PageAllocationGranularity());
+ page_characteristics.shift.store(shift, std::memory_order_relaxed);
+ }
+ return shift;
#else
return 12; // 4kB
#endif
@@ -59,8 +97,17 @@ PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
PageAllocationGranularity() {
#if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)
// This is literally equivalent to |1 << PageAllocationGranularityShift()|
- // below, but was separated out for OS_APPLE to avoid << on a non-constexpr.
+ // below, but was separated out for IS_APPLE to avoid << on a non-constexpr.
return vm_page_size;
+#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
+ // arm64 supports 4kb, 16kb, and 64kb page sizes. Retrieve from or
+ // initialize cache.
+ int size = page_characteristics.size.load(std::memory_order_relaxed);
+ if (UNLIKELY(size == 0)) {
+ size = getpagesize();
+ page_characteristics.size.store(size, std::memory_order_relaxed);
+ }
+ return size;
#else
return 1 << PageAllocationGranularityShift();
#endif
@@ -90,9 +137,11 @@ SystemPageShift() {
PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
SystemPageSize() {
-#if BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)
+#if (BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)) || \
+ (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64))
// This is literally equivalent to |1 << SystemPageShift()| below, but was
- // separated out for 64-bit OS_APPLE to avoid << on a non-constexpr.
+ // separated out for 64-bit IS_APPLE and arm64 on Linux to avoid << on a
+ // non-constexpr.
return PageAllocationGranularity();
#else
return 1 << SystemPageShift();
diff --git a/base/allocator/partition_allocator/partition_address_space.cc b/base/allocator/partition_allocator/partition_address_space.cc
index 71075090cc64bfac2d443a3ee4e210d7aca8a3e5..1e9dc5d312e154ef7a1c66aaef4de3c45a69c912 100644
--- a/base/allocator/partition_allocator/partition_address_space.cc
+++ b/base/allocator/partition_allocator/partition_address_space.cc
@@ -167,6 +167,12 @@ void PartitionAddressSpace::UninitConfigurablePoolForTesting() {
setup_.configurable_pool_ = 0;
}
+#if BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
+
+PageCharacteristics page_characteristics;
+
+#endif // BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
+
#endif // defined(PA_HAS_64_BITS_POINTERS)
} // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_alloc_constants.h b/base/allocator/partition_allocator/partition_alloc_constants.h
index e54a9a4a3bfafc1a174ed3f3b3cfa98e9d0794f3..1a1c110dc7b49f59e1b9cb7fa0297b90f70880ec 100644
--- a/base/allocator/partition_allocator/partition_alloc_constants.h
+++ b/base/allocator/partition_allocator/partition_alloc_constants.h
@@ -59,10 +59,11 @@ PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
PartitionPageShift() {
return 18; // 256 KiB
}
-#elif BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)
+#elif (BUILDFLAG(IS_APPLE) && defined(ARCH_CPU_64_BITS)) || \
+ (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64))
PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
PartitionPageShift() {
- return vm_page_shift + 2;
+ return PageAllocationGranularityShift() + 2;
}
#else
PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
diff --git a/base/allocator/partition_allocator/partition_page.h b/base/allocator/partition_allocator/partition_page.h
index 5ee5e2c3843a6fa7d8717a619b5d8ccec2edd8f3..efaf910f2b4e9db812402db6566bb7b6aeb354f2 100644
--- a/base/allocator/partition_allocator/partition_page.h
+++ b/base/allocator/partition_allocator/partition_page.h
@@ -134,6 +134,12 @@ struct __attribute__((packed)) SlotSpanMetadata {
// PartitionPageSize() is 4 times the OS page size.
static constexpr size_t kMaxSlotsPerSlotSpan =
4 * (1 << 14) / kSmallestBucket;
+#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
+ // System page size can be 4, 16, or 64 kiB on Linux on arm64. 64 kiB is
+ // currently (kMaxSlotsPerSlotSpanBits == 13) not supported by the code,
+ // so we use the 16 kiB maximum (64 kiB will crash).
+ static constexpr size_t kMaxSlotsPerSlotSpan =
+ 4 * (1 << 14) / kSmallestBucket;
#else
// A slot span can "span" multiple PartitionPages, but then its slot size is
// larger, so it doesn't have as many slots.
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc
index 98e3940c44cb7460b91305aab0fcc24ef739b979..31f6519227c0c6c5d6235cff8567e46ebe010d24 100644
--- a/base/allocator/partition_allocator/partition_root.cc
+++ b/base/allocator/partition_allocator/partition_root.cc
@@ -309,11 +309,12 @@ static size_t PartitionPurgeSlotSpan(
constexpr size_t kMaxSlotCount =
(PartitionPageSize() * kMaxPartitionPagesPerRegularSlotSpan) /
SystemPageSize();
-#elif BUILDFLAG(IS_APPLE)
+#elif BUILDFLAG(IS_APPLE) || (BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64))
// It's better for slot_usage to be stack-allocated and fixed-size, which
- // demands that its size be constexpr. On OS_APPLE, PartitionPageSize() is
- // always SystemPageSize() << 2, so regardless of what the run time page size
- // is, kMaxSlotCount can always be simplified to this expression.
+ // demands that its size be constexpr. On OS_APPLE and Linux on arm64,
+ // PartitionPageSize() is always SystemPageSize() << 2, so regardless of
+ // what the run time page size is, kMaxSlotCount can always be simplified
+ // to this expression.
constexpr size_t kMaxSlotCount = 4 * kMaxPartitionPagesPerRegularSlotSpan;
PA_CHECK(kMaxSlotCount ==
(PartitionPageSize() * kMaxPartitionPagesPerRegularSlotSpan) /
@@ -633,6 +634,14 @@ void PartitionRoot<thread_safe>::Init(PartitionOptions opts) {
// apple OSes.
PA_CHECK((SystemPageSize() == (size_t{1} << 12)) ||
(SystemPageSize() == (size_t{1} << 14)));
+#elif BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM64)
+ // Check runtime pagesize. Though the code is currently the same, it is
+ // not merged with the IS_APPLE case above as a 1 << 16 case needs to be
+ // added here in the future, to allow 64 kiB pagesize. That is only
+ // supported on Linux on arm64, not on IS_APPLE, but not yet present here
+ // as the rest of the partition allocator does not currently support it.
+ PA_CHECK((SystemPageSize() == (size_t{1} << 12)) ||
+ (SystemPageSize() == (size_t{1} << 14)));
#endif
::partition_alloc::internal::ScopedGuard guard{lock_};

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 35742be01f2c5c9f834bb7d2f03b261193bc69f2..2769936403ca6d4a1bb6c1f61ac6db776feba6d6 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
@@ -45,7 +45,7 @@
#include "util/linux/socket.h"
#include "util/misc/address_sanitizer.h"
#include "util/misc/from_pointer_cast.h"
-#include "util/posix/double_fork_and_exec.h"
+#include "util/posix/fork_and_spawn.h"
#include "util/posix/scoped_mmap.h"
#include "util/posix/signals.h"
@@ -445,7 +445,7 @@ bool CrashpadClient::StartHandler(
argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get()));
argv.push_back("--shared-client-connection");
- if (!DoubleForkAndExec(argv, nullptr, handler_sock.get(), false, nullptr)) {
+ if (!ForkAndSpawn(argv, nullptr, handler_sock.get(), false, nullptr)) {
return false;
}
@@ -600,7 +600,7 @@ bool CrashpadClient::StartJavaHandlerForClient(
int socket) {
std::vector<std::string> argv = BuildAppProcessArgs(
class_name, database, metrics_dir, url, annotations, arguments, socket);
- return DoubleForkAndExec(argv, env, socket, false, nullptr);
+ return ForkAndSpawn(argv, env, socket, false, nullptr);
}
bool CrashpadClient::StartHandlerWithLinkerAtCrash(
@@ -649,7 +649,7 @@ bool CrashpadClient::StartHandlerWithLinkerForClient(
annotations,
arguments,
socket);
- return DoubleForkAndExec(argv, env, socket, false, nullptr);
+ return ForkAndSpawn(argv, env, socket, false, nullptr);
}
#endif
@@ -683,7 +683,7 @@ bool CrashpadClient::StartHandlerForClient(
argv.push_back(FormatArgumentInt("initial-client-fd", socket));
- return DoubleForkAndExec(argv, nullptr, socket, true, nullptr);
+ return ForkAndSpawn(argv, nullptr, socket, true, nullptr);
}
// static
diff --git a/third_party/crashpad/crashpad/client/crashpad_client_mac.cc b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
index d25bfb71638a4837d40950df770b461d8beac351..585bac910a4b6d3fef10ab23b86a04c2985173b8 100644
--- a/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
+++ b/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
@@ -36,7 +36,7 @@
#include "util/mach/notify_server.h"
#include "util/misc/clock.h"
#include "util/misc/implicit_cast.h"
-#include "util/posix/double_fork_and_exec.h"
+#include "util/posix/fork_and_spawn.h"
namespace crashpad {
@@ -343,7 +343,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface {
// this parent process, which was probably using the exception server now
// being restarted. The handler 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 8251312c78e1c1bfc4aff8fa8b7ade1f5c191e33..81c2484461843268bd8a6c21de3d7fd2ea504ac4 100644
--- a/third_party/crashpad/crashpad/util/BUILD.gn
+++ b/third_party/crashpad/crashpad/util/BUILD.gn
@@ -296,10 +296,10 @@ crashpad_static_library("util") {
sources += [
"posix/close_multiple.cc",
"posix/close_multiple.h",
- "posix/double_fork_and_exec.cc",
- "posix/double_fork_and_exec.h",
"posix/drop_privileges.cc",
"posix/drop_privileges.h",
+ "posix/fork_and_spawn.cc",
+ "posix/fork_and_spawn.h",
"posix/process_info.h",
# These map signals to and from strings. While Fuchsia defines some of
diff --git a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc b/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc
deleted file mode 100644
index 1960430954d3f6459dce688493db5c42047567b0..0000000000000000000000000000000000000000
--- a/third_party/crashpad/crashpad/util/posix/double_fork_and_exec.cc
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2017 The Crashpad Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "util/posix/double_fork_and_exec.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "base/check_op.h"
-#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
-#include "base/strings/stringprintf.h"
-#include "util/posix/close_multiple.h"
-
-namespace crashpad {
-
-bool DoubleForkAndExec(const std::vector<std::string>& argv,
- const std::vector<std::string>* envp,
- int preserve_fd,
- bool use_path,
- void (*child_function)()) {
- DCHECK(!envp || !use_path);
-
- // argv_c contains const char* pointers and is terminated by nullptr. This is
- // suitable for passing to execv(). Although argv_c is not used in the parent
- // process, it must be built in the parent process because 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

@@ -0,0 +1,57 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tom Anderson <thomasanderson@chromium.org>
Date: Fri, 24 Jun 2022 22:27:07 +0000
Subject: Remove default window title
This is to prevent the enterprise startup dialog from having "Ozone X11"
in the titlebar. The intention is for the window to have no title [1]
[1] https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc;drc=855b63f8fe9115f8b38d4736a259f9a96f83fcc6;l=209
R=sky
Change-Id: Iecc200941ad36b6d9feb91e88791b3612e941ea6
Fixed: 1336343
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3722478
Reviewed-by: Scott Violet <sky@chromium.org>
Commit-Queue: Thomas Anderson <thomasanderson@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1017858}
diff --git a/ui/ozone/platform/x11/ozone_platform_x11.cc b/ui/ozone/platform/x11/ozone_platform_x11.cc
index e312287e4aca61b51a69c8413088f56f9f704b5e..06bb59fe5e855d0a339e738cf12c566afcf376ac 100644
--- a/ui/ozone/platform/x11/ozone_platform_x11.cc
+++ b/ui/ozone/platform/x11/ozone_platform_x11.cc
@@ -102,7 +102,6 @@ class OzonePlatformX11 : public OzonePlatform,
PlatformWindowInitProperties properties) override {
auto window = std::make_unique<X11Window>(delegate);
window->Initialize(std::move(properties));
- window->SetTitle(u"Ozone X11");
return std::move(window);
}
diff --git a/ui/ozone/platform/x11/x11_window.cc b/ui/ozone/platform/x11/x11_window.cc
index 15b5f6ff21d3fca9e06e95bae08d1b2c0d2e3b1c..0b002c3950c9d450f42212f5dc9c5ce1e7cff010 100644
--- a/ui/ozone/platform/x11/x11_window.cc
+++ b/ui/ozone/platform/x11/x11_window.cc
@@ -362,6 +362,8 @@ void X11Window::Initialize(PlatformWindowInitProperties properties) {
if (wm_role_name)
SetWindowRole(xwindow_, std::string(wm_role_name));
+ SetTitle(u"");
+
if (properties.remove_standard_frame) {
// Setting _GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED tells gnome-shell to not force
// fullscreen on the window when it matches the desktop size.
diff --git a/ui/ozone/platform/x11/x11_window.h b/ui/ozone/platform/x11/x11_window.h
index 0b4ccfed94123120d491d08a4c95a02624d9d0ef..147304843f2acc90cf534b8da7710541605fe880 100644
--- a/ui/ozone/platform/x11/x11_window.h
+++ b/ui/ozone/platform/x11/x11_window.h
@@ -363,7 +363,7 @@ class X11Window : public PlatformWindow,
// Was this window initialized with the override_redirect window attribute?
bool override_redirect_ = false;
- std::u16string window_title_;
+ absl::optional<std::u16string> window_title_;
// Whether the window is visible with respect to Aura.
bool window_mapped_in_client_ = false;

View File

@@ -13,11 +13,17 @@
"src/electron/patches/nan": "src/third_party/nan",
"src/electron/patches/ffmpeg": "src/third_party/ffmpeg",
"src/electron/patches/perfetto": "src/third_party/perfetto",
"src/electron/patches/squirrel.mac": "src/third_party/squirrel.mac",
"src/electron/patches/Mantle": "src/third_party/squirrel.mac/vendor/Mantle",
"src/electron/patches/ReactiveObjC": "src/third_party/squirrel.mac/vendor/ReactiveObjC"
"src/electron/patches/ReactiveObjC": "src/third_party/squirrel.mac/vendor/ReactiveObjC",
"src/electron/patches/angle": "src/third_party/angle",
"src/electron/patches/pdfium": "src/third_party/pdfium"
}

1
patches/ffmpeg/.patches Normal file
View File

@@ -0,0 +1 @@
cherry-pick-e481fc655a62.patch

View File

@@ -0,0 +1,27 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Dan Sanders <sandersd@chromium.org>
Date: Tue, 3 May 2022 14:39:37 -0700
Subject: Do not parse late SEI messages during decoding.
Bug: 1306751
Change-Id: I2088b9ff89bd8eee8ab82675258af302d9bfccf9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/third_party/ffmpeg/+/3625832
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
index 73b17ba43c3eda5bed4b4abc70d22bbebf0c2eac..a6413bc20db5d08a5c8f1ee4b07edbd4870e168c 100644
--- a/libavcodec/h264dec.c
+++ b/libavcodec/h264dec.c
@@ -678,6 +678,12 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
avpriv_request_sample(avctx, "data partitioning");
break;
case H264_NAL_SEI:
+ // If setup is finished, threads can contend over the contents of
+ // the active SEI.
+ if (h->setup_finished) {
+ av_log(h->avctx, AV_LOG_DEBUG, "Late SEI\n");
+ break;
+ }
ret = ff_h264_sei_decode(&h->sei, &nal->gb, &h->ps, avctx);
h->has_recovery_point = h->has_recovery_point || h->sei.recovery_point.recovery_frame_cnt != -1;
if (avctx->debug & FF_DEBUG_GREEN_MD)

1
patches/pdfium/.patches Normal file
View File

@@ -0,0 +1 @@
cherry-pick-3466cc056b05.patch

View File

@@ -0,0 +1,283 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tom Sepez <tsepez@chromium.org>
Date: Tue, 12 Jul 2022 18:13:14 +0000
Subject: M102: Retain nodes when manipulating their dictionaries in
CPDF_NameTree.
-- Pass retain ptrs consistently in a few other places.
Bug: chromium:1335861
Change-Id: If23cf6b6ec39ef02384beaa6745e1c7256160cba
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/94430
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
(cherry picked from commit ebebb757c1f210ccc16e0cb2b425860a141a4e9f)
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/95271
Reviewed-by: Nigi <nigi@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_array.cpp b/core/fpdfapi/parser/cpdf_array.cpp
index 708e6778a8d5383c2fd15518e091bb439212a1c7..6aa6f3089f191799231d49949de7f60adf367b5a 100644
--- a/core/fpdfapi/parser/cpdf_array.cpp
+++ b/core/fpdfapi/parser/cpdf_array.cpp
@@ -10,6 +10,7 @@
#include <utility>
#include "core/fpdfapi/parser/cpdf_boolean.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_name.h"
#include "core/fpdfapi/parser/cpdf_number.h"
#include "core/fpdfapi/parser/cpdf_reference.h"
@@ -153,6 +154,10 @@ float CPDF_Array::GetNumberAt(size_t index) const {
return m_Objects[index]->GetNumber();
}
+RetainPtr<CPDF_Dictionary> CPDF_Array::GetMutableDictAt(size_t index) {
+ return pdfium::WrapRetain(GetDictAt(index));
+}
+
CPDF_Dictionary* CPDF_Array::GetDictAt(size_t index) {
CPDF_Object* p = GetDirectObjectAt(index);
if (!p)
diff --git a/core/fpdfapi/parser/cpdf_array.h b/core/fpdfapi/parser/cpdf_array.h
index 2270c7d9c8f22bbac3d67c8c5b6570bc128a72b4..223cd59ab7cb6cc2c08071118d1fbb3241904c6b 100644
--- a/core/fpdfapi/parser/cpdf_array.h
+++ b/core/fpdfapi/parser/cpdf_array.h
@@ -62,7 +62,8 @@ class CPDF_Array final : public CPDF_Object {
bool GetBooleanAt(size_t index, bool bDefault) const;
int GetIntegerAt(size_t index) const;
float GetNumberAt(size_t index) const;
- CPDF_Dictionary* GetDictAt(size_t index);
+ RetainPtr<CPDF_Dictionary> GetMutableDictAt(size_t index);
+ CPDF_Dictionary* GetDictAt(size_t index); // prefer previous form.
const CPDF_Dictionary* GetDictAt(size_t index) const;
CPDF_Stream* GetStreamAt(size_t index);
const CPDF_Stream* GetStreamAt(size_t index) const;
diff --git a/core/fpdfapi/parser/cpdf_dictionary.cpp b/core/fpdfapi/parser/cpdf_dictionary.cpp
index fc671e76bfb6f17af93a19cca57aa0532c4111e4..047b25e2c0b539a00814de6cd80d8f224413586b 100644
--- a/core/fpdfapi/parser/cpdf_dictionary.cpp
+++ b/core/fpdfapi/parser/cpdf_dictionary.cpp
@@ -148,6 +148,11 @@ float CPDF_Dictionary::GetNumberFor(const ByteString& key) const {
return p ? p->GetNumber() : 0;
}
+RetainPtr<CPDF_Dictionary> CPDF_Dictionary::GetMutableDictFor(
+ const ByteString& key) {
+ return pdfium::WrapRetain(GetDictFor(key));
+}
+
const CPDF_Dictionary* CPDF_Dictionary::GetDictFor(
const ByteString& key) const {
const CPDF_Object* p = GetDirectObjectFor(key);
@@ -165,6 +170,11 @@ CPDF_Dictionary* CPDF_Dictionary::GetDictFor(const ByteString& key) {
static_cast<const CPDF_Dictionary*>(this)->GetDictFor(key));
}
+RetainPtr<CPDF_Array> CPDF_Dictionary::GetMutableArrayFor(
+ const ByteString& key) {
+ return pdfium::WrapRetain(GetArrayFor(key));
+}
+
const CPDF_Array* CPDF_Dictionary::GetArrayFor(const ByteString& key) const {
return ToArray(GetDirectObjectFor(key));
}
diff --git a/core/fpdfapi/parser/cpdf_dictionary.h b/core/fpdfapi/parser/cpdf_dictionary.h
index d2f814327e7b880d68119aa938e73af1260668d5..fe990efaa9844fc6a7aa2c6fd671d74a2446383a 100644
--- a/core/fpdfapi/parser/cpdf_dictionary.h
+++ b/core/fpdfapi/parser/cpdf_dictionary.h
@@ -67,9 +67,11 @@ class CPDF_Dictionary final : public CPDF_Object {
int GetIntegerFor(const ByteString& key, int default_int) const;
float GetNumberFor(const ByteString& key) const;
const CPDF_Dictionary* GetDictFor(const ByteString& key) const;
- CPDF_Dictionary* GetDictFor(const ByteString& key);
+ CPDF_Dictionary* GetDictFor(const ByteString& key); // Prefer next form.
+ RetainPtr<CPDF_Dictionary> GetMutableDictFor(const ByteString& key);
const CPDF_Array* GetArrayFor(const ByteString& key) const;
- CPDF_Array* GetArrayFor(const ByteString& key);
+ CPDF_Array* GetArrayFor(const ByteString& key); // Prefer next form.
+ RetainPtr<CPDF_Array> GetMutableArrayFor(const ByteString& key);
const CPDF_Stream* GetStreamFor(const ByteString& key) const;
CPDF_Stream* GetStreamFor(const ByteString& key);
CFX_FloatRect GetRectFor(const ByteString& key) const;
diff --git a/core/fpdfdoc/cpdf_nametree.cpp b/core/fpdfdoc/cpdf_nametree.cpp
index dfd4e619c055229135b67bb056cb546992fe1d21..20b68b5874ff14b5625c6fc028211ce44b53a119 100644
--- a/core/fpdfdoc/cpdf_nametree.cpp
+++ b/core/fpdfdoc/cpdf_nametree.cpp
@@ -42,7 +42,7 @@ std::pair<WideString, WideString> GetNodeLimitsAndSanitize(
// Get the limit arrays that leaf array |pFind| is under in the tree with root
// |pNode|. |pLimits| will hold all the limit arrays from the leaf up to before
// the root. Return true if successful.
-bool GetNodeAncestorsLimitsInternal(CPDF_Dictionary* pNode,
+bool GetNodeAncestorsLimitsInternal(const RetainPtr<CPDF_Dictionary>& pNode,
const CPDF_Array* pFind,
int nLevel,
std::vector<CPDF_Array*>* pLimits) {
@@ -59,7 +59,7 @@ bool GetNodeAncestorsLimitsInternal(CPDF_Dictionary* pNode,
return false;
for (size_t i = 0; i < pKids->size(); ++i) {
- CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+ RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
if (!pKid)
continue;
@@ -73,8 +73,9 @@ bool GetNodeAncestorsLimitsInternal(CPDF_Dictionary* pNode,
// Wrapper for GetNodeAncestorsLimitsInternal() so callers do not need to know
// about the details.
-std::vector<CPDF_Array*> GetNodeAncestorsLimits(CPDF_Dictionary* pNode,
- const CPDF_Array* pFind) {
+std::vector<CPDF_Array*> GetNodeAncestorsLimits(
+ const RetainPtr<CPDF_Dictionary>& pNode,
+ const CPDF_Array* pFind) {
std::vector<CPDF_Array*> results;
GetNodeAncestorsLimitsInternal(pNode, pFind, 0, &results);
return results;
@@ -168,21 +169,22 @@ bool UpdateNodesAndLimitsUponDeletion(CPDF_Dictionary* pNode,
// will be the index of |csName| in |ppFind|. If |csName| is not found, |ppFind|
// will be the leaf array that |csName| should be added to, and |pFindIndex|
// will be the index that it should be added at.
-CPDF_Object* SearchNameNodeByNameInternal(CPDF_Dictionary* pNode,
- const WideString& csName,
- int nLevel,
- size_t* nIndex,
- CPDF_Array** ppFind,
- int* pFindIndex) {
+CPDF_Object* SearchNameNodeByNameInternal(
+ const RetainPtr<CPDF_Dictionary>& pNode,
+ const WideString& csName,
+ int nLevel,
+ size_t* nIndex,
+ RetainPtr<CPDF_Array>* ppFind,
+ int* pFindIndex) {
if (nLevel > kNameTreeMaxRecursion)
return nullptr;
- CPDF_Array* pLimits = pNode->GetArrayFor("Limits");
- CPDF_Array* pNames = pNode->GetArrayFor("Names");
+ RetainPtr<CPDF_Array> pLimits = pNode->GetMutableArrayFor("Limits");
+ RetainPtr<CPDF_Array> pNames = pNode->GetMutableArrayFor("Names");
if (pLimits) {
WideString csLeft;
WideString csRight;
- std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits);
+ std::tie(csLeft, csRight) = GetNodeLimitsAndSanitize(pLimits.Get());
// Skip this node if the name to look for is smaller than its lower limit.
if (csName.Compare(csLeft) < 0)
return nullptr;
@@ -221,12 +223,12 @@ CPDF_Object* SearchNameNodeByNameInternal(CPDF_Dictionary* pNode,
}
// Search through the node's children.
- CPDF_Array* pKids = pNode->GetArrayFor("Kids");
+ RetainPtr<CPDF_Array> pKids = pNode->GetMutableArrayFor("Kids");
if (!pKids)
return nullptr;
for (size_t i = 0; i < pKids->size(); i++) {
- CPDF_Dictionary* pKid = pKids->GetDictAt(i);
+ RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
if (!pKid)
continue;
@@ -240,9 +242,9 @@ CPDF_Object* SearchNameNodeByNameInternal(CPDF_Dictionary* pNode,
// Wrapper for SearchNameNodeByNameInternal() so callers do not need to know
// about the details.
-CPDF_Object* SearchNameNodeByName(CPDF_Dictionary* pNode,
+CPDF_Object* SearchNameNodeByName(const RetainPtr<CPDF_Dictionary>& pNode,
const WideString& csName,
- CPDF_Array** ppFind,
+ RetainPtr<CPDF_Array>* ppFind,
int* pFindIndex) {
size_t nIndex = 0;
return SearchNameNodeByNameInternal(pNode, csName, 0, &nIndex, ppFind,
@@ -438,17 +440,17 @@ size_t CPDF_NameTree::GetCount() const {
bool CPDF_NameTree::AddValueAndName(RetainPtr<CPDF_Object> pObj,
const WideString& name) {
- CPDF_Array* pFind = nullptr;
+ RetainPtr<CPDF_Array> pFind;
int nFindIndex = -1;
// Handle the corner case where the root node is empty. i.e. No kids and no
// names. In which case, just insert into it and skip all the searches.
- CPDF_Array* pNames = m_pRoot->GetArrayFor("Names");
+ RetainPtr<CPDF_Array> pNames = m_pRoot->GetMutableArrayFor("Names");
if (pNames && pNames->IsEmpty() && !m_pRoot->GetArrayFor("Kids"))
pFind = pNames;
if (!pFind) {
// Fail if the tree already contains this name or if the tree is too deep.
- if (SearchNameNodeByName(m_pRoot.Get(), name, &pFind, &nFindIndex))
+ if (SearchNameNodeByName(m_pRoot, name, &pFind, &nFindIndex))
return false;
}
@@ -478,7 +480,7 @@ bool CPDF_NameTree::AddValueAndName(RetainPtr<CPDF_Object> pObj,
// Expand the limits that the newly added name is under, if the name falls
// outside of the limits of its leaf array or any arrays above it.
std::vector<CPDF_Array*> all_limits =
- GetNodeAncestorsLimits(m_pRoot.Get(), pFind);
+ GetNodeAncestorsLimits(m_pRoot, pFind.Get());
for (auto* pLimits : all_limits) {
if (!pLimits)
continue;
@@ -524,7 +526,7 @@ CPDF_Object* CPDF_NameTree::LookupValueAndName(size_t nIndex,
}
CPDF_Object* CPDF_NameTree::LookupValue(const WideString& csName) const {
- return SearchNameNodeByName(m_pRoot.Get(), csName, nullptr, nullptr);
+ return SearchNameNodeByName(m_pRoot, csName, nullptr, nullptr);
}
CPDF_Array* CPDF_NameTree::LookupNewStyleNamedDest(const ByteString& sName) {
diff --git a/testing/resources/javascript/bug_1335681.in b/testing/resources/javascript/bug_1335681.in
new file mode 100644
index 0000000000000000000000000000000000000000..254e5964518c40029fcde1e0f31f3e2703d9b85f
--- /dev/null
+++ b/testing/resources/javascript/bug_1335681.in
@@ -0,0 +1,38 @@
+{{header}}
+{{object 1 0}} <<
+ /Pages 1 0 R
+ /OpenAction 2 0 R
+ /Names <<
+ /Dests 3 0 R
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Action
+ /S /JavaScript
+ /JS (
+ app.alert\("Starting"\);
+ this.gotoNamedDest\(""\);
+ )
+>>
+endobj
+{{object 3 0}} <<
+ /Kids 4 0 R
+>>
+endobj
+{{object 4 0}} [
+ << >>
+ << >>
+ <<
+ /Kids [
+ <<
+ /Limits 4 0 R
+ >>
+ ]
+ >>
+]
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1335681_expected.txt b/testing/resources/javascript/bug_1335681_expected.txt
new file mode 100644
index 0000000000000000000000000000000000000000..80a6951c49c8c8ef9a4e0036f7d4875c38373b89
--- /dev/null
+++ b/testing/resources/javascript/bug_1335681_expected.txt
@@ -0,0 +1 @@
+Alert: Starting

View File

@@ -8,3 +8,7 @@ fix_build_deprecated_attribute_for_older_msvc_versions.patch
fix_disable_implies_dcheck_for_node_stream_array_buffers.patch
revert_fix_cppgc_removed_deleted_cstors_in_cppheapcreateparams.patch
cherry-pick-723ed8a9cfff.patch
cherry-pick-44c4e56fea2c.patch
version_10_2_154_10_cherry-pick.patch
cherry-pick-13ffdf63a471.patch
cherry-pick-8ea66a7833e2.patch

View File

@@ -0,0 +1,169 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tobias Tebbi <tebbi@chromium.org>
Date: Fri, 17 Jun 2022 10:14:44 +0000
Subject: Merged: [compiler] make CanCover() transitive
In addition to checking that a node is owned, CanCover() also needs to
check if there are any side-effects in between the current node and
the merged node. When merging inputs of inputs, this check was done
with the wrong side-effect level of the in-between node.
We partially fixed this before with `CanCoverTransitively`.
This CL addresses the issue by always comparing to the side-effect
level of the node from which we started, making `CanCoverTransitively`
superfluous.
Bug: chromium:1336869
(cherry picked from commit 6048f754931e0971cab58fb0de785482d175175b)
Change-Id: I63cf6bfdd40c2c55921db4a2ac737612bb7da4e4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3722095
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/branch-heads/10.2@{#19}
Cr-Branched-From: 374091f382e88095694c1283cbdc2acddc1b1417-refs/heads/10.2.154@{#1}
Cr-Branched-From: f0c353f6315eeb2212ba52478983a3b3af07b5b1-refs/heads/main@{#79976}
diff --git a/src/compiler/backend/instruction-selector.cc b/src/compiler/backend/instruction-selector.cc
index 528aa543688041b816087efa02dec81ad8c9e3b1..b0e589738a0a1d7fb9d7f8eeb7677a5d7d0df022 100644
--- a/src/compiler/backend/instruction-selector.cc
+++ b/src/compiler/backend/instruction-selector.cc
@@ -283,7 +283,7 @@ Instruction* InstructionSelector::Emit(Instruction* instr) {
bool InstructionSelector::CanCover(Node* user, Node* node) const {
// 1. Both {user} and {node} must be in the same basic block.
- if (schedule()->block(node) != schedule()->block(user)) {
+ if (schedule()->block(node) != current_block_) {
return false;
}
// 2. Pure {node}s must be owned by the {user}.
@@ -291,7 +291,7 @@ bool InstructionSelector::CanCover(Node* user, Node* node) const {
return node->OwnedBy(user);
}
// 3. Impure {node}s must match the effect level of {user}.
- if (GetEffectLevel(node) != GetEffectLevel(user)) {
+ if (GetEffectLevel(node) != current_effect_level_) {
return false;
}
// 4. Only {node} must have value edges pointing to {user}.
@@ -303,21 +303,6 @@ bool InstructionSelector::CanCover(Node* user, Node* node) const {
return true;
}
-bool InstructionSelector::CanCoverTransitively(Node* user, Node* node,
- Node* node_input) const {
- if (CanCover(user, node) && CanCover(node, node_input)) {
- // If {node} is pure, transitivity might not hold.
- if (node->op()->HasProperty(Operator::kPure)) {
- // If {node_input} is pure, the effect levels do not matter.
- if (node_input->op()->HasProperty(Operator::kPure)) return true;
- // Otherwise, {user} and {node_input} must have the same effect level.
- return GetEffectLevel(user) == GetEffectLevel(node_input);
- }
- return true;
- }
- return false;
-}
-
bool InstructionSelector::IsOnlyUserOfNodeInSameBlock(Node* user,
Node* node) const {
BasicBlock* bb_user = schedule()->block(user);
@@ -1206,6 +1191,7 @@ void InstructionSelector::VisitBlock(BasicBlock* block) {
int effect_level = 0;
for (Node* const node : *block) {
SetEffectLevel(node, effect_level);
+ current_effect_level_ = effect_level;
if (node->opcode() == IrOpcode::kStore ||
node->opcode() == IrOpcode::kUnalignedStore ||
node->opcode() == IrOpcode::kCall ||
@@ -1223,6 +1209,7 @@ void InstructionSelector::VisitBlock(BasicBlock* block) {
// control input should be on the same effect level as the last node.
if (block->control_input() != nullptr) {
SetEffectLevel(block->control_input(), effect_level);
+ current_effect_level_ = effect_level;
}
auto FinishEmittedInstructions = [&](Node* node, int instruction_start) {
diff --git a/src/compiler/backend/instruction-selector.h b/src/compiler/backend/instruction-selector.h
index b33de8e8569ebce59eebfec6eef12ed1f1874ddc..167cdc1f0c4962d55f3f9b40b7865737fc652bb0 100644
--- a/src/compiler/backend/instruction-selector.h
+++ b/src/compiler/backend/instruction-selector.h
@@ -423,15 +423,12 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
// Used in pattern matching during code generation.
// Check if {node} can be covered while generating code for the current
// instruction. A node can be covered if the {user} of the node has the only
- // edge and the two are in the same basic block.
- // Before fusing two instructions a and b, it is useful to check that
- // CanCover(a, b) holds. If this is not the case, code for b must still be
- // generated for other users, and fusing is unlikely to improve performance.
+ // edge, the two are in the same basic block, and there are no side-effects
+ // in-between. The last check is crucial for soundness.
+ // For pure nodes, CanCover(a,b) is checked to avoid duplicated execution:
+ // If this is not the case, code for b must still be generated for other
+ // users, and fusing is unlikely to improve performance.
bool CanCover(Node* user, Node* node) const;
- // CanCover is not transitive. The counter example are Nodes A,B,C such that
- // CanCover(A, B) and CanCover(B,C) and B is pure: The the effect level of A
- // and B might differ. CanCoverTransitively does the additional checks.
- bool CanCoverTransitively(Node* user, Node* node, Node* node_input) const;
// Used in pattern matching during code generation.
// This function checks that {node} and {user} are in the same basic block,
@@ -756,6 +753,7 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
BoolVector defined_;
BoolVector used_;
IntVector effect_level_;
+ int current_effect_level_;
IntVector virtual_registers_;
IntVector virtual_register_rename_;
InstructionScheduler* scheduler_;
diff --git a/src/compiler/backend/loong64/instruction-selector-loong64.cc b/src/compiler/backend/loong64/instruction-selector-loong64.cc
index 4f03f99acd5b859b6fc095a34dea66ec058bcd4b..6b2d25f5dc1d912c1183e1b349bd29615024fa48 100644
--- a/src/compiler/backend/loong64/instruction-selector-loong64.cc
+++ b/src/compiler/backend/loong64/instruction-selector-loong64.cc
@@ -1446,7 +1446,7 @@ void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
if (CanCover(node, value)) {
switch (value->opcode()) {
case IrOpcode::kWord64Sar: {
- if (CanCoverTransitively(node, value, value->InputAt(0)) &&
+ if (CanCover(value, value->InputAt(0)) &&
TryEmitExtendingLoad(this, value, node)) {
return;
} else {
diff --git a/src/compiler/backend/mips64/instruction-selector-mips64.cc b/src/compiler/backend/mips64/instruction-selector-mips64.cc
index 4f5738ddaddba03aaa0ebbc351fe18d766e04d00..1e29e0ed730be49f4a09484d6c4db3ffca92355e 100644
--- a/src/compiler/backend/mips64/instruction-selector-mips64.cc
+++ b/src/compiler/backend/mips64/instruction-selector-mips64.cc
@@ -1532,7 +1532,7 @@ void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
if (CanCover(node, value)) {
switch (value->opcode()) {
case IrOpcode::kWord64Sar: {
- if (CanCoverTransitively(node, value, value->InputAt(0)) &&
+ if (CanCover(value, value->InputAt(0)) &&
TryEmitExtendingLoad(this, value, node)) {
return;
} else {
diff --git a/src/compiler/backend/riscv64/instruction-selector-riscv64.cc b/src/compiler/backend/riscv64/instruction-selector-riscv64.cc
index b6ced36f796b922df19e8bbb70db1bb6c33a2149..3b744795e5cd83ba4a8eff23cf4e607ffa0ca1c5 100644
--- a/src/compiler/backend/riscv64/instruction-selector-riscv64.cc
+++ b/src/compiler/backend/riscv64/instruction-selector-riscv64.cc
@@ -1428,7 +1428,7 @@ void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
if (CanCover(node, value)) {
switch (value->opcode()) {
case IrOpcode::kWord64Sar: {
- if (CanCoverTransitively(node, value, value->InputAt(0)) &&
+ if (CanCover(value, value->InputAt(0)) &&
TryEmitExtendingLoad(this, value, node)) {
return;
} else {
diff --git a/src/compiler/backend/x64/instruction-selector-x64.cc b/src/compiler/backend/x64/instruction-selector-x64.cc
index 3aec65221fb03e0d4143d08f5156b8a3f192e82c..5b8064010419d2749cddc0302ba63dc20f6e58df 100644
--- a/src/compiler/backend/x64/instruction-selector-x64.cc
+++ b/src/compiler/backend/x64/instruction-selector-x64.cc
@@ -1802,7 +1802,7 @@ void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
case IrOpcode::kWord64Shr: {
Int64BinopMatcher m(value);
if (m.right().Is(32)) {
- if (CanCoverTransitively(node, value, value->InputAt(0)) &&
+ if (CanCover(value, value->InputAt(0)) &&
TryMatchLoadWord64AndShiftRight(this, value, kX64Movl)) {
return EmitIdentity(node);
}

View File

@@ -0,0 +1,185 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shu-yu Guo <syg@chromium.org>
Date: Tue, 3 May 2022 13:26:32 -0700
Subject: Merged: [weakrefs] Set unregister_token to undefined when
unregistering
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
(cherry picked from commit dd3289d7945dac855d1287cf4ea248883e908d54)
Bug: chromium:1321078
Change-Id: Ic7537cc5101b35018911c27a81e9b0e0a7da154b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3695262
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/branch-heads/10.2@{#16}
Cr-Branched-From: 374091f382e88095694c1283cbdc2acddc1b1417-refs/heads/10.2.154@{#1}
Cr-Branched-From: f0c353f6315eeb2212ba52478983a3b3af07b5b1-refs/heads/main@{#79976}
diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc
index f1d6755161be21a76d084f2ccb8f73d6976d70f7..6999d52f2d38b1eb937e5fb46d26a90d4bfe6c0f 100644
--- a/src/heap/mark-compact.cc
+++ b/src/heap/mark-compact.cc
@@ -3003,14 +3003,11 @@ void MarkCompactCollector::ClearJSWeakRefs() {
// unregister_token field set to undefined when processing the first
// WeakCell. Like above, we're modifying pointers during GC, so record the
// slots.
- HeapObject undefined = ReadOnlyRoots(isolate()).undefined_value();
JSFinalizationRegistry finalization_registry =
JSFinalizationRegistry::cast(weak_cell.finalization_registry());
finalization_registry.RemoveUnregisterToken(
JSReceiver::cast(unregister_token), isolate(),
- [undefined](WeakCell matched_cell) {
- matched_cell.set_unregister_token(undefined);
- },
+ JSFinalizationRegistry::kKeepMatchedCellsInRegistry,
gc_notify_updated_slot);
} else {
// The unregister_token is alive.
diff --git a/src/objects/js-weak-refs-inl.h b/src/objects/js-weak-refs-inl.h
index acce7b72b9430d810b538d6d082e9b57b2a04b75..76e6e075e5ded6bbbf84605c8672b741757eb816 100644
--- a/src/objects/js-weak-refs-inl.h
+++ b/src/objects/js-weak-refs-inl.h
@@ -60,16 +60,14 @@ bool JSFinalizationRegistry::Unregister(
// key. Each WeakCell will be in the "active_cells" or "cleared_cells" list of
// its FinalizationRegistry; remove it from there.
return finalization_registry->RemoveUnregisterToken(
- *unregister_token, isolate,
- [isolate](WeakCell matched_cell) {
- matched_cell.RemoveFromFinalizationRegistryCells(isolate);
- },
+ *unregister_token, isolate, kRemoveMatchedCellsFromRegistry,
[](HeapObject, ObjectSlot, Object) {});
}
-template <typename MatchCallback, typename GCNotifyUpdatedSlotCallback>
+template <typename GCNotifyUpdatedSlotCallback>
bool JSFinalizationRegistry::RemoveUnregisterToken(
- JSReceiver unregister_token, Isolate* isolate, MatchCallback match_callback,
+ JSReceiver unregister_token, Isolate* isolate,
+ RemoveUnregisterTokenMode removal_mode,
GCNotifyUpdatedSlotCallback gc_notify_updated_slot) {
// This method is called from both FinalizationRegistry#unregister and for
// removing weakly-held dead unregister tokens. The latter is during GC so
@@ -107,7 +105,16 @@ bool JSFinalizationRegistry::RemoveUnregisterToken(
value = weak_cell.key_list_next();
if (weak_cell.unregister_token() == unregister_token) {
// weak_cell has the same unregister token; remove it from the key list.
- match_callback(weak_cell);
+ switch (removal_mode) {
+ case kRemoveMatchedCellsFromRegistry:
+ weak_cell.RemoveFromFinalizationRegistryCells(isolate);
+ break;
+ case kKeepMatchedCellsInRegistry:
+ // Do nothing.
+ break;
+ }
+ // Clear unregister token-related fields.
+ weak_cell.set_unregister_token(undefined);
weak_cell.set_key_list_prev(undefined);
weak_cell.set_key_list_next(undefined);
was_present = true;
diff --git a/src/objects/js-weak-refs.h b/src/objects/js-weak-refs.h
index 57f765b282e653122fd83e827bdd52cb9fe818cf..f678234ff81afc7a32c93bb275edd39fbe0b3891 100644
--- a/src/objects/js-weak-refs.h
+++ b/src/objects/js-weak-refs.h
@@ -43,10 +43,14 @@ class JSFinalizationRegistry
// it modifies slots in key_map and WeakCells and the normal write barrier is
// disabled during GC, we need to tell the GC about the modified slots via the
// gc_notify_updated_slot function.
- template <typename MatchCallback, typename GCNotifyUpdatedSlotCallback>
+ enum RemoveUnregisterTokenMode {
+ kRemoveMatchedCellsFromRegistry,
+ kKeepMatchedCellsInRegistry
+ };
+ template <typename GCNotifyUpdatedSlotCallback>
inline bool RemoveUnregisterToken(
JSReceiver unregister_token, Isolate* isolate,
- MatchCallback match_callback,
+ RemoveUnregisterTokenMode removal_mode,
GCNotifyUpdatedSlotCallback gc_notify_updated_slot);
// Returns true if the cleared_cells list is non-empty.
diff --git a/src/objects/objects.cc b/src/objects/objects.cc
index c2c9fe36c57603425dddbf36823489c59bcc4868..0908eb76b3610e6bdb5e7e816773a53d180f3ecf 100644
--- a/src/objects/objects.cc
+++ b/src/objects/objects.cc
@@ -6936,7 +6936,7 @@ void JSFinalizationRegistry::RemoveCellFromUnregisterTokenMap(
}
// weak_cell is now removed from the unregister token map, so clear its
- // unregister token-related fields for heap verification.
+ // unregister token-related fields.
weak_cell.set_unregister_token(undefined);
weak_cell.set_key_list_prev(undefined);
weak_cell.set_key_list_next(undefined);
diff --git a/test/cctest/test-js-weak-refs.cc b/test/cctest/test-js-weak-refs.cc
index 8974bdf6dba65be319afbfdfc66641d27ed14a07..eb6ca2d73685b437978fac642190c8d653f4c73a 100644
--- a/test/cctest/test-js-weak-refs.cc
+++ b/test/cctest/test-js-weak-refs.cc
@@ -853,9 +853,7 @@ TEST(TestRemoveUnregisterToken) {
finalization_registry->RemoveUnregisterToken(
JSReceiver::cast(*token2), isolate,
- [undefined](WeakCell matched_cell) {
- matched_cell.set_unregister_token(*undefined);
- },
+ JSFinalizationRegistry::kKeepMatchedCellsInRegistry,
[](HeapObject, ObjectSlot, Object) {});
// Both weak_cell2a and weak_cell2b remain on the weak cell chains.
@@ -1024,5 +1022,52 @@ TEST(UnregisterTokenHeapVerifier) {
EmptyMessageQueues(isolate);
}
+TEST(UnregisteredAndUnclearedCellHeapVerifier) {
+ if (!FLAG_incremental_marking) return;
+ ManualGCScope manual_gc_scope;
+#ifdef VERIFY_HEAP
+ FLAG_verify_heap = true;
+#endif
+
+ CcTest::InitializeVM();
+ v8::Isolate* isolate = CcTest::isolate();
+ Heap* heap = CcTest::heap();
+ v8::HandleScope outer_scope(isolate);
+
+ {
+ // Make a new FinalizationRegistry and register an object with a token.
+ v8::HandleScope scope(isolate);
+ CompileRun(
+ "var token = {}; "
+ "var registry = new FinalizationRegistry(function () {}); "
+ "registry.register({}, undefined, token);");
+ }
+
+ // Start incremental marking to activate the marking barrier.
+ heap::SimulateIncrementalMarking(heap, false);
+
+ {
+ // Make a WeakCell list with length >1, then unregister with the token to
+ // the WeakCell from the registry. The linked list manipulation keeps the
+ // unregistered WeakCell alive (i.e. not put into cleared_cells) due to the
+ // marking barrier from incremental marking. Then make the original token
+ // collectible.
+ v8::HandleScope scope(isolate);
+ CompileRun(
+ "registry.register({}); "
+ "registry.unregister(token); "
+ "token = 0;");
+ }
+
+ // Trigger GC.
+ CcTest::CollectAllGarbage();
+ CcTest::CollectAllGarbage();
+
+ // Pump message loop to run the finalizer task, then the incremental marking
+ // task. The verifier will verify that live WeakCells don't point to dead
+ // unregister tokens.
+ EmptyMessageQueues(isolate);
+}
+
} // namespace internal
} // namespace v8

View File

@@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tobias Tebbi <tebbi@chromium.org>
Date: Wed, 29 Jun 2022 10:18:59 +0000
Subject: Merged: [compiler] fix FrameState revisit bug in escape analysis
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
(cherry picked from commit 17da9e70833014e0a2646db5c11588f0aee02de7)
Bug: chromium:1340335, chromium:1315901
Change-Id: I81cdc6bc3d6c7441ebc333d33801329c05fbd5d4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3755103
Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
Commit-Queue: Samuel Groß <saelo@chromium.org>
Cr-Commit-Position: refs/branch-heads/10.2@{#25}
Cr-Branched-From: 374091f382e88095694c1283cbdc2acddc1b1417-refs/heads/10.2.154@{#1}
Cr-Branched-From: f0c353f6315eeb2212ba52478983a3b3af07b5b1-refs/heads/main@{#79976}
diff --git a/src/compiler/escape-analysis.cc b/src/compiler/escape-analysis.cc
index 1ee9100ecd78757e8873866dd9da05231b97439b..42c5819fa1fa9ab00a0b9573d6c13cbcf66e251b 100644
--- a/src/compiler/escape-analysis.cc
+++ b/src/compiler/escape-analysis.cc
@@ -78,6 +78,8 @@ class ReduceScope {
explicit ReduceScope(Node* node, Reduction* reduction)
: current_node_(node), reduction_(reduction) {}
+ void SetValueChanged() { reduction()->set_value_changed(); }
+
protected:
Node* current_node() const { return current_node_; }
Reduction* reduction() { return reduction_; }
@@ -806,7 +808,9 @@ void ReduceNode(const Operator* op, EscapeAnalysisTracker::Scope* current,
break;
}
case IrOpcode::kStateValues:
- // These uses are always safe.
+ // We visit StateValue nodes through their correpsonding FrameState node,
+ // so we need to make sure we revisit the FrameState.
+ current->SetValueChanged();
break;
case IrOpcode::kFrameState: {
// We mark the receiver as escaping due to the non-standard `.getThis`

View File

@@ -0,0 +1,53 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tobias Tebbi <tebbi@chromium.org>
Date: Fri, 10 Jun 2022 12:53:10 +0000
Subject: Version 10.2.154.10 (cherry-pick)
Merged 85b4b5d719c50ecc8f376ca853c9001d5ac0ed9a
[compiler] only enable MidTierRegisterAllocator for Wasm
Bug: chromium:1335054
Change-Id: I61ab97d4fbfcbb81319e611a64a6454e050a1d65
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3705397
Owners-Override: Tobias Tebbi <tebbi@chromium.org>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/10.2@{#18}
Cr-Branched-From: 374091f382e88095694c1283cbdc2acddc1b1417-refs/heads/10.2.154@{#1}
Cr-Branched-From: f0c353f6315eeb2212ba52478983a3b3af07b5b1-refs/heads/main@{#79976}
diff --git a/include/v8-version.h b/include/v8-version.h
index 7999dcb13f34b68d12660cdc0f6767e5e8bf6d18..ebd8289678fcf2d746e3a67a49c086058651fb28 100644
--- a/include/v8-version.h
+++ b/include/v8-version.h
@@ -11,7 +11,7 @@
#define V8_MAJOR_VERSION 10
#define V8_MINOR_VERSION 0
#define V8_BUILD_NUMBER 139
-#define V8_PATCH_LEVEL 17
+#define V8_PATCH_LEVEL 18
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc
index 6ea2df76c863ed0466ee96fcf1ad5736192c6b44..555942c2be2287d41d443d109e305c4858579447 100644
--- a/src/compiler/pipeline.cc
+++ b/src/compiler/pipeline.cc
@@ -3456,11 +3456,15 @@ bool PipelineImpl::SelectInstructions(Linkage* linkage) {
const RegisterConfiguration* config = RegisterConfiguration::Default();
std::unique_ptr<const RegisterConfiguration> restricted_config;
+ // The mid-tier register allocator keeps values in stack slots for too long.
+ // This is incompatible with left-trimming, therefore we cannot enable it for
+ // JS functions.
bool use_mid_tier_register_allocator =
- FLAG_turbo_force_mid_tier_regalloc ||
+ data->info()->code_kind() == CodeKind::WASM_FUNCTION &&
+ (FLAG_turbo_force_mid_tier_regalloc ||
(FLAG_turbo_use_mid_tier_regalloc_for_huge_functions &&
data->sequence()->VirtualRegisterCount() >
- kTopTierVirtualRegistersLimit);
+ kTopTierVirtualRegistersLimit));
if (call_descriptor->HasRestrictedAllocatableRegisters()) {
RegList registers = call_descriptor->AllocatableRegisters();

View File

@@ -1,2 +1,4 @@
add_thread_local_to_x_error_trap_cc.patch
cherry-pick-a18fddcb53e6.patch
cherry-pick-763d847f1e5a.patch
cherry-pick-06aea31d10f8.patch

View File

@@ -0,0 +1,38 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20Bostr=C3=B6m?= <hbos@webrtc.org>
Date: Wed, 13 Jul 2022 11:10:14 +0200
Subject: Disallow invalid arguments in RestoreEncodingLayers.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Changing DCHECK into CHECK for good measure.
(cherry picked from commit 2b1f509f3a05035a17917061a71b16114e8c72dc)
No-Try: True
Bug: chromium:1343889
Change-Id: I2cede85dc2d2a4238739f73afe25275047f4aa50
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/268460
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Cr-Original-Commit-Position: refs/heads/main@{#37511}
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/269242
Cr-Commit-Position: refs/branch-heads/5112@{#8}
Cr-Branched-From: a976a871159f85f975fbd6f170d0a8f00a4aee49-refs/heads/main@{#37168}
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc
index d1355e31ab6331058cd557b0f164c903c9bc8f46..c41904ac8395c874ea327cab9c800d9311da8dd8 100644
--- a/pc/rtp_sender.cc
+++ b/pc/rtp_sender.cc
@@ -74,8 +74,8 @@ RtpParameters RestoreEncodingLayers(
const RtpParameters& parameters,
const std::vector<std::string>& removed_rids,
const std::vector<RtpEncodingParameters>& all_layers) {
- RTC_DCHECK_EQ(parameters.encodings.size() + removed_rids.size(),
- all_layers.size());
+ RTC_CHECK_EQ(parameters.encodings.size() + removed_rids.size(),
+ all_layers.size());
RtpParameters result(parameters);
result.encodings.clear();
size_t index = 0;

View File

@@ -0,0 +1,93 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taylor Brandstetter <deadbeef@webrtc.org>
Date: Fri, 1 Jul 2022 12:20:05 -0700
Subject: Do not allow simulcast to be turned off using SDP munging
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This is an error that puts the PC into an inconsistent state, so
causing a crash is the right thing to do.
(cherry picked from commit 3fe8b0d9a980642ee5ebb1f9e429378b063c1f07)
TBR=hta@webrtc.org
Bug: chromium:1341043
Change-Id: Ie1eb89400ad87f0c83634b7073236b07e92ec7ab
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/267281
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Original-Commit-Position: refs/heads/main@{#37391}
No-Presubmit: true
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/267427
Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Guido Urdaneta <guidou@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/branch-heads/5005@{#9}
Cr-Branched-From: 8f9b44ba38c134e1c69126196e4d9e566ca5d5e3-refs/heads/main@{#36552}
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc
index 110b5aae0a01a5d612eefdc3262b20e4dd5504fe..d1355e31ab6331058cd557b0f164c903c9bc8f46 100644
--- a/pc/rtp_sender.cc
+++ b/pc/rtp_sender.cc
@@ -299,8 +299,8 @@ void RtpSenderBase::SetSsrc(uint32_t ssrc) {
// we need to copy.
RtpParameters current_parameters =
media_channel_->GetRtpSendParameters(ssrc_);
- RTC_DCHECK_GE(current_parameters.encodings.size(),
- init_parameters_.encodings.size());
+ RTC_CHECK_GE(current_parameters.encodings.size(),
+ init_parameters_.encodings.size());
for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) {
init_parameters_.encodings[i].ssrc =
current_parameters.encodings[i].ssrc;
diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc
index 2c6bc7b71a1e5e30609f382fee0b4cc30f03512e..388edbaf73f0570d895b4855bc2207afb8eff1a5 100644
--- a/pc/rtp_sender_receiver_unittest.cc
+++ b/pc/rtp_sender_receiver_unittest.cc
@@ -1157,6 +1157,44 @@ TEST_F(RtpSenderReceiverTest,
DestroyVideoRtpSender();
}
+#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+using RtpSenderReceiverDeathTest = RtpSenderReceiverTest;
+
+TEST_F(RtpSenderReceiverDeathTest,
+ VideoSenderManualRemoveSimulcastFailsDeathTest) {
+ AddVideoTrack(false);
+
+ std::unique_ptr<MockSetStreamsObserver> set_streams_observer =
+ std::make_unique<MockSetStreamsObserver>();
+ video_rtp_sender_ = VideoRtpSender::Create(worker_thread_, video_track_->id(),
+ set_streams_observer.get());
+ ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get()));
+ EXPECT_CALL(*set_streams_observer, OnSetStreams());
+ video_rtp_sender_->SetStreams({local_stream_->id()});
+
+ std::vector<RtpEncodingParameters> init_encodings(2);
+ init_encodings[0].max_bitrate_bps = 60000;
+ init_encodings[1].max_bitrate_bps = 120000;
+ video_rtp_sender_->set_init_send_encodings(init_encodings);
+
+ RtpParameters params = video_rtp_sender_->GetParameters();
+ ASSERT_EQ(2u, params.encodings.size());
+ EXPECT_EQ(params.encodings[0].max_bitrate_bps, 60000);
+
+ // Simulate the setLocalDescription call as if the user used SDP munging
+ // to disable simulcast.
+ std::vector<uint32_t> ssrcs;
+ ssrcs.reserve(2);
+ for (int i = 0; i < 2; ++i)
+ ssrcs.push_back(kVideoSsrcSimulcast + i);
+ cricket::StreamParams stream_params =
+ cricket::StreamParams::CreateLegacy(kVideoSsrc);
+ video_media_channel_->AddSendStream(stream_params);
+ video_rtp_sender_->SetMediaChannel(video_media_channel_);
+ EXPECT_DEATH(video_rtp_sender_->SetSsrc(kVideoSsrcSimulcast), "");
+}
+#endif
+
TEST_F(RtpSenderReceiverTest,
VideoSenderMustCallGetParametersBeforeSetParametersBeforeNegotiation) {
video_rtp_sender_ =

Some files were not shown because too many files have changed in this diff Show More