Compare commits

..

42 Commits

Author SHA1 Message Date
Sudowoodo Release Bot
605ee9e28a Bump v21.0.0-beta.3 2022-09-06 15:03:08 -07:00
Samuel Attard
e48878ea63 chore: cherry-pick 9b5207569882 from chromium (#35543)
* chore: cherry-pick 9b5207569882 from chromium

* chore: update patches

Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
2022-09-06 10:27:46 -07:00
trop[bot]
e78aa6af16 fix: screen.getCursorScreenPoint() crash on Wayland (#35575)
fix: screen.getCursorScreenPoint() crash on Wayland

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-09-06 12:18:03 -04:00
Sudowoodo Release Bot
fb0bbf0100 Bump v21.0.0-beta.2 2022-09-01 06:31:01 -07:00
trop[bot]
5e8b4bd86f fix: crash when switching origins with emulation settings set (#35488)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-08-31 11:20:10 +09:00
Sudowoodo Release Bot
da47103ad7 Bump v21.0.0-beta.1 2022-08-30 10:11:56 -07:00
trop[bot]
7b62386296 fix: crash on WebWorker destruction (#35492)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-08-30 13:58:28 +02:00
trop[bot]
018bc0993e chore: use nghttp2's config.h on all platforms (#35487)
https://github.com/nodejs/node/pull/27283

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-08-30 10:57:32 +02:00
Sudowoodo Release Bot
f9b02358c6 Bump v21.0.0-alpha.6 2022-08-29 15:51:14 -07:00
trop[bot]
23aa6e5084 build: update libcxx filenames (#35497)
* build: update libcxx filenames

* build: change upload_to_storage variable

Co-authored-by: VerteDinde <vertedinde@electronjs.org>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
2022-08-29 15:50:06 -07:00
Sudowoodo Release Bot
65a2c18560 Revert "Bump v21.0.0-alpha.6"
This reverts commit c8b3e23f31.
2022-08-29 10:36:52 -07:00
Sudowoodo Release Bot
c8b3e23f31 Bump v21.0.0-alpha.6 2022-08-29 09:19:34 -07:00
trop[bot]
4ab9295e62 chore: delete implicit fallthrough patch (#35469)
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-08-29 15:25:38 +02:00
trop[bot]
0e79733404 docs: update docs description to match sidebar (#35482)
Update introduction.md

Co-authored-by: Nik K <nk@nikk.ca>
2022-08-29 12:28:21 +02:00
trop[bot]
38d408a26b fix: pass rfh instances through to the permission helper (#35467)
* fix: pass rfh instances through to the permission helper

* refactor: use WeakDocumentPtr instead of frame node id

* fix: handle missing initiator document

* fix: dispatch openExternal event for top level webview navs still

Co-authored-by: Samuel Attard <sattard@salesforce.com>
2022-08-29 09:52:53 +02:00
trop[bot]
0ad176fd13 fix: fullscreen crashing with roundedCorners: false (#35454)
fix: fullscreen crashing with roundedCorners

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-08-25 15:52:24 +02:00
trop[bot]
a3cfd1a206 fix: Node.js atob input validation (#35443)
fix: Node.js atob input validation

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-08-25 15:08:20 +02:00
trop[bot]
63fe34c703 build: fix export patches to work when source directory does not exist (#35437)
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-08-25 10:18:11 +09:00
trop[bot]
c99a52eab4 chore: bump chromium to 106.0.5216.0 (21-x-y) (#35363)
* chore: bump chromium to 106.0.5216.0 (main) (#34993)

* Trigger Build

Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-08-23 15:22:47 -04:00
trop[bot]
95eb6ea772 fix: undefined details.requestingUrl from session.setPermissionCheckHandler (#35409)
fix: undefined details.requestingUrl from setPermissionCheckHandler

Co-authored-by: deepak1556 <hop2deep@gmail.com>
2022-08-23 10:27:14 -04:00
trop[bot]
8d6c29ecbc fix: add restore event for minimized maximization (#35410)
Co-authored-by: mlaurencin <mlaurencin@electronjs.org>
2022-08-23 14:39:46 +02:00
trop[bot]
637824e086 fix: promise support with webFrameMain.executeJavaScript (#35359)
* fix: promise support with webFrameMain.executeJavaScript

* chore: reject when result is an error

Co-authored-by: deepak1556 <hop2deep@gmail.com>
2022-08-23 11:39:59 +02:00
trop[bot]
cb6fa8268f fix: ensure chrome colors are initialized (#35401)
* fix: ensure chrome colors are initialized

* build: fix linking on windows

* build: fix linking on windows

* build: add needed files to chromium_src/BUILD

Co-authored-by: Samuel Attard <sattard@salesforce.com>
Co-authored-by: VerteDinde <keeleymhammond@gmail.com>
2022-08-23 11:19:32 +02:00
Sudowoodo Release Bot
76e8536ca8 Bump v21.0.0-alpha.5 2022-08-22 16:25:16 -07:00
trop[bot]
b075434447 fix: IPC emit order in -ipc-ports handler (#35364)
Co-authored-by: Milan Burda <miburda@microsoft.com>
2022-08-18 18:00:45 -07:00
Sudowoodo Release Bot
9673d29ebe Bump v21.0.0-alpha.4 2022-08-18 12:05:12 -07:00
trop[bot]
5e6d152231 fix: don't bypass redirect checks (#35366)
Co-authored-by: Jeremy Rose <japthorp@slack-corp.com>
2022-08-18 10:09:18 -07:00
trop[bot]
b31a4b4394 fix: Frameless window shows frame while opening (#35353)
* fix: Frameless window shows frame while opening

* Clarify comments

* Inline setter

* Edit comment

Co-authored-by: Raymond Zhao <7199958+rzhao271@users.noreply.github.com>
2022-08-17 15:20:20 -07:00
trop[bot]
28ca18a912 feat: add WebContents.ipc (#35231)
feat: add WebContents.ipc (#34959)

Co-authored-by: Jeremy Rose <jeremya@chromium.org>
2022-08-17 09:50:15 -07:00
trop[bot]
8caea9006a fix: add uv_loop_close when object release to fix crash (#35336)
Co-authored-by: yangzuohui <yangzuohui@bytedance.com>
2022-08-16 17:03:05 +09:00
trop[bot]
855536bcb9 refactor: simplify Browser::SetLoginItemSettings (#35329)
refactor: simplify Browser::SetLoginItemSettings

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-08-15 16:43:48 -04:00
trop[bot]
1978a0b5fe fix: serialPort.open() failing (#35339)
fix: serialPort.open() failing

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2022-08-15 13:53:00 -04:00
Sudowoodo Release Bot
fe70152b43 Bump v21.0.0-alpha.3 2022-08-15 08:42:41 -07:00
trop[bot]
9b38a25206 docs: add Electron deps to license credits file (#35332)
* docs: add Electron deps to license credits file

* fixup! docs: add Electron deps to license credits file

remove nan; it is dev-only

Co-authored-by: Charles Kerr <charles@charleskerr.com>
2022-08-15 10:42:39 -04:00
trop[bot]
a66d3bf206 docs: removes unused import in preload script (#35334)
* Remove unused import of path

This import gives out the error in the preload script:

Error: module not found: path
    at preloadRequire

* Remove import path as it is not used in the script

note: Removes import path as it is not used in the script

Co-authored-by: Rhitik Bhatt <bhattrhitik95@gmail.com>
2022-08-15 17:06:50 +09:00
Sudowoodo Release Bot
752b10f16f Bump v21.0.0-alpha.2 2022-08-11 09:37:32 -07:00
trop[bot]
5803a67963 docs: fix getStoragePath return type (#35296)
Fixes #35255

Co-authored-by: Samuel Attard <sam@electronjs.org>
2022-08-10 22:42:17 +02:00
trop[bot]
40058af821 fix(docs): fix a typo in section on debugging with VSCode (#35285)
fix(docs): fix a typo

Co-authored-by: Trang Le <trang.thule@zoho.com>
2022-08-09 15:30:29 -07:00
trop[bot]
64b40ea69c docs: update tray docs with info for mac menubar icons (#35221)
Co-authored-by: Brad Carter <16466430+carterbs@users.noreply.github.com>
2022-08-09 09:01:10 +09:00
trop[bot]
7c6e16975f fix: app.relaunch loses args when execPath specified (#35254)
fix: app.relaunch loses args when execPath specified (#35108)

Co-authored-by: Aaron Meriwether <me@ameriwether.com>
2022-08-09 08:54:58 +09:00
trop[bot]
c9e7f33f7e test: temporarily disable tests on mas arm64 that are causing a crash (#35247)
* test: temporarily disable test on mas arm64 that is causing crash

* disable the right test

* chore: speculative fix for CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER crash

* enable all the tests

* Revert "chore: speculative fix for CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER crash"

This reverts commit b7c8ef364c.

* test: disable tests that crash on mas arm64

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2022-08-08 10:02:58 -04:00
Sudowoodo Release Bot
98aa0ef090 Bump v21.0.0-alpha.1 2022-08-03 07:37:56 -07:00
400 changed files with 9682 additions and 6916 deletions

View File

@@ -991,7 +991,7 @@ step-ts-compile: &step-ts-compile
do
out="${f:29}"
if [ "$out" != "base.js" ]; then
node script/yarn webpack --config $f --output-filename=$out --output-path=./.tmp --env mode=development
node script/yarn webpack --config $f --output-filename=$out --output-path=./.tmp --env.mode=development
fi
done
@@ -1061,15 +1061,18 @@ steps-tests: &steps-tests
export MOCHA_TIMEOUT=180000
echo "Piping output to ASAN_SYMBOLIZE ($ASAN_SYMBOLIZE)"
(cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging --files $(circleci tests glob spec-main/*-spec.ts | circleci tests split --split-by=timings)) 2>&1 | $ASAN_SYMBOLIZE
(cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging --files $(circleci tests glob spec/*-spec.js | circleci tests split --split-by=timings)) 2>&1 | $ASAN_SYMBOLIZE
else
if [ "$TARGET_ARCH" == "arm" ] || [ "$TARGET_ARCH" == "arm64" ]; then
export ELECTRON_SKIP_NATIVE_MODULE_TESTS=true
(cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging)
(cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging)
else
if [ "$TARGET_ARCH" == "ia32" ]; then
npm_config_arch=x64 node electron/node_modules/dugite/script/download-git.js
fi
(cd electron && node script/yarn test --runners=main --trace-uncaught --enable-logging --files $(circleci tests glob spec-main/*-spec.ts | circleci tests split --split-by=timings))
(cd electron && node script/yarn test --runners=remote --trace-uncaught --enable-logging --files $(circleci tests glob spec/*-spec.js | circleci tests split --split-by=timings))
fi
fi
- run:
@@ -1078,6 +1081,9 @@ steps-tests: &steps-tests
cd src
# Check if test results exist and are not empty.
if [ ! -s "junit/test-results-remote.xml" ]; then
exit 1
fi
if [ ! -s "junit/test-results-main.xml" ]; then
exit 1
fi

View File

@@ -117,6 +117,21 @@ jobs:
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

4
.gitignore vendored
View File

@@ -35,7 +35,7 @@ electron-api.json
electron.d.ts
# Spec hash calculation
spec-main/.hash
spec/.hash
# Eslint Cache
.eslintcache*
@@ -53,4 +53,4 @@ ts-gen
# Used to accelerate builds after sync
patches/mtime-cache.json
spec-main/fixtures/logo.png
spec/fixtures/logo.png

View File

@@ -1 +1 @@
22.0.0-nightly.20220825
21.0.0-beta.3

View File

@@ -5,7 +5,7 @@
[![Electron Discord Invite](https://img.shields.io/discord/745037351163527189?color=%237289DA&label=chat&logo=discord&logoColor=white)](https://discord.gg/electronjs)
:memo: Available Translations: 🇨🇳 🇧🇷 🇪🇸 🇯🇵 🇷🇺 🇫🇷 🇺🇸 🇩🇪.
View these docs in other languages on our [Crowdin](https://crowdin.com/project/electron) project.
View these docs in other languages at [electron/i18n](https://github.com/electron/i18n/tree/master/content/).
The Electron framework lets you write cross-platform desktop applications
using JavaScript, HTML and CSS. It is based on [Node.js](https://nodejs.org/) and

View File

@@ -34,281 +34,213 @@ environment:
MOCHA_REPORTER: mocha-multi-reporters
MOCHA_MULTI_REPORTERS: mocha-appveyor-reporter, tap
GOMA_FALLBACK_ON_AUTH_FAILURE: true
matrix:
- job_name: Build
- job_name: Test
job_depends_on: Build
clone_folder: C:\projects\src\electron
# the first failed job cancels other jobs and fails entire build
matrix:
fast_finish: true
for:
-
matrix:
only:
- job_name: Build
init:
- ps: >-
if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
Write-warning "Skipping PR build for branch"; Exit-AppveyorBuild
}
build_script:
- ps: |
build_script:
- ps: >-
if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
Write-warning "Skipping PR build for branch"; Exit-AppveyorBuild
} else {
node script/yarn.js install --frozen-lockfile
node script/doc-only-change.js --prNumber=$env:APPVEYOR_PULL_REQUEST_NUMBER --prBranch=$env:APPVEYOR_REPO_BRANCH
if ($LASTEXITCODE -eq 0) {
Write-warning "Skipping tests for doc only change"; Exit-AppveyorBuild
$result = node script/doc-only-change.js --prNumber=$env:APPVEYOR_PULL_REQUEST_NUMBER --prBranch=$env:APPVEYOR_REPO_BRANCH
Write-Output $result
if ($result.ExitCode -eq 0) {
Write-warning "Skipping build for doc only change"; Exit-AppveyorBuild
}
$global:LASTEXITCODE = 0
- cd ..
- ps: Write-Host "Building $env:GN_CONFIG build"
- git config --global core.longpaths true
- update_depot_tools.bat
- ps: >-
if (Test-Path 'env:RAW_GOMA_AUTH') {
$env:GOMA_OAUTH2_CONFIG_FILE = "$pwd\.goma_oauth2_config"
$env:RAW_GOMA_AUTH | Set-Content $env:GOMA_OAUTH2_CONFIG_FILE
}
- git clone https://github.com/electron/build-tools.git
- cd build-tools
- npm install
- mkdir third_party
- ps: >-
node -e "require('./src/utils/goma.js').downloadAndPrepare({ gomaOneForAll: true })"
- ps: $env:GN_GOMA_FILE = node -e "console.log(require('./src/utils/goma.js').gnFilePath)"
- ps: $env:LOCAL_GOMA_DIR = node -e "console.log(require('./src/utils/goma.js').dir)"
- cd ..\..
- ps: .\src\electron\script\start-goma.ps1 -gomaDir $env:LOCAL_GOMA_DIR
- ps: >-
if (Test-Path 'env:RAW_GOMA_AUTH') {
$goma_login = python $env:LOCAL_GOMA_DIR\goma_auth.py info
if ($goma_login -eq 'Login as Fermi Planck') {
Write-warning "Goma authentication is correct";
} else {
Write-warning "WARNING!!!!!! Goma authentication is incorrect; please update Goma auth token.";
$host.SetShouldExit(1)
}
}
- ps: $env:CHROMIUM_BUILDTOOLS_PATH="$pwd\src\buildtools"
- ps: >-
if ($env:GN_CONFIG -ne 'release') {
$env:NINJA_STATUS="[%r processes, %f/%t @ %o/s : %es] "
}
- >-
gclient config
--name "src\electron"
--unmanaged
%GCLIENT_EXTRA_ARGS%
"https://github.com/electron/electron"
- ps: >-
if ($env:GN_CONFIG -eq 'release') {
$env:RUN_GCLIENT_SYNC="true"
}
- echo "Building $env:GN_CONFIG build"
- git config --global core.longpaths true
- cd ..
- mkdir src
- update_depot_tools.bat
- ps: Move-Item $env:APPVEYOR_BUILD_FOLDER -Destination src\electron
- ps: >-
if (Test-Path 'env:RAW_GOMA_AUTH') {
$env:GOMA_OAUTH2_CONFIG_FILE = "$pwd\.goma_oauth2_config"
$env:RAW_GOMA_AUTH | Set-Content $env:GOMA_OAUTH2_CONFIG_FILE
}
- git clone https://github.com/electron/build-tools.git
- cd build-tools
- npm install
- mkdir third_party
- ps: >-
node -e "require('./src/utils/goma.js').downloadAndPrepare({ gomaOneForAll: true })"
- ps: $env:GN_GOMA_FILE = node -e "console.log(require('./src/utils/goma.js').gnFilePath)"
- ps: $env:LOCAL_GOMA_DIR = node -e "console.log(require('./src/utils/goma.js').dir)"
- cd ..
- ps: .\src\electron\script\start-goma.ps1 -gomaDir $env:LOCAL_GOMA_DIR
- ps: >-
if (Test-Path 'env:RAW_GOMA_AUTH') {
$goma_login = python $env:LOCAL_GOMA_DIR\goma_auth.py info
if ($goma_login -eq 'Login as Fermi Planck') {
Write-warning "Goma authentication is correct";
} else {
cd src\electron
node script\generate-deps-hash.js
$depshash = Get-Content .\.depshash -Raw
$zipfile = "Z:\$depshash.7z"
cd ..\..
if (Test-Path -Path $zipfile) {
# file exists, unzip and then gclient sync
7z x -y $zipfile -mmt=14 -aoa
if (-not (Test-Path -Path "src\buildtools")) {
# the zip file must be corrupt - resync
$env:RUN_GCLIENT_SYNC="true"
if ($env:TARGET_ARCH -ne 'ia32') {
# only save on x64/woa to avoid contention saving
$env:SAVE_GCLIENT_SRC="true"
}
} else {
# update angle
cd src\third_party\angle
git remote set-url origin https://chromium.googlesource.com/angle/angle.git
git fetch
cd ..\..\..
}
} else {
# file does not exist, gclient sync, then zip
Write-warning "WARNING!!!!!! Goma authentication is incorrect; please update Goma auth token.";
$host.SetShouldExit(1)
}
}
- ps: $env:CHROMIUM_BUILDTOOLS_PATH="$pwd\src\buildtools"
- ps: >-
if ($env:GN_CONFIG -ne 'release') {
$env:NINJA_STATUS="[%r processes, %f/%t @ %o/s : %es] "
}
- >-
gclient config
--name "src\electron"
--unmanaged
%GCLIENT_EXTRA_ARGS%
"https://github.com/electron/electron"
- ps: >-
if ($env:GN_CONFIG -eq 'release') {
$env:RUN_GCLIENT_SYNC="true"
} else {
cd src\electron
node script\generate-deps-hash.js
$depshash = Get-Content .\.depshash -Raw
$zipfile = "Z:\$depshash.7z"
cd ..\..
if (Test-Path -Path $zipfile) {
# file exists, unzip and then gclient sync
7z x -y $zipfile -mmt=30 -aoa
if (-not (Test-Path -Path "src\buildtools")) {
# the zip file must be corrupt - resync
$env:RUN_GCLIENT_SYNC="true"
if ($env:TARGET_ARCH -ne 'ia32') {
# only save on x64/woa to avoid contention saving
$env:SAVE_GCLIENT_SRC="true"
}
} else {
# update angle
cd src\third_party\angle
git remote set-url origin https://chromium.googlesource.com/angle/angle.git
git fetch
cd ..\..\..
}
} else {
# file does not exist, gclient sync, then zip
$env:RUN_GCLIENT_SYNC="true"
if ($env:TARGET_ARCH -ne 'ia32') {
# only save on x64/woa to avoid contention saving
$env:SAVE_GCLIENT_SRC="true"
}
}
- if "%RUN_GCLIENT_SYNC%"=="true" ( gclient sync )
- ps: >-
if ($env:SAVE_GCLIENT_SRC -eq 'true') {
# archive current source for future use
# only run on x64/woa to avoid contention saving
$(7z a $zipfile src -xr!android_webview -xr!electron -xr'!*\.git' -xr!third_party\WebKit\LayoutTests! -xr!third_party\blink\web_tests -xr!third_party\blink\perf_tests -slp -t7z -mmt=30)
if ($LASTEXITCODE -ne 0) {
Write-warning "Could not save source to shared drive; continuing anyway"
}
# build time generation of file gen/angle/angle_commit.h depends on
# third_party/angle/.git
# https://chromium-review.googlesource.com/c/angle/angle/+/2074924
$(7z a $zipfile src\third_party\angle\.git)
if ($LASTEXITCODE -ne 0) {
Write-warning "Failed to add third_party\angle\.git; continuing anyway"
}
# build time generation of file dawn/common/Version_autogen.h depends on third_party/dawn/.git/HEAD
# https://dawn-review.googlesource.com/c/dawn/+/83901
$(7z a $zipfile src\third_party\dawn\.git)
if ($LASTEXITCODE -ne 0) {
Write-warning "Failed to add third_party\dawn\.git; continuing anyway"
}
}
- if "%RUN_GCLIENT_SYNC%"=="true" ( gclient sync )
- ps: >-
if ($env:SAVE_GCLIENT_SRC -eq 'true') {
# archive current source for future use
# only run on x64/woa to avoid contention saving
$(7z a $zipfile src -xr!android_webview -xr!electron -xr'!*\.git' -xr!third_party\WebKit\LayoutTests! -xr!third_party\blink\web_tests -xr!third_party\blink\perf_tests -slp -t7z -mmt=30)
if ($LASTEXITCODE -ne 0) {
Write-warning "Could not save source to shared drive; continuing anyway"
}
- cd src
- set BUILD_CONFIG_PATH=//electron/build/args/%GN_CONFIG%.gn
- gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") import(\"%GN_GOMA_FILE%\") %GN_EXTRA_ARGS% "
- gn check out/Default //electron:electron_lib
- gn check out/Default //electron:electron_app
- gn check out/Default //electron/shell/common/api:mojo
- if DEFINED GN_GOMA_FILE (ninja -j 300 -C out/Default electron:electron_app) else (ninja -C out/Default electron:electron_app)
- if "%GN_CONFIG%"=="testing" ( python C:\depot_tools\post_build_ninja_summary.py -C out\Default )
- gn gen out/ffmpeg "--args=import(\"//electron/build/args/ffmpeg.gn\") %GN_EXTRA_ARGS%"
- ninja -C out/ffmpeg electron:electron_ffmpeg_zip
- ninja -C out/Default electron:electron_dist_zip
- ninja -C out/Default shell_browser_ui_unittests
- gn desc out/Default v8:run_mksnapshot_default args > out/Default/mksnapshot_args
- ninja -C out/Default electron:electron_mksnapshot_zip
- cd out\Default
- 7z a mksnapshot.zip mksnapshot_args gen\v8\embedded.S
- cd ..\..
- ninja -C out/Default electron:hunspell_dictionaries_zip
- ninja -C out/Default electron:electron_chromedriver_zip
- ninja -C out/Default third_party/electron_node:headers
- python %LOCAL_GOMA_DIR%\goma_ctl.py stat
- python3 electron/build/profile_toolchain.py --output-json=out/Default/windows_toolchain_profile.json
- 7z a node_headers.zip out\Default\gen\node_headers
- 7z a builtins-pgo.zip v8\tools\builtins-pgo
- ps: >-
if ($env:GN_CONFIG -eq 'release') {
# Needed for msdia140.dll on 64-bit windows
$env:Path += ";$pwd\third_party\llvm-build\Release+Asserts\bin"
ninja -C out/Default electron:electron_symbols
# build time generation of file gen/angle/angle_commit.h depends on
# third_party/angle/.git
# https://chromium-review.googlesource.com/c/angle/angle/+/2074924
$(7z a $zipfile src\third_party\angle\.git)
if ($LASTEXITCODE -ne 0) {
Write-warning "Failed to add third_party\angle\.git; continuing anyway"
}
- ps: >-
if ($env:GN_CONFIG -eq 'release') {
python electron\script\zip-symbols.py
appveyor-retry appveyor PushArtifact out/Default/symbols.zip
# build time generation of file dawn/common/Version_autogen.h depends on third_party/dawn/.git/HEAD
# https://dawn-review.googlesource.com/c/dawn/+/83901
$(7z a $zipfile src\third_party\dawn\.git)
if ($LASTEXITCODE -ne 0) {
Write-warning "Failed to add third_party\dawn\.git; continuing anyway"
}
}
- cd src
- set BUILD_CONFIG_PATH=//electron/build/args/%GN_CONFIG%.gn
- gn gen out/Default "--args=import(\"%BUILD_CONFIG_PATH%\") import(\"%GN_GOMA_FILE%\") %GN_EXTRA_ARGS% "
- gn check out/Default //electron:electron_lib
- gn check out/Default //electron:electron_app
- gn check out/Default //electron/shell/common/api:mojo
- if DEFINED GN_GOMA_FILE (ninja -j 300 -C out/Default electron:electron_app) else (ninja -C out/Default electron:electron_app)
- if "%GN_CONFIG%"=="testing" ( python C:\depot_tools\post_build_ninja_summary.py -C out\Default )
- gn gen out/ffmpeg "--args=import(\"//electron/build/args/ffmpeg.gn\") %GN_EXTRA_ARGS%"
- ninja -C out/ffmpeg electron:electron_ffmpeg_zip
- ninja -C out/Default electron:electron_dist_zip
- ninja -C out/Default shell_browser_ui_unittests
- gn desc out/Default v8:run_mksnapshot_default args > out/Default/mksnapshot_args
- ninja -C out/Default electron:electron_mksnapshot_zip
- cd out\Default
- 7z a mksnapshot.zip mksnapshot_args gen\v8\embedded.S
- cd ..\..
- ninja -C out/Default electron:hunspell_dictionaries_zip
- ninja -C out/Default electron:electron_chromedriver_zip
- ninja -C out/Default third_party/electron_node:headers
- python %LOCAL_GOMA_DIR%\goma_ctl.py stat
- python3 electron/build/profile_toolchain.py --output-json=out/Default/windows_toolchain_profile.json
- 7z a node_headers.zip out\Default\gen\node_headers
- ps: >-
if ($env:GN_CONFIG -eq 'release') {
# Needed for msdia140.dll on 64-bit windows
$env:Path += ";$pwd\third_party\llvm-build\Release+Asserts\bin"
ninja -C out/Default electron:electron_symbols
}
- ps: >-
if ($env:GN_CONFIG -eq 'release') {
python electron\script\zip-symbols.py
appveyor-retry appveyor PushArtifact out/Default/symbols.zip
} else {
# It's useful to have pdb files when debugging testing builds that are
# built on CI.
7z a pdb.zip out\Default\*.pdb
}
- python electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.win.%TARGET_ARCH%.manifest
test_script:
# Workaround for https://github.com/appveyor/ci/issues/2420
- set "PATH=%PATH%;C:\Program Files\Git\mingw64\libexec\git-core"
- ps: >-
if ((-Not (Test-Path Env:\TEST_WOA)) -And (-Not (Test-Path Env:\ELECTRON_RELEASE)) -And ($env:GN_CONFIG -in "testing", "release")) {
$env:RUN_TESTS="true"
}
- ps: >-
if ($env:RUN_TESTS -eq 'true') {
New-Item .\out\Default\gen\node_headers\Release -Type directory
Copy-Item -path .\out\Default\electron.lib -destination .\out\Default\gen\node_headers\Release\node.lib
} else {
echo "Skipping tests for $env:GN_CONFIG build"
}
- cd electron
- if "%RUN_TESTS%"=="true" ( echo Running main test suite & node script/yarn test -- --trace-uncaught --runners=main --enable-logging=file --log-file=%cd%\electron.log )
- if "%RUN_TESTS%"=="true" ( echo Running remote test suite & node script/yarn test -- --trace-uncaught --runners=remote --runTestFilesSeparately --enable-logging=file --log-file=%cd%\electron.log )
- if "%RUN_TESTS%"=="true" ( echo Running native test suite & node script/yarn test -- --trace-uncaught --runners=native --enable-logging=file --log-file=%cd%\electron.log )
- cd ..
- if "%RUN_TESTS%"=="true" ( echo Verifying non proprietary ffmpeg & python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg )
- echo "About to verify mksnapshot"
- if "%RUN_TESTS%"=="true" ( echo Verifying mksnapshot & python electron\script\verify-mksnapshot.py --build-dir out\Default --source-root %cd% )
- echo "Done verifying mksnapshot"
- if "%RUN_TESTS%"=="true" ( echo Verifying chromedriver & python electron\script\verify-chromedriver.py --build-dir out\Default --source-root %cd% )
- echo "Done verifying chromedriver"
deploy_script:
- cd electron
- ps: >-
if (Test-Path Env:\ELECTRON_RELEASE) {
if (Test-Path Env:\UPLOAD_TO_STORAGE) {
Write-Output "Uploading Electron release distribution to azure"
& python script\release\uploaders\upload.py --verbose --upload_to_storage
} else {
# It's useful to have pdb files when debugging testing builds that are
# built on CI.
7z a pdb.zip out\Default\*.pdb
Write-Output "Uploading Electron release distribution to github releases"
& python script\release\uploaders\upload.py --verbose
}
- python electron/script/zip_manifests/check-zip-manifest.py out/Default/dist.zip electron/script/zip_manifests/dist_zip.win.%TARGET_ARCH%.manifest
deploy_script:
- cd electron
- ps: >-
if (Test-Path Env:\ELECTRON_RELEASE) {
if (Test-Path Env:\UPLOAD_TO_STORAGE) {
Write-Output "Uploading Electron release distribution to azure"
& python script\release\uploaders\upload.py --verbose --upload_to_storage
} else {
Write-Output "Uploading Electron release distribution to github releases"
& python script\release\uploaders\upload.py --verbose
}
} elseif (Test-Path Env:\TEST_WOA) {
node script/release/ci-release-build.js --job=electron-woa-testing --ci=GHA --appveyorJobId=$env:APPVEYOR_JOB_ID $env:APPVEYOR_REPO_BRANCH
}
on_finish:
# Uncomment this lines to enable RDP
#- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
- cd ..
- if exist out\Default\windows_toolchain_profile.json ( appveyor-retry appveyor PushArtifact out\Default\windows_toolchain_profile.json )
- if exist out\Default\dist.zip (appveyor-retry appveyor PushArtifact out\Default\dist.zip)
- if exist out\Default\shell_browser_ui_unittests.exe (appveyor-retry appveyor PushArtifact out\Default\shell_browser_ui_unittests.exe)
- if exist out\Default\chromedriver.zip (appveyor-retry appveyor PushArtifact out\Default\chromedriver.zip)
- if exist out\ffmpeg\ffmpeg.zip (appveyor-retry appveyor PushArtifact out\ffmpeg\ffmpeg.zip)
- if exist node_headers.zip (appveyor-retry appveyor PushArtifact node_headers.zip)
- if exist out\Default\mksnapshot.zip (appveyor-retry appveyor PushArtifact out\Default\mksnapshot.zip)
- if exist out\Default\hunspell_dictionaries.zip (appveyor-retry appveyor PushArtifact out\Default\hunspell_dictionaries.zip)
- if exist out\Default\electron.lib (appveyor-retry appveyor PushArtifact out\Default\electron.lib)
- if exist builtins-pgo.zip (appveyor-retry appveyor PushArtifact builtins-pgo.zip)
- ps: >-
if ((Test-Path "pdb.zip") -And ($env:GN_CONFIG -ne 'release')) {
appveyor-retry appveyor PushArtifact pdb.zip
}
} elseif (Test-Path Env:\TEST_WOA) {
node script/release/ci-release-build.js --job=electron-woa-testing --ci=GHA --appveyorJobId=$env:APPVEYOR_JOB_ID $env:APPVEYOR_REPO_BRANCH
}
on_finish:
# Uncomment this lines to enable RDP
#- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
- cd ..
- if exist out\Default\windows_toolchain_profile.json ( appveyor-retry appveyor PushArtifact out\Default\windows_toolchain_profile.json )
- if exist out\Default\dist.zip (appveyor-retry appveyor PushArtifact out\Default\dist.zip)
- if exist out\Default\shell_browser_ui_unittests.exe (appveyor-retry appveyor PushArtifact out\Default\shell_browser_ui_unittests.exe)
- if exist out\Default\chromedriver.zip (appveyor-retry appveyor PushArtifact out\Default\chromedriver.zip)
- if exist out\ffmpeg\ffmpeg.zip (appveyor-retry appveyor PushArtifact out\ffmpeg\ffmpeg.zip)
- if exist node_headers.zip (appveyor-retry appveyor PushArtifact node_headers.zip)
- if exist out\Default\mksnapshot.zip (appveyor-retry appveyor PushArtifact out\Default\mksnapshot.zip)
- if exist out\Default\hunspell_dictionaries.zip (appveyor-retry appveyor PushArtifact out\Default\hunspell_dictionaries.zip)
- if exist out\Default\electron.lib (appveyor-retry appveyor PushArtifact out\Default\electron.lib)
- ps: >-
if ((Test-Path "pdb.zip") -And ($env:GN_CONFIG -ne 'release')) {
appveyor-retry appveyor PushArtifact pdb.zip
}
-
matrix:
only:
- job_name: Test
init:
- ps: |
if ($env:RUN_TESTS -ne 'true') {
Write-warning "Skipping tests for $env:APPVEYOR_PROJECT_NAME"; Exit-AppveyorBuild
}
if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
Write-warning "Skipping PR build for branch"; Exit-AppveyorBuild
}
build_script:
- ps: |
node script/yarn.js install --frozen-lockfile
node script/doc-only-change.js --prNumber=$env:APPVEYOR_PULL_REQUEST_NUMBER --prBranch=$env:APPVEYOR_REPO_BRANCH
if ($LASTEXITCODE -eq 0) {
Write-warning "Skipping tests for doc only change"; Exit-AppveyorBuild
}
$global:LASTEXITCODE = 0
- ps: |
cd ..
mkdir out\Default
cd ..
# Download build artifacts
$apiUrl = 'https://ci.appveyor.com/api'
$build_info = Invoke-RestMethod -Method Get -Uri "$apiUrl/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/builds/$env:APPVEYOR_BUILD_ID"
$artifacts_to_download = @('dist.zip','shell_browser_ui_unittests.exe','chromedriver.zip','ffmpeg.zip','node_headers.zip','mksnapshot.zip','electron.lib','builtins-pgo.zip')
foreach ($job in $build_info.build.jobs) {
if ($job.name -eq "Build") {
$jobId = $job.jobId
foreach($artifact_name in $artifacts_to_download) {
if ($artifact_name -eq 'shell_browser_ui_unittests.exe' -Or $artifact_name -eq 'electron.lib') {
$outfile = "src\out\Default\$artifact_name"
} else {
$outfile = $artifact_name
}
Invoke-RestMethod -Method Get -Uri "$apiUrl/buildjobs/$jobId/artifacts/$artifact_name" -OutFile $outfile
}
}
}
- ps: |
$out_default_zips = @('dist.zip','chromedriver.zip','mksnapshot.zip')
foreach($zip_name in $out_default_zips) {
7z x -y -osrc\out\Default $zip_name
}
- ps: 7z x -y -osrc\out\ffmpeg ffmpeg.zip
- ps: 7z x -y -osrc node_headers.zip
- ps: 7z x -y -osrc builtins-pgo.zip
test_script:
# Workaround for https://github.com/appveyor/ci/issues/2420
- set "PATH=%PATH%;C:\Program Files\Git\mingw64\libexec\git-core"
- ps: |
cd src
New-Item .\out\Default\gen\node_headers\Release -Type directory
Copy-Item -path .\out\Default\electron.lib -destination .\out\Default\gen\node_headers\Release\node.lib
- cd electron
- echo Running main test suite & node script/yarn test -- --trace-uncaught --runners=main --enable-logging=file --log-file=%cd%\electron.log
- echo Running native test suite & node script/yarn test -- --trace-uncaught --runners=native --enable-logging=file --log-file=%cd%\electron.log
- cd ..
- echo Verifying non proprietary ffmpeg & python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg
- echo "About to verify mksnapshot"
- echo Verifying mksnapshot & python electron\script\verify-mksnapshot.py --build-dir out\Default --source-root %cd%
- echo "Done verifying mksnapshot"
- echo Verifying chromedriver & python electron\script\verify-chromedriver.py --build-dir out\Default --source-root %cd%
- echo "Done verifying chromedriver"
on_finish:
- if exist electron\electron.log ( appveyor-retry appveyor PushArtifact electron\electron.log )
- if exist electron\electron.log ( appveyor-retry appveyor PushArtifact electron\electron.log )

View File

@@ -1,4 +1,4 @@
root_extra_deps = [ "//electron/spec-chromium:spec" ]
root_extra_deps = [ "//electron/spec" ]
dcheck_always_on = true
is_debug = false

View File

@@ -75,17 +75,9 @@ module.exports = ({
if (targetDeletesNodeGlobals) {
plugins.push(new webpack.ProvidePlugin({
Buffer: ['@electron/internal/common/webpack-provider', 'Buffer'],
process: ['@electron/internal/common/webpack-provider', 'process'],
global: ['@electron/internal/common/webpack-provider', '_global'],
process: ['@electron/internal/common/webpack-provider', 'process']
}));
}
// Webpack 5 no longer polyfills process or Buffer.
if (!alwaysHasNode) {
plugins.push(new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: 'process/browser'
Buffer: ['@electron/internal/common/webpack-provider', 'Buffer']
}));
}
@@ -137,12 +129,7 @@ if ((globalThis.process || binding.process).argv.includes("--profile-electron-in
// Force timers to resolve to our dependency that doesn't use window.postMessage
timers: path.resolve(electronRoot, 'node_modules', 'timers-browserify', 'main.js')
},
extensions: ['.ts', '.js'],
fallback: {
// We provide our own "timers" import above, any usage of setImmediate inside
// one of our renderer bundles should import it from the 'timers' package
setImmediate: false
}
extensions: ['.ts', '.js']
},
module: {
rules: [{
@@ -163,7 +150,10 @@ if ((globalThis.process || binding.process).argv.includes("--profile-electron-in
},
node: {
__dirname: false,
__filename: false
__filename: false,
// We provide our own "timers" import above, any usage of setImmediate inside
// one of our renderer bundles should import it from the 'timers' package
setImmediate: false
},
optimization: {
minimize: env.mode === 'production',

View File

@@ -30,14 +30,11 @@ template("webpack_build") {
args = [
"--config",
rebase_path(invoker.config_file),
"--output-filename",
get_path_info(invoker.out_file, "file"),
"--output-path",
rebase_path(get_path_info(invoker.out_file, "dir")),
"--env",
"buildflags=" + rebase_path("$target_gen_dir/buildflags/buildflags.h"),
"--env",
"mode=" + mode,
"--output-filename=" + get_path_info(invoker.out_file, "file"),
"--output-path=" + rebase_path(get_path_info(invoker.out_file, "dir")),
"--env.buildflags=" +
rebase_path("$target_gen_dir/buildflags/buildflags.h"),
"--env.mode=" + mode,
]
deps += [ "//electron/buildflags" ]

View File

@@ -635,7 +635,7 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
* `notifications` - Request notification creation and the ability to display them in the user's system tray.
* `midi` - Request MIDI access in the `webmidi` API.
* `midiSysex` - Request the use of system exclusive messages in the `webmidi` API.
* `pointerLock` - Request to directly interpret mouse movements as an input method. Click [here](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API) to know more.
* `pointerLock` - Request to directly interpret mouse movements as an input method. Click [here](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API) to know more. These requests always appear to originate from the main frame.
* `fullscreen` - Request for the app to enter fullscreen mode.
* `openExternal` - Request to open links in external applications.
* `unknown` - An unrecognized permission request
@@ -698,60 +698,6 @@ session.fromPartition('some-partition').setPermissionCheckHandler((webContents,
})
```
#### `ses.setDisplayMediaRequestHandler(handler)`
* `handler` Function | null
* `request` Object
* `frame` [WebFrameMain](web-frame-main.md) - Frame that is requesting access to media.
* `securityOrigin` String - Origin of the page making the request.
* `videoRequested` Boolean - true if the web content requested a video stream.
* `audioRequested` Boolean - true if the web content requested an audio stream.
* `userGesture` Boolean - Whether a user gesture was active when this request was triggered.
* `callback` Function
* `streams` Object
* `video` Object | [WebFrameMain](web-frame-main.md) (optional)
* `id` String - The id of the stream being granted. This will usually
come from a [DesktopCapturerSource](structures/desktop-capturer-source.md)
object.
* `name` String - The name of the stream being granted. This will
usually come from a [DesktopCapturerSource](structures/desktop-capturer-source.md)
object.
* `audio` String | [WebFrameMain](web-frame-main.md) (optional) - If
a string is specified, can be `loopback` or `loopbackWithMute`.
Specifying a loopback device will capture system audio, and is
currently only supported on Windows. If a WebFrameMain is specified,
will capture audio from that frame.
This handler will be called when web content requests access to display media
via the `navigator.mediaDevices.getDisplayMedia` API. Use the
[desktopCapturer](desktop-capturer.md) API to choose which stream(s) to grant
access to.
```javascript
const { session, desktopCapturer } = require('electron')
session.defaultSession.setDisplayMediaRequestHandler((request, callback) => {
desktopCapturer.getSources({ types: ['screen'] }).then((sources) => {
// Grant access to the first screen found.
callback({ video: sources[0] })
})
})
```
Passing a [WebFrameMain](web-frame-main.md) object as a video or audio stream
will capture the video or audio stream from that frame.
```javascript
const { session } = require('electron')
session.defaultSession.setDisplayMediaRequestHandler((request, callback) => {
// Allow the tab to capture itself.
callback({ video: request.frame })
})
```
Passing `null` instead of a function resets the handler to its default state.
#### `ses.setDevicePermissionHandler(handler)`
* `handler` Function\<boolean> | null

View File

@@ -0,0 +1,3 @@
# NewWindowWebContentsEvent Object extends `Event`
* `newGuest` BrowserWindow (optional)

View File

@@ -156,6 +156,64 @@ Returns:
Emitted when page receives favicon urls.
#### Event: 'new-window' _Deprecated_
Returns:
* `event` NewWindowWebContentsEvent
* `url` string
* `frameName` string
* `disposition` string - Can be `default`, `foreground-tab`, `background-tab`,
`new-window`, `save-to-disk` and `other`.
* `options` BrowserWindowConstructorOptions - The options which will be used for creating the new
[`BrowserWindow`](browser-window.md).
* `additionalFeatures` string[] - The non-standard features (features not handled
by Chromium or Electron) given to `window.open()`. Deprecated, and will now
always be the empty array `[]`.
* `referrer` [Referrer](structures/referrer.md) - The referrer that will be
passed to the new window. May or may not result in the `Referer` header being
sent, depending on the referrer policy.
* `postBody` [PostBody](structures/post-body.md) (optional) - The post data that
will be sent to the new window, along with the appropriate headers that will
be set. If no post data is to be sent, the value will be `null`. Only defined
when the window is being created by a form that set `target=_blank`.
Deprecated in favor of [`webContents.setWindowOpenHandler`](web-contents.md#contentssetwindowopenhandlerhandler).
Emitted when the page requests to open a new window for a `url`. It could be
requested by `window.open` or an external link like `<a target='_blank'>`.
By default a new `BrowserWindow` will be created for the `url`.
Calling `event.preventDefault()` will prevent Electron from automatically creating a
new [`BrowserWindow`](browser-window.md). If you call `event.preventDefault()` and manually create a new
[`BrowserWindow`](browser-window.md) then you must set `event.newGuest` to reference the new [`BrowserWindow`](browser-window.md)
instance, failing to do so may result in unexpected behavior. For example:
```javascript
myBrowserWindow.webContents.on('new-window', (event, url, frameName, disposition, options, additionalFeatures, referrer, postBody) => {
event.preventDefault()
const win = new BrowserWindow({
webContents: options.webContents, // use existing webContents if provided
show: false
})
win.once('ready-to-show', () => win.show())
if (!options.webContents) {
const loadOptions = {
httpReferrer: referrer
}
if (postBody != null) {
const { data, contentType, boundary } = postBody
loadOptions.postData = postBody.data
loadOptions.extraHeaders = `content-type: ${contentType}; boundary=${boundary}`
}
win.loadURL(url, loadOptions) // existing webContents will be navigated automatically
}
event.newGuest = win
})
```
#### Event: 'did-create-window'
Returns:
@@ -1568,8 +1626,6 @@ 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

@@ -805,6 +805,33 @@ const requestId = webview.findInPage('test')
console.log(requestId)
```
### Event: 'new-window'
Returns:
* `url` string
* `frameName` string
* `disposition` string - Can be `default`, `foreground-tab`, `background-tab`,
`new-window`, `save-to-disk` and `other`.
* `options` BrowserWindowConstructorOptions - The options which should be used for creating the new
[`BrowserWindow`](browser-window.md).
Fired when the guest page attempts to open a new browser window.
The following example code opens the new url in system's default browser.
```javascript
const { shell } = require('electron')
const webview = document.querySelector('webview')
webview.addEventListener('new-window', async (e) => {
const protocol = (new URL(e.url)).protocol
if (protocol === 'http:' || protocol === 'https:') {
await shell.openExternal(e.url)
}
})
```
### Event: 'will-navigate'
Returns:

View File

@@ -12,24 +12,6 @@ This document uses the following convention to categorize breaking changes:
* **Deprecated:** An API was marked as deprecated. The API will continue to function, but will emit a deprecation warning, and will be removed in a future release.
* **Removed:** An API or feature was removed, and is no longer supported by Electron.
## Planned Breaking API Changes (22.0)
### Removed: WebContents `new-window` event
The `new-window` event of WebContents has been removed. It is replaced by [`webContents.setWindowOpenHandler()`](api/web-contents.md#contentssetwindowopenhandlerhandler).
```js
// Removed in Electron 21
webContents.on('new-window', (event) => {
event.preventDefault()
})
// Replace with
webContents.setWindowOpenHandler((details) => {
return { action: 'deny' }
})
```
## Planned Breaking API Changes (20.0)
### Behavior Changed: V8 Memory Cage enabled

View File

@@ -54,7 +54,7 @@ See [issues](issues.md) for more information.
Most pull requests opened against the `electron/electron` repository include
changes to either the C/C++ code in the `shell/` folder,
the TypeScript code in the `lib/` folder, the documentation in `docs/`,
or tests in the `spec/` folder.
or tests in the `spec/` and `spec-main/` folders.
See [pull requests](pull-requests.md) for more information.

View File

@@ -281,22 +281,9 @@ $ cd electron
$ gclient sync -f
```
This may also happen if you have checked out a branch (as opposed to having a detached head) in `electron/src/`
or some other dependencys repository. If that is the case, a `git checkout --detach HEAD` in the appropriate repository should do the trick.
### I'm being asked for a username/password for chromium-internal.googlesource.com
If you see a prompt for `Username for 'https://chrome-internal.googlesource.com':` when running `gclient sync` on Windows, it's probably because the `DEPOT_TOOLS_WIN_TOOLCHAIN` environment variable is not set to 0. Open `Control Panel``System and Security``System``Advanced system settings` and add a system variable
`DEPOT_TOOLS_WIN_TOOLCHAIN` with value `0`. This tells `depot_tools` to use
your locally installed version of Visual Studio (by default, `depot_tools` will
try to download a Google-internal version that only Googlers have access to).
### `e` Module not found
If `e` is not recognized despite running `npm i -g @electron/build-tools`, ie:
```sh
Error: Cannot find module '/Users/<user>/.electron_build_tools/src/e'
```
We recommend installing Node through [nvm](https://github.com/nvm-sh/nvm). This allows for easier Node version management, and is often a fix for missing `e` modules.

View File

@@ -13,46 +13,6 @@ Follow the guidelines below for building **Electron itself** on macOS, for the p
* [node.js](https://nodejs.org) (external)
* Python >= 3.7
### Arm64-specific prerequisites
* Rosetta 2
* We recommend installing Rosetta if using dependencies that need to cross-compile on x64 and arm64 machines. Rosetta can be installed by using the softwareupdate command line tool.
* `$ softwareupdate --install-rosetta`
## Building Electron
See [Build Instructions: GN](build-instructions-gn.md).
## Troubleshooting
### Xcode "incompatible architecture" errors (MacOS arm64-specific)
If both Xcode and Xcode command line tools are installed (`$ xcode -select --install`, or directly download the correct version [here](https://developer.apple.com/download/all/?q=command%20line%20tools)), but the stack trace says otherwise like so:
```sh
xcrun: error: unable to load libxcrun
(dlopen(/Users/<user>/.electron_build_tools/third_party/Xcode/Xcode.app/Contents/Developer/usr/lib/libxcrun.dylib (http://xcode.app/Contents/Developer/usr/lib/libxcrun.dylib), 0x0005):
tried: '/Users/<user>/.electron_build_tools/third_party/Xcode/Xcode.app/Contents/Developer/usr/lib/libxcrun.dylib (http://xcode.app/Contents/Developer/usr/lib/libxcrun.dylib)'
(mach-o file, but is an incompatible architecture (have (x86_64), need (arm64e))), '/Users/<user>/.electron_build_tools/third_party/Xcode/Xcode-11.1.0.app/Contents/Developer/usr/lib/libxcrun.dylib (http://xcode-11.1.0.app/Contents/Developer/usr/lib/libxcrun.dylib)' (mach-o file, but is an incompatible architecture (have (x86_64), need (arm64e)))).`
```
If you are on arm64 architecture, the build script may be pointing to the wrong Xcode version (11.x.y doesn't support arm64). Navigate to `/Users/<user>/.electron_build_tools/third_party/Xcode/` and rename `Xcode-13.3.0.app` to `Xcode.app` to ensure the right Xcode version is used.
### Certificates fail to verify
installing [`certifi`](https://pypi.org/project/certifi/) will fix the following error:
```sh
________ running 'python3 src/tools/clang/scripts/update.py' in '/Users/<user>/electron'
Downloading https://commondatastorage.googleapis.com/chromium-browser-clang/Mac_arm64/clang-llvmorg-15-init-15652-g89a99ec9-1.tgz
<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:997)>
Retrying in 5 s ...
Downloading https://commondatastorage.googleapis.com/chromium-browser-clang/Mac_arm64/clang-llvmorg-15-init-15652-g89a99ec9-1.tgz
<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:997)>
Retrying in 10 s ...
Downloading https://commondatastorage.googleapis.com/chromium-browser-clang/Mac_arm64/clang-llvmorg-15-init-15652-g89a99ec9-1.tgz
<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:997)>
Retrying in 20 s ...
```
This issue has to do with Python 3.6 using its [own](https://github.com/python/cpython/blob/560ea272b01acaa6c531cc7d94331b2ef0854be6/Mac/BuildScript/resources/ReadMe.rtf#L35) copy of OpenSSL in lieu of the deprecated Apple-supplied OpenSSL libraries. `certifi` adds a curated bundle of default root certificates. This issue is documented in the Electron repo [here](https://github.com/electron/build-tools/issues/55). Further information about this issue can be found [here](https://stackoverflow.com/questions/27835619/urllib-and-ssl-certificate-verify-failed-error) and [here](https://stackoverflow.com/questions/40684543/how-to-make-python-use-ca-certificates-from-mac-os-truststore).

View File

@@ -72,7 +72,8 @@ Electron
| | message loop into Chromium's message loop.
| └── api/ - The implementation of common APIs, and foundations of
| Electron's built-in modules.
├── spec/ - Components of Electron's test suite run in the main process.
├── spec/ - Components of Electron's test suite run in the renderer process.
├── spec-main/ - Components of Electron's test suite run in the main process.
└── BUILD.gn - Building rules of Electron.
```

View File

@@ -32,6 +32,9 @@ app (surprise!) that can be found in the `spec` folder. Note that it has
its own `package.json` and that its dependencies are therefore not defined
in the top-level `package.json`.
To run only tests in a specific process, run `npm run test --runners=PROCESS`
where `PROCESS` is one of `main` or `remote`.
To run only specific tests matching a pattern, run `npm run test --
-g=PATTERN`, replacing the `PATTERN` with a regex that matches the tests
you would like to run. As an example: If you want to run only IPC tests, you

View File

@@ -47,7 +47,7 @@ are the different categories and what you can expect on each one:
- **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
- **Testing And Debugging**: How to debug JavaScript, write tests, and other tools used
to create quality Electron applications.
- **References**: Useful links to better understand how the Electron project works
and is organized.

View File

@@ -98,6 +98,7 @@ auto_filenames = {
"docs/api/structures/mime-typed-buffer.md",
"docs/api/structures/mouse-input-event.md",
"docs/api/structures/mouse-wheel-input-event.md",
"docs/api/structures/new-window-web-contents-event.md",
"docs/api/structures/notification-action.md",
"docs/api/structures/notification-response.md",
"docs/api/structures/payment-discount.md",
@@ -136,6 +137,7 @@ auto_filenames = {
]
sandbox_bundle_deps = [
"lib/common/api/deprecate.ts",
"lib/common/api/native-image.ts",
"lib/common/define-properties.ts",
"lib/common/ipc-messages.ts",
@@ -237,11 +239,11 @@ auto_filenames = {
"lib/browser/rpc-server.ts",
"lib/browser/web-view-events.ts",
"lib/common/api/clipboard.ts",
"lib/common/api/deprecate.ts",
"lib/common/api/module-list.ts",
"lib/common/api/native-image.ts",
"lib/common/api/shell.ts",
"lib/common/define-properties.ts",
"lib/common/deprecate.ts",
"lib/common/init.ts",
"lib/common/ipc-messages.ts",
"lib/common/reset-search-paths.ts",
@@ -258,6 +260,7 @@ auto_filenames = {
renderer_bundle_deps = [
"lib/common/api/clipboard.ts",
"lib/common/api/deprecate.ts",
"lib/common/api/module-list.ts",
"lib/common/api/native-image.ts",
"lib/common/api/shell.ts",
@@ -296,6 +299,7 @@ auto_filenames = {
worker_bundle_deps = [
"lib/common/api/clipboard.ts",
"lib/common/api/deprecate.ts",
"lib/common/api/module-list.ts",
"lib/common/api/native-image.ts",
"lib/common/api/shell.ts",

View File

@@ -577,8 +577,6 @@ filenames = {
"shell/common/gin_converters/hid_device_info_converter.h",
"shell/common/gin_converters/image_converter.cc",
"shell/common/gin_converters/image_converter.h",
"shell/common/gin_converters/media_converter.cc",
"shell/common/gin_converters/media_converter.h",
"shell/common/gin_converters/message_box_converter.cc",
"shell/common/gin_converters/message_box_converter.h",
"shell/common/gin_converters/native_window_converter.h",
@@ -677,6 +675,8 @@ filenames = {
"shell/renderer/electron_renderer_pepper_host_factory.h",
"shell/renderer/electron_sandboxed_renderer_client.cc",
"shell/renderer/electron_sandboxed_renderer_client.h",
"shell/renderer/guest_view_container.cc",
"shell/renderer/guest_view_container.h",
"shell/renderer/renderer_client_base.cc",
"shell/renderer/renderer_client_base.h",
"shell/renderer/web_worker_observer.cc",

View File

@@ -60,8 +60,8 @@ const splitPath = (archivePathOrBuffer: string | Buffer) => {
// Convert asar archive's Stats object to fs's Stats object.
let nextInode = 0;
const uid = process.getuid?.() ?? 0;
const gid = process.getgid?.() ?? 0;
const uid = process.getuid != null ? process.getuid() : 0;
const gid = process.getgid != null ? process.getgid() : 0;
const fakeTime = new Date();

View File

@@ -1,7 +1,6 @@
import * as fs from 'fs';
import { Menu } from 'electron/main';
import * as deprecate from '@electron/internal/common/deprecate';
import { Menu, deprecate } from 'electron/main';
const bindings = process._linkedBinding('electron_browser_app');
const commandLine = process._linkedBinding('electron_common_command_line');

View File

@@ -68,7 +68,7 @@ const spawnUpdate = function (args: string[], detached: boolean, callback: Funct
if (code !== 0) {
// Disabled for backwards compatibility:
// eslint-disable-next-line standard/no-callback-literal
return callback(`Command failed: ${signal ?? code}\n${stderr}`);
return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`);
}
// Success.

View File

@@ -1,5 +1,4 @@
import { app } from 'electron/main';
import * as deprecate from '@electron/internal/common/deprecate';
import { app, deprecate } from 'electron/main';
const binding = process._linkedBinding('electron_browser_crash_reporter');

View File

@@ -9,7 +9,8 @@ let currentlyRunning: {
// |options.types| can't be empty and must be an array
function isValid (options: Electron.SourcesOptions) {
return Array.isArray(options?.types);
const types = options ? options.types : undefined;
return Array.isArray(types);
}
export async function getSources (args: Electron.SourcesOptions) {

View File

@@ -395,7 +395,7 @@ class TouchBar extends EventEmitter implements Electron.TouchBar {
this.on('change', changeListener);
const escapeItemListener = (item: Electron.TouchBarItemType | null) => {
window._setEscapeTouchBarItem(item ?? {});
window._setEscapeTouchBarItem(item != null ? item : {});
};
this.on('escape-item-change', escapeItemListener);

View File

@@ -1,4 +1,4 @@
import { app, ipcMain, session, webFrameMain } from 'electron/main';
import { app, ipcMain, session, webFrameMain, deprecate } from 'electron/main';
import type { BrowserWindowConstructorOptions, LoadURLOptions } from 'electron/main';
import * as url from 'url';
@@ -10,7 +10,6 @@ import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-util
import { MessagePortMain } from '@electron/internal/browser/message-port-main';
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
import { IpcMainImpl } from '@electron/internal/browser/ipc-main-impl';
import * as deprecate from '@electron/internal/common/deprecate';
// session is not used here, the purpose is to make sure session is initialized
// before the webContents module.
@@ -671,6 +670,7 @@ WebContents.prototype._init = function () {
const options = result.browserWindowConstructorOptions;
if (!event.defaultPrevented) {
openGuestWindow({
event,
embedder: event.sender,
disposition,
referrer,
@@ -717,16 +717,18 @@ WebContents.prototype._init = function () {
transparent: windowOpenOverriddenOptions.transparent,
...windowOpenOverriddenOptions.webPreferences
} : undefined;
// TODO(zcbenz): The features string is parsed twice: here where it is
// passed to C++, and in |makeBrowserWindowOptions| later where it is
// not actually used since the WebContents is created here.
// We should be able to remove the latter once the |new-window| event
// is removed.
const { webPreferences: parsedWebPreferences } = parseFeatures(rawFeatures);
// Parameters should keep same with |makeBrowserWindowOptions|.
const webPreferences = makeWebPreferences({
embedder: event.sender,
insecureParsedWebPreferences: parsedWebPreferences,
secureOverrideWebPreferences
});
windowOpenOverriddenOptions = {
...windowOpenOverriddenOptions,
webPreferences
};
this._setNextChildWebPreferences(webPreferences);
}
});
@@ -748,6 +750,7 @@ WebContents.prototype._init = function () {
}
openGuestWindow({
event,
embedder: event.sender,
guest: webContents,
overrideBrowserWindowOptions: overriddenOptions,

View File

@@ -22,6 +22,13 @@ const supportedWebViewEvents = Object.keys(webViewEvents);
const guestInstances = new Map<number, GuestInstance>();
const embedderElementsMap = new Map<string, number>();
function sanitizeOptionsForGuest (options: Record<string, any>) {
const ret = { ...options };
// WebContents values can't be sent over IPC.
delete ret.webContents;
return ret;
}
function makeWebPreferences (embedder: Electron.WebContents, params: Record<string, any>) {
// parse the 'webpreferences' attribute string, if set
// this uses the same parsing rules as window.open uses for its features
@@ -31,8 +38,8 @@ function makeWebPreferences (embedder: Electron.WebContents, params: Record<stri
: null;
const webPreferences: Electron.WebPreferences = {
nodeIntegration: params.nodeintegration ?? false,
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes ?? false,
nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false,
nodeIntegrationInSubFrames: params.nodeintegrationinsubframes != null ? params.nodeintegrationinsubframes : false,
plugins: params.plugins,
zoomFactor: embedder.zoomFactor,
disablePopups: !params.allowpopups,
@@ -149,6 +156,15 @@ const createGuest = function (embedder: Electron.WebContents, embedderFrameId: n
});
}
guest.on('new-window', function (event, url, frameName, disposition, options) {
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, 'new-window', {
url,
frameName,
disposition,
options: sanitizeOptionsForGuest(options)
});
});
// Dispatch guest's IPC messages to embedder.
guest.on('ipc-message-host' as any, function (event: Electron.IpcMainEvent, channel: string, args: any[]) {
sendToEmbedder(IPC_MESSAGES.GUEST_VIEW_INTERNAL_DISPATCH_EVENT, 'ipc-message', {

View File

@@ -5,7 +5,7 @@
* out-of-process (cross-origin) are created here. "Embedder" roughly means
* "parent."
*/
import { BrowserWindow } from 'electron/main';
import { BrowserWindow, deprecate } from 'electron/main';
import type { BrowserWindowConstructorOptions, Referrer, WebContents, LoadURLOptions } from 'electron/main';
import { parseFeatures } from '@electron/internal/browser/parse-features-string';
@@ -24,8 +24,13 @@ const getGuestWindowByFrameName = (name: string) => frameNamesToWindow.get(name)
/**
* `openGuestWindow` is called to create and setup event handling for the new
* window.
*
* Until its removal in 12.0.0, the `new-window` event is fired, allowing the
* user to preventDefault() on the passed event (which ends up calling
* DestroyWebContents).
*/
export function openGuestWindow ({ embedder, guest, referrer, disposition, postData, overrideBrowserWindowOptions, windowOpenArgs, outlivesOpener }: {
export function openGuestWindow ({ event, embedder, guest, referrer, disposition, postData, overrideBrowserWindowOptions, windowOpenArgs, outlivesOpener }: {
event: { sender: WebContents, defaultPrevented: boolean },
embedder: WebContents,
guest?: WebContents,
referrer: Referrer,
@@ -36,14 +41,23 @@ export function openGuestWindow ({ embedder, guest, referrer, disposition, postD
outlivesOpener: boolean,
}): BrowserWindow | undefined {
const { url, frameName, features } = windowOpenArgs;
const { options: parsedOptions } = parseFeatures(features);
const browserWindowOptions = {
show: true,
width: 800,
height: 600,
...parsedOptions,
...overrideBrowserWindowOptions
};
const browserWindowOptions = makeBrowserWindowOptions({
embedder,
features,
overrideOptions: overrideBrowserWindowOptions
});
const didCancelEvent = emitDeprecatedNewWindowEvent({
event,
embedder,
guest,
browserWindowOptions,
windowOpenArgs,
disposition,
postData,
referrer
});
if (didCancelEvent) return;
// To spec, subsequent window.open calls with the same frame name (`target` in
// spec parlance) will reuse the previous window.
@@ -120,6 +134,68 @@ const handleWindowLifecycleEvents = function ({ embedder, guest, frameName, outl
}
};
/**
* Deprecated in favor of `webContents.setWindowOpenHandler` and
* `did-create-window` in 11.0.0. Will be removed in 12.0.0.
*/
function emitDeprecatedNewWindowEvent ({ event, embedder, guest, windowOpenArgs, browserWindowOptions, disposition, referrer, postData }: {
event: { sender: WebContents, defaultPrevented: boolean, newGuest?: BrowserWindow },
embedder: WebContents,
guest?: WebContents,
windowOpenArgs: WindowOpenArgs,
browserWindowOptions: BrowserWindowConstructorOptions,
disposition: string,
referrer: Referrer,
postData?: PostData,
}): boolean {
const { url, frameName } = windowOpenArgs;
const isWebViewWithPopupsDisabled = embedder.getType() === 'webview' && embedder.getLastWebPreferences()!.disablePopups;
const postBody = postData ? {
data: postData,
...parseContentTypeFormat(postData)
} : null;
if (embedder.listenerCount('new-window') > 0) {
deprecate.log('The new-window event is deprecated and will be removed. Please use contents.setWindowOpenHandler() instead.');
}
embedder.emit(
'new-window',
event,
url,
frameName,
disposition,
{
...browserWindowOptions,
webContents: guest
},
[], // additionalFeatures
referrer,
postBody
);
const { newGuest } = event;
if (isWebViewWithPopupsDisabled) return true;
if (event.defaultPrevented) {
if (newGuest) {
if (guest === newGuest.webContents) {
// The webContents is not changed, so set defaultPrevented to false to
// stop the callers of this event from destroying the webContents.
event.defaultPrevented = false;
}
handleWindowLifecycleEvents({
embedder: event.sender,
guest: newGuest,
frameName,
outlivesOpener: false
});
}
return true;
}
return false;
}
// Security options that child windows will always inherit from parent windows
const securityWebPreferences: { [key: string]: boolean } = {
contextIsolation: true,
@@ -131,6 +207,31 @@ const securityWebPreferences: { [key: string]: boolean } = {
enableWebSQL: false
};
function makeBrowserWindowOptions ({ embedder, features, overrideOptions }: {
embedder: WebContents,
features: string,
overrideOptions?: BrowserWindowConstructorOptions,
}) {
const { options: parsedOptions, webPreferences: parsedWebPreferences } = parseFeatures(features);
return {
show: true,
width: 800,
height: 600,
...parsedOptions,
...overrideOptions,
// Note that for normal code path an existing WebContents created by
// Chromium will be used, with web preferences parsed in the
// |-will-add-new-contents| event.
// The |webPreferences| here is only used by the |new-window| event.
webPreferences: makeWebPreferences({
embedder,
insecureParsedWebPreferences: parsedWebPreferences,
secureOverrideWebPreferences: overrideOptions && overrideOptions.webPreferences
})
} as Electron.BrowserViewConstructorOptions;
}
export function makeWebPreferences ({ embedder, secureOverrideWebPreferences = {}, insecureParsedWebPreferences: parsedWebPreferences = {} }: {
embedder: WebContents,
insecureParsedWebPreferences?: ReturnType<typeof parseFeatures>['webPreferences'],

135
lib/common/api/deprecate.ts Normal file
View File

@@ -0,0 +1,135 @@
let deprecationHandler: ElectronInternal.DeprecationHandler | null = null;
function warnOnce (oldName: string, newName?: string) {
let warned = false;
const msg = newName
? `'${oldName}' is deprecated and will be removed. Please use '${newName}' instead.`
: `'${oldName}' is deprecated and will be removed.`;
return () => {
if (!warned && !process.noDeprecation) {
warned = true;
deprecate.log(msg);
}
};
}
const deprecate: ElectronInternal.DeprecationUtil = {
warnOnce,
setHandler: (handler) => { deprecationHandler = handler; },
getHandler: () => deprecationHandler,
warn: (oldName, newName) => {
if (!process.noDeprecation) {
deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`);
}
},
log: (message) => {
if (typeof deprecationHandler === 'function') {
deprecationHandler(message);
} else if (process.throwDeprecation) {
throw new Error(message);
} else if (process.traceDeprecation) {
return console.trace(message);
} else {
return console.warn(`(electron) ${message}`);
}
},
// remove a function with no replacement
removeFunction: (fn, removedName) => {
if (!fn) { throw Error(`'${removedName} function' is invalid or does not exist.`); }
// wrap the deprecated function to warn user
const warn = warnOnce(`${fn.name} function`);
return function (this: any) {
warn();
fn.apply(this, arguments);
} as unknown as typeof fn;
},
// change the name of a function
renameFunction: (fn, newName) => {
const warn = warnOnce(`${fn.name} function`, `${newName} function`);
return function (this: any) {
warn();
return fn.apply(this, arguments);
} as unknown as typeof fn;
},
moveAPI<T extends Function> (fn: T, oldUsage: string, newUsage: string): T {
const warn = warnOnce(oldUsage, newUsage);
return function (this: any) {
warn();
return fn.apply(this, arguments);
} as unknown as typeof fn;
},
// change the name of an event
event: (emitter, oldName, newName) => {
const warn = newName.startsWith('-') /* internal event */
? warnOnce(`${oldName} event`)
: warnOnce(`${oldName} event`, `${newName} event`);
return emitter.on(newName, function (this: NodeJS.EventEmitter, ...args) {
if (this.listenerCount(oldName) !== 0) {
warn();
this.emit(oldName, ...args);
}
});
},
// remove a property with no replacement
removeProperty: (o, removedName, onlyForValues) => {
// if the property's already been removed, warn about it
const info = Object.getOwnPropertyDescriptor((o as any).__proto__, removedName) // eslint-disable-line
if (!info) {
deprecate.log(`Unable to remove property '${removedName}' from an object that lacks it.`);
return o;
}
if (!info.get || !info.set) {
deprecate.log(`Unable to remove property '${removedName}' from an object does not have a getter / setter`);
return o;
}
// wrap the deprecated property in an accessor to warn
const warn = warnOnce(removedName);
return Object.defineProperty(o, removedName, {
configurable: true,
get: () => {
warn();
return info.get!.call(o);
},
set: newVal => {
if (!onlyForValues || onlyForValues.includes(newVal)) {
warn();
}
return info.set!.call(o, newVal);
}
});
},
// change the name of a property
renameProperty: (o, oldName, newName) => {
const warn = warnOnce(oldName, newName);
// if the new property isn't there yet,
// inject it and warn about it
if ((oldName in o) && !(newName in o)) {
warn();
o[newName] = (o as any)[oldName];
}
// wrap the deprecated property in an accessor to warn
// and redirect to the new property
return Object.defineProperty(o, oldName, {
get: () => {
warn();
return o[newName];
},
set: value => {
warn();
o[newName] = value;
}
});
}
};
export default deprecate;

View File

@@ -2,5 +2,7 @@
export const commonModuleList: ElectronInternal.ModuleEntry[] = [
{ name: 'clipboard', loader: () => require('./clipboard') },
{ name: 'nativeImage', loader: () => require('./native-image') },
{ name: 'shell', loader: () => require('./shell') }
{ name: 'shell', loader: () => require('./shell') },
// The internal modules, invisible unless you know their names.
{ name: 'deprecate', loader: () => require('./deprecate'), private: true }
];

View File

@@ -9,7 +9,7 @@ export function defineProperties (targetExports: Object, moduleList: ElectronInt
const descriptors: PropertyDescriptorMap = {};
for (const module of moduleList) {
descriptors[module.name] = {
enumerable: true,
enumerable: !module.private,
get: handleESModule(module.loader)
};
}

View File

@@ -1,139 +0,0 @@
type DeprecationHandler = (message: string) => void;
let deprecationHandler: DeprecationHandler | null = null;
export function warnOnce (oldName: string, newName?: string) {
let warned = false;
const msg = newName
? `'${oldName}' is deprecated and will be removed. Please use '${newName}' instead.`
: `'${oldName}' is deprecated and will be removed.`;
return () => {
if (!warned && !process.noDeprecation) {
warned = true;
log(msg);
}
};
}
export function setHandler (handler: DeprecationHandler | null): void {
deprecationHandler = handler;
}
export function getHandler (): DeprecationHandler | null {
return deprecationHandler;
}
export function warn (oldName: string, newName: string): void {
if (!process.noDeprecation) {
log(`'${oldName}' is deprecated. Use '${newName}' instead.`);
}
}
export function log (message: string): void {
if (typeof deprecationHandler === 'function') {
deprecationHandler(message);
} else if (process.throwDeprecation) {
throw new Error(message);
} else if (process.traceDeprecation) {
return console.trace(message);
} else {
return console.warn(`(electron) ${message}`);
}
}
// remove a function with no replacement
export function removeFunction<T extends Function> (fn: T, removedName: string): T {
if (!fn) { throw Error(`'${removedName} function' is invalid or does not exist.`); }
// wrap the deprecated function to warn user
const warn = warnOnce(`${fn.name} function`);
return function (this: any) {
warn();
fn.apply(this, arguments);
} as unknown as typeof fn;
}
// change the name of a function
export function renameFunction<T extends Function> (fn: T, newName: string): T {
const warn = warnOnce(`${fn.name} function`, `${newName} function`);
return function (this: any) {
warn();
return fn.apply(this, arguments);
} as unknown as typeof fn;
}
// change the name of an event
export function event (emitter: NodeJS.EventEmitter, oldName: string, newName: string) {
const warn = newName.startsWith('-') /* internal event */
? warnOnce(`${oldName} event`)
: warnOnce(`${oldName} event`, `${newName} event`);
return emitter.on(newName, function (this: NodeJS.EventEmitter, ...args) {
if (this.listenerCount(oldName) !== 0) {
warn();
this.emit(oldName, ...args);
}
});
}
// remove a property with no replacement
export function removeProperty<T, K extends (keyof T & string)>(object: T, removedName: K, onlyForValues?: any[]): T {
// if the property's already been removed, warn about it
const info = Object.getOwnPropertyDescriptor((object as any).__proto__, removedName) // eslint-disable-line
if (!info) {
log(`Unable to remove property '${removedName}' from an object that lacks it.`);
return object;
}
if (!info.get || !info.set) {
log(`Unable to remove property '${removedName}' from an object does not have a getter / setter`);
return object;
}
// wrap the deprecated property in an accessor to warn
const warn = warnOnce(removedName);
return Object.defineProperty(object, removedName, {
configurable: true,
get: () => {
warn();
return info.get!.call(object);
},
set: newVal => {
if (!onlyForValues || onlyForValues.includes(newVal)) {
warn();
}
return info.set!.call(object, newVal);
}
});
}
// change the name of a property
export function renameProperty<T, K extends (keyof T & string)>(object: T, oldName: string, newName: K): T {
const warn = warnOnce(oldName, newName);
// if the new property isn't there yet,
// inject it and warn about it
if ((oldName in object) && !(newName in object)) {
warn();
object[newName] = (object as any)[oldName];
}
// wrap the deprecated property in an accessor to warn
// and redirect to the new property
return Object.defineProperty(object, oldName, {
get: () => {
warn();
return object[newName];
},
set: value => {
warn();
object[newName] = value;
}
});
}
export function moveAPI<T extends Function> (fn: T, oldUsage: string, newUsage: string): T {
const warn = warnOnce(oldUsage, newUsage);
return function (this: any) {
warn();
return fn.apply(this, arguments);
} as unknown as typeof fn;
}

View File

@@ -18,5 +18,11 @@ export const moduleList: ElectronInternal.ModuleEntry[] = [
{
name: 'webFrame',
loader: () => require('@electron/internal/renderer/api/web-frame')
},
// The internal modules, invisible unless you know their names.
{
name: 'deprecate',
loader: () => require('@electron/internal/common/api/deprecate'),
private: true
}
];

View File

@@ -8,11 +8,11 @@
"postinstall": "node install.js"
},
"dependencies": {
"@electron/get": "^2.0.0",
"@electron/get": "^1.14.1",
"@types/node": "^16.11.26",
"extract-zip": "^2.0.1"
},
"engines": {
"node": ">= 12.20.55"
"node": ">= 10.17.0"
}
}

View File

@@ -1,12 +1,12 @@
{
"name": "electron",
"version": "22.0.0-nightly.20220825",
"version": "21.0.0-beta.3",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {
"@azure/storage-blob": "^12.9.0",
"@electron/docs-parser": "^0.12.4",
"@electron/typescript-definitions": "^8.9.6",
"@electron/typescript-definitions": "^8.9.5",
"@octokit/auth-app": "^2.10.0",
"@octokit/rest": "^18.0.3",
"@primer/octicons": "^10.0.0",
@@ -27,13 +27,12 @@
"@types/stream-json": "^1.5.1",
"@types/temp": "^0.8.34",
"@types/uuid": "^3.4.6",
"@types/webpack": "^5.28.0",
"@types/webpack-env": "^1.17.0",
"@types/webpack": "^4.41.21",
"@types/webpack-env": "^1.16.3",
"@typescript-eslint/eslint-plugin": "^4.4.1",
"@typescript-eslint/parser": "^4.4.1",
"asar": "^3.1.0",
"aws-sdk": "^2.814.0",
"buffer": "^6.0.3",
"check-for-leaks": "^1.2.1",
"colors": "1.4.0",
"dotenv-safe": "^4.0.4",
@@ -58,7 +57,6 @@
"minimist": "^1.2.6",
"null-loader": "^4.0.0",
"pre-flight": "^1.1.0",
"process": "^0.11.10",
"remark-cli": "^10.0.0",
"remark-preset-lint-markdown-style-guide": "^4.0.0",
"semver": "^5.6.0",
@@ -71,9 +69,9 @@
"ts-loader": "^8.0.2",
"ts-node": "6.2.0",
"typescript": "^4.5.5",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0",
"wrapper-webpack-plugin": "^2.2.0"
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"wrapper-webpack-plugin": "^2.1.0"
},
"private": true,
"scripts": {

View File

@@ -117,3 +117,5 @@ add_maximized_parameter_to_linuxui_getwindowframeprovider.patch
revert_spellcheck_fully_launch_spell_check_delayed_initialization.patch
add_electron_deps_to_license_credits_file.patch
feat_add_set_can_resize_mutator.patch
fix_revert_emulationhandler_update_functions_to_early_return.patch
cherry-pick-9b5207569882.patch

View File

@@ -0,0 +1,179 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ken Rockot <rockot@google.com>
Date: Wed, 31 Aug 2022 15:39:45 +0000
Subject: Mojo: Validate response message type
Ensures that a response message is actually the type expected by the
original request.
Fixed: 1358134
Change-Id: I8f8f58168764477fbf7a6d2e8aeb040f07793d45
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3864274
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Commit-Queue: Ken Rockot <rockot@google.com>
Cr-Commit-Position: refs/heads/main@{#1041553}
diff --git a/mojo/public/cpp/bindings/interface_endpoint_client.h b/mojo/public/cpp/bindings/interface_endpoint_client.h
index 0ebbc94ad51cca74fcebc357b5262229ae5c9d0e..cd79a5edb3f939623b874db36542ee651113c164 100644
--- a/mojo/public/cpp/bindings/interface_endpoint_client.h
+++ b/mojo/public/cpp/bindings/interface_endpoint_client.h
@@ -221,20 +221,32 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) InterfaceEndpointClient
void ForgetAsyncRequest(uint64_t request_id);
private:
- // Maps from the id of a response to the MessageReceiver that handles the
- // response.
- using AsyncResponderMap =
- std::map<uint64_t, std::unique_ptr<MessageReceiver>>;
+ struct PendingAsyncResponse {
+ public:
+ PendingAsyncResponse(uint32_t request_message_name,
+ std::unique_ptr<MessageReceiver> responder);
+ PendingAsyncResponse(PendingAsyncResponse&&);
+ PendingAsyncResponse(const PendingAsyncResponse&) = delete;
+ PendingAsyncResponse& operator=(PendingAsyncResponse&&);
+ PendingAsyncResponse& operator=(const PendingAsyncResponse&) = delete;
+ ~PendingAsyncResponse();
+
+ uint32_t request_message_name;
+ std::unique_ptr<MessageReceiver> responder;
+ };
+
+ using AsyncResponderMap = std::map<uint64_t, PendingAsyncResponse>;
struct SyncResponseInfo {
public:
- explicit SyncResponseInfo(bool* in_response_received);
+ SyncResponseInfo(uint32_t request_message_name, bool* in_response_received);
SyncResponseInfo(const SyncResponseInfo&) = delete;
SyncResponseInfo& operator=(const SyncResponseInfo&) = delete;
~SyncResponseInfo();
+ uint32_t request_message_name;
Message response;
// Points to a stack-allocated variable.
diff --git a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
index ded0d20ab193cfdaf36bd44c25719d7073c989fb..a6f41414b8918989662eb0a1dea773932631c8cc 100644
--- a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
+++ b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
@@ -30,6 +30,7 @@
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "mojo/public/cpp/bindings/sync_event_watcher.h"
#include "mojo/public/cpp/bindings/thread_safe_proxy.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_mojo_event_info.pbzero.h"
namespace mojo {
@@ -316,9 +317,27 @@ class ResponderThunk : public MessageReceiverWithStatus {
// ----------------------------------------------------------------------------
+InterfaceEndpointClient::PendingAsyncResponse::PendingAsyncResponse(
+ uint32_t request_message_name,
+ std::unique_ptr<MessageReceiver> responder)
+ : request_message_name(request_message_name),
+ responder(std::move(responder)) {}
+
+InterfaceEndpointClient::PendingAsyncResponse::PendingAsyncResponse(
+ PendingAsyncResponse&&) = default;
+
+InterfaceEndpointClient::PendingAsyncResponse&
+InterfaceEndpointClient::PendingAsyncResponse::operator=(
+ PendingAsyncResponse&&) = default;
+
+InterfaceEndpointClient::PendingAsyncResponse::~PendingAsyncResponse() =
+ default;
+
InterfaceEndpointClient::SyncResponseInfo::SyncResponseInfo(
+ uint32_t request_message_name,
bool* in_response_received)
- : response_received(in_response_received) {}
+ : request_message_name(request_message_name),
+ response_received(in_response_received) {}
InterfaceEndpointClient::SyncResponseInfo::~SyncResponseInfo() {}
@@ -606,6 +625,7 @@ bool InterfaceEndpointClient::SendMessageWithResponder(
// message before calling |SendMessage()| below.
#endif
+ const uint32_t message_name = message->name();
const bool is_sync = message->has_flag(Message::kFlagIsSync);
const bool exclusive_wait = message->has_flag(Message::kFlagNoInterrupt);
if (!controller_->SendMessage(message))
@@ -622,7 +642,8 @@ bool InterfaceEndpointClient::SendMessageWithResponder(
controller_->RegisterExternalSyncWaiter(request_id);
}
base::AutoLock lock(async_responders_lock_);
- async_responders_[request_id] = std::move(responder);
+ async_responders_.emplace(
+ request_id, PendingAsyncResponse{message_name, std::move(responder)});
return true;
}
@@ -630,7 +651,8 @@ bool InterfaceEndpointClient::SendMessageWithResponder(
bool response_received = false;
sync_responses_.insert(std::make_pair(
- request_id, std::make_unique<SyncResponseInfo>(&response_received)));
+ request_id,
+ std::make_unique<SyncResponseInfo>(message_name, &response_received)));
base::WeakPtr<InterfaceEndpointClient> weak_self =
weak_ptr_factory_.GetWeakPtr();
@@ -808,13 +830,13 @@ void InterfaceEndpointClient::ResetFromAnotherSequenceUnsafe() {
}
void InterfaceEndpointClient::ForgetAsyncRequest(uint64_t request_id) {
- std::unique_ptr<MessageReceiver> responder;
+ absl::optional<PendingAsyncResponse> response;
{
base::AutoLock lock(async_responders_lock_);
auto it = async_responders_.find(request_id);
if (it == async_responders_.end())
return;
- responder = std::move(it->second);
+ response = std::move(it->second);
async_responders_.erase(it);
}
}
@@ -906,6 +928,10 @@ bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) {
return false;
if (it->second) {
+ if (message->name() != it->second->request_message_name) {
+ return false;
+ }
+
it->second->response = std::move(*message);
*it->second->response_received = true;
return true;
@@ -916,18 +942,22 @@ bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) {
sync_responses_.erase(it);
}
- std::unique_ptr<MessageReceiver> responder;
+ absl::optional<PendingAsyncResponse> pending_response;
{
base::AutoLock lock(async_responders_lock_);
auto it = async_responders_.find(request_id);
if (it == async_responders_.end())
return false;
- responder = std::move(it->second);
+ pending_response = std::move(it->second);
async_responders_.erase(it);
}
+ if (message->name() != pending_response->request_message_name) {
+ return false;
+ }
+
internal::MessageDispatchContext dispatch_context(message);
- return responder->Accept(message);
+ return pending_response->responder->Accept(message);
} else {
if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
return control_message_handler_.Accept(message);

View File

@@ -15,8 +15,8 @@ Error message:
ptype: expected 'browser' to equal 'renderer'
Error stack trace:
AssertionError: ptype: expected 'browser' to equal 'renderer'
at checkCrash (electron\spec\api-crash-reporter-spec.ts:39:35)
at Context.<anonymous> (electron\spec\api-crash-reporter-spec.ts:154:7)
at checkCrash (electron\spec-main\api-crash-reporter-spec.ts:39:35)
at Context.<anonymous> (electron\spec-main\api-crash-reporter-spec.ts:154:7)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:93:5)

View File

@@ -13,7 +13,7 @@ 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 9de82e5422428d6419a24401e0479fbd19a15147..a30b3aae436bbe762ada1bdae69ef83127f0144b 100644
index 5a64220aaf1309832dc0ad543e353de67fe0a779..55a2a78ce166a65cd11b26e0aa31968f6a10bec8 100644
--- a/chrome/browser/process_singleton.h
+++ b/chrome/browser/process_singleton.h
@@ -18,6 +18,7 @@
@@ -53,7 +53,7 @@ index 9de82e5422428d6419a24401e0479fbd19a15147..a30b3aae436bbe762ada1bdae69ef831
~ProcessSingleton();
// Notify another process, if available. Otherwise sets ourselves as the
@@ -178,7 +182,10 @@ class ProcessSingleton {
@@ -177,7 +181,10 @@ class ProcessSingleton {
#endif
private:
@@ -65,7 +65,7 @@ index 9de82e5422428d6419a24401e0479fbd19a15147..a30b3aae436bbe762ada1bdae69ef831
#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 b1721c7034cd082edd2d155b172b8f3aa8d67566..e9419a0b4de2f39730f9478914b307b3bdb45459 100644
index 2de07460462632680f9b16a019744527dcee5125..f7e7edd645f212750704d45a2967f584fab81b35 100644
--- a/chrome/browser/process_singleton_posix.cc
+++ b/chrome/browser/process_singleton_posix.cc
@@ -607,6 +607,7 @@ class ProcessSingleton::LinuxWatcher
@@ -143,10 +143,10 @@ index b1721c7034cd082edd2d155b172b8f3aa8d67566..e9419a0b4de2f39730f9478914b307b3
const NotificationCallback& notification_callback)
: notification_callback_(notification_callback),
+ additional_data_(additional_data),
current_pid_(base::GetCurrentProcId()) {
current_pid_(base::GetCurrentProcId()),
watcher_(new LinuxWatcher(this)) {
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
lock_path_ = user_data_dir.Append(chrome::kSingletonLockFilename);
@@ -896,7 +921,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
@@ -897,7 +922,8 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
sizeof(socket_timeout));
// Found another process, prepare our command line
@@ -156,7 +156,7 @@ index b1721c7034cd082edd2d155b172b8f3aa8d67566..e9419a0b4de2f39730f9478914b307b3
std::string to_send(kStartToken);
to_send.push_back(kTokenDelimiter);
@@ -906,11 +932,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
@@ -907,11 +933,21 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessWithTimeout(
to_send.append(current_dir.value());
const std::vector<std::string>& argv = cmd_line.argv();
@@ -179,7 +179,7 @@ index b1721c7034cd082edd2d155b172b8f3aa8d67566..e9419a0b4de2f39730f9478914b307b3
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 c375a587abd3d575b5c75a0863b01c9611e8287c..33948d69a4a3bf098187b1c383821d1d67be5818 100644
index 0c87fc8ccb4511904f19b76ae5e03a5df6664391..c34d4fe10781e6b9286a43176f7312da4e815caf 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) {

View File

@@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
Date: Fri, 26 Aug 2022 11:24:27 +0200
Subject: fix: revert EmulationHandler update functions to early return
Our debugger class (a subclass of ontent::DevToolsAgentHostClient) isn't
aware of those changes unless we hook RenderFrameHostChanged. If we
don't do this, some navigation lifecycle events
{loadingFinished, dataReceived} emitted by the renderer are lost. We
disconnect and reconnect the webcontents to the DevToolsAgentHost to
prevent this from happening.
As of https://chromium-review.googlesource.com/c/chromium/src/+/3758294
this results in a DCHECK, since DevToolsAgentHost::DisconnectWebContents
results in a call to UpdateDeviceEmulationState and host_ is nullptr. To
fix this, we revert those state update calls to early returns.
Upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/3856525
diff --git a/content/browser/devtools/protocol/emulation_handler.cc b/content/browser/devtools/protocol/emulation_handler.cc
index 84736afeb21d1deec0f4032ef6f3304075d8fffb..d023bb7b5a34c5c055d3d7cc5dc3e04ef43bcc3b 100644
--- a/content/browser/devtools/protocol/emulation_handler.cc
+++ b/content/browser/devtools/protocol/emulation_handler.cc
@@ -565,7 +565,9 @@ WebContentsImpl* EmulationHandler::GetWebContents() {
}
void EmulationHandler::UpdateTouchEventEmulationState() {
- DCHECK(host_);
+ if (!host_)
+ return;
+
// We only have a single TouchEmulator for all frames, so let the main frame's
// EmulationHandler enable/disable it.
DCHECK(!host_->GetParentOrOuterDocument());
@@ -585,7 +587,9 @@ void EmulationHandler::UpdateTouchEventEmulationState() {
}
void EmulationHandler::UpdateDeviceEmulationState() {
- DCHECK(host_);
+ if (!host_)
+ return;
+
// Device emulation only happens on the outermost main frame.
DCHECK(!host_->GetParentOrOuterDocument());

View File

@@ -16,9 +16,42 @@ This patch adds a few changes to the Chromium code:
implementation, along with a parameter `is_app_sandboxed` in the
constructor, to handle the case when the primary app is run with
admin permissions.
4. It adds an `OnBrowserReady` function to allow
`requestSingleInstanceLock` to start listening to the socket later
in the posix implementation. This function is necessary, because
otherwise, users calling `requestSingleInstanceLock` create a
`ProcessSingleton` instance that tries to connect to the socket
before the browser thread is ready.
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index aca2f19f53657fc2b944b69418e099b056c8e734..e43b0d89e2e6ad0bcb2c34bfacc4ef3b59eaaab1 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -1424,7 +1424,6 @@ int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() {
switch (notify_result_) {
case ProcessSingleton::PROCESS_NONE:
// No process already running, fall through to starting a new one.
- process_singleton_->StartWatching();
g_browser_process->platform_part()->PlatformSpecificCommandLineProcessing(
*base::CommandLine::ForCurrentProcess());
break;
diff --git a/chrome/browser/chrome_process_singleton.cc b/chrome/browser/chrome_process_singleton.cc
index d97fa8a96c110acc25b0ef46d7a4ac1c708f7c76..0919af8e8b0a51ef8e8dd28f2f07139d197a7384 100644
--- a/chrome/browser/chrome_process_singleton.cc
+++ b/chrome/browser/chrome_process_singleton.cc
@@ -31,10 +31,6 @@ ProcessSingleton::NotifyResult
return process_singleton_.NotifyOtherProcessOrCreate();
}
-void ChromeProcessSingleton::StartWatching() {
- process_singleton_.StartWatching();
-}
-
void ChromeProcessSingleton::Cleanup() {
process_singleton_.Cleanup();
}
diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h
index 7cd82d27a741f194da5d0b3fcfd9c15c8ea1fa5c..9de82e5422428d6419a24401e0479fbd19a15147 100644
index 7cd82d27a741f194da5d0b3fcfd9c15c8ea1fa5c..5a64220aaf1309832dc0ad543e353de67fe0a779 100644
--- a/chrome/browser/process_singleton.h
+++ b/chrome/browser/process_singleton.h
@@ -102,12 +102,19 @@ class ProcessSingleton {
@@ -41,7 +74,26 @@ index 7cd82d27a741f194da5d0b3fcfd9c15c8ea1fa5c..9de82e5422428d6419a24401e0479fbd
~ProcessSingleton();
// Notify another process, if available. Otherwise sets ourselves as the
@@ -176,6 +183,8 @@ class ProcessSingleton {
@@ -118,6 +125,8 @@ class ProcessSingleton {
// TODO(brettw): Make the implementation of this method non-platform-specific
// by making Linux re-use the Windows implementation.
NotifyResult NotifyOtherProcessOrCreate();
+ void StartListeningOnSocket();
+ void OnBrowserReady();
// Sets ourself up as the singleton instance. Returns true on success. If
// false is returned, we are not the singleton instance and the caller must
@@ -127,9 +136,6 @@ class ProcessSingleton {
// another process should call this directly.
bool Create();
- // Start watching for notifications from other processes.
- void StartWatching();
-
// Clear any lock state during shutdown.
void Cleanup();
@@ -176,6 +182,8 @@ class ProcessSingleton {
#if BUILDFLAG(IS_WIN)
bool EscapeVirtualization(const base::FilePath& user_data_dir);
@@ -50,8 +102,23 @@ index 7cd82d27a741f194da5d0b3fcfd9c15c8ea1fa5c..9de82e5422428d6419a24401e0479fbd
HWND remote_window_; // The HWND_MESSAGE of another browser.
base::win::MessageWindow window_; // The message-only window.
bool is_virtualized_; // Stuck inside Microsoft Softricity VM environment.
@@ -220,12 +228,13 @@ class ProcessSingleton {
// Temporary directory to hold the socket.
base::ScopedTempDir socket_dir_;
- int sock_ = -1;
// Helper class for linux specific messages. LinuxWatcher is ref counted
// because it posts messages between threads.
class LinuxWatcher;
scoped_refptr<LinuxWatcher> watcher_;
+ int sock_ = -1;
+ bool listen_on_ready_ = false;
#endif
#if BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
index 7e4cc9cdd350e814c20feccdcc8d70b1080e60a1..b1721c7034cd082edd2d155b172b8f3aa8d67566 100644
index 7e4cc9cdd350e814c20feccdcc8d70b1080e60a1..2de07460462632680f9b16a019744527dcee5125 100644
--- a/chrome/browser/process_singleton_posix.cc
+++ b/chrome/browser/process_singleton_posix.cc
@@ -54,6 +54,7 @@
@@ -114,7 +181,17 @@ index 7e4cc9cdd350e814c20feccdcc8d70b1080e60a1..b1721c7034cd082edd2d155b172b8f3a
bool ConnectSocket(ScopedSocket* socket,
const base::FilePath& socket_path,
const base::FilePath& cookie_path) {
@@ -768,6 +790,10 @@ ProcessSingleton::ProcessSingleton(
@@ -757,7 +779,8 @@ ProcessSingleton::ProcessSingleton(
const base::FilePath& user_data_dir,
const NotificationCallback& notification_callback)
: notification_callback_(notification_callback),
- current_pid_(base::GetCurrentProcId()) {
+ current_pid_(base::GetCurrentProcId()),
+ watcher_(new LinuxWatcher(this)) {
socket_path_ = user_data_dir.Append(chrome::kSingletonSocketFilename);
lock_path_ = user_data_dir.Append(chrome::kSingletonLockFilename);
cookie_path_ = user_data_dir.Append(chrome::kSingletonCookieFilename);
@@ -768,6 +791,10 @@ ProcessSingleton::ProcessSingleton(
ProcessSingleton::~ProcessSingleton() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -125,7 +202,28 @@ index 7e4cc9cdd350e814c20feccdcc8d70b1080e60a1..b1721c7034cd082edd2d155b172b8f3a
}
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
@@ -1031,14 +1057,32 @@ bool ProcessSingleton::Create() {
@@ -932,6 +959,20 @@ ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
base::Seconds(kTimeoutInSeconds));
}
+void ProcessSingleton::StartListeningOnSocket() {
+ watcher_ = base::MakeRefCounted<LinuxWatcher>(this);
+ content::GetIOThreadTaskRunner({})->PostTask(FROM_HERE,
+ base::BindOnce(&ProcessSingleton::LinuxWatcher::StartListening,
+ watcher_, sock_));
+}
+
+void ProcessSingleton::OnBrowserReady() {
+ if (listen_on_ready_) {
+ StartListeningOnSocket();
+ listen_on_ready_ = false;
+ }
+}
+
ProcessSingleton::NotifyResult
ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate(
const base::CommandLine& command_line,
@@ -1031,14 +1072,32 @@ bool ProcessSingleton::Create() {
#endif
}
@@ -163,8 +261,58 @@ index 7e4cc9cdd350e814c20feccdcc8d70b1080e60a1..b1721c7034cd082edd2d155b172b8f3a
// Check that the directory was created with the correct permissions.
int dir_mode = 0;
CHECK(base::GetPosixFilePermissions(socket_dir_.GetPath(), &dir_mode) &&
@@ -1050,9 +1109,10 @@ bool ProcessSingleton::Create() {
// leaving a dangling symlink.
base::FilePath socket_target_path =
socket_dir_.GetPath().Append(chrome::kSingletonSocketFilename);
+ int sock;
SockaddrUn addr;
socklen_t socklen;
- SetupSocket(socket_target_path.value(), &sock_, &addr, &socklen);
+ SetupSocket(socket_target_path.value(), &sock, &addr, &socklen);
// Setup the socket symlink and the two cookies.
base::FilePath cookie(GenerateCookie());
@@ -1071,26 +1131,24 @@ bool ProcessSingleton::Create() {
return false;
}
- if (bind(sock_, reinterpret_cast<sockaddr*>(&addr), socklen) < 0) {
+ if (bind(sock, reinterpret_cast<sockaddr*>(&addr), socklen) < 0) {
PLOG(ERROR) << "Failed to bind() " << socket_target_path.value();
- CloseSocket(sock_);
+ CloseSocket(sock);
return false;
}
- if (listen(sock_, 5) < 0)
+ if (listen(sock, 5) < 0)
NOTREACHED() << "listen failed: " << base::safe_strerror(errno);
- return true;
-}
+ sock_ = sock;
-void ProcessSingleton::StartWatching() {
- DCHECK_GE(sock_, 0);
- DCHECK(!watcher_);
- watcher_ = new LinuxWatcher(this);
- DCHECK(BrowserThread::IsThreadInitialized(BrowserThread::IO));
- content::GetIOThreadTaskRunner({})->PostTask(
- FROM_HERE, base::BindOnce(&ProcessSingleton::LinuxWatcher::StartListening,
- watcher_, sock_));
+ if (BrowserThread::IsThreadInitialized(BrowserThread::IO)) {
+ StartListeningOnSocket();
+ } else {
+ listen_on_ready_ = true;
+ }
+
+ return true;
}
void ProcessSingleton::Cleanup() {
diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc
index 41bc176510f93ef667e4f1373eab61142f3e264f..c375a587abd3d575b5c75a0863b01c9611e8287c 100644
index 41bc176510f93ef667e4f1373eab61142f3e264f..0c87fc8ccb4511904f19b76ae5e03a5df6664391 100644
--- a/chrome/browser/process_singleton_win.cc
+++ b/chrome/browser/process_singleton_win.cc
@@ -29,7 +29,9 @@
@@ -208,7 +356,16 @@ index 41bc176510f93ef667e4f1373eab61142f3e264f..c375a587abd3d575b5c75a0863b01c96
is_virtualized_(false),
lock_file_(INVALID_HANDLE_VALUE),
user_data_dir_(user_data_dir),
@@ -374,7 +385,7 @@ ProcessSingleton::NotifyOtherProcessOrCreate() {
@@ -368,13 +379,16 @@ ProcessSingleton::NotifyOtherProcessOrCreate() {
return PROFILE_IN_USE;
}
+void ProcessSingleton::StartListeningOnSocket() {}
+void ProcessSingleton::OnBrowserReady() {}
+
// Look for a Chrome instance that uses the same profile directory. If there
// isn't one, create a message window with its title set to the profile
// directory path.
bool ProcessSingleton::Create() {
TRACE_EVENT0("startup", "ProcessSingleton::Create");
@@ -217,7 +374,7 @@ index 41bc176510f93ef667e4f1373eab61142f3e264f..c375a587abd3d575b5c75a0863b01c96
remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) {
@@ -383,7 +394,7 @@ bool ProcessSingleton::Create() {
@@ -383,7 +397,7 @@ bool ProcessSingleton::Create() {
// access. As documented, it's clearer to NOT request ownership on creation
// since it isn't guaranteed we will get it. It is better to create it
// without ownership and explicitly get the ownership afterward.
@@ -226,7 +383,7 @@ index 41bc176510f93ef667e4f1373eab61142f3e264f..c375a587abd3d575b5c75a0863b01c96
if (!only_me.IsValid()) {
DPLOG(FATAL) << "CreateMutex failed";
return false;
@@ -422,6 +433,17 @@ bool ProcessSingleton::Create() {
@@ -422,6 +436,17 @@ bool ProcessSingleton::Create() {
window_.CreateNamed(base::BindRepeating(&ProcessLaunchNotification,
notification_callback_),
user_data_dir_.value());
@@ -244,3 +401,12 @@ index 41bc176510f93ef667e4f1373eab61142f3e264f..c375a587abd3d575b5c75a0863b01c96
CHECK(result && window_.hwnd());
}
}
@@ -430,8 +455,6 @@ bool ProcessSingleton::Create() {
return window_.hwnd() != NULL;
}
-void ProcessSingleton::StartWatching() {}
-
void ProcessSingleton::Cleanup() {
}

View File

@@ -16,7 +16,6 @@ fix_crypto_tests_to_run_with_bssl.patch
fix_account_for_debugger_agent_race_condition.patch
repl_fix_crash_when_sharedarraybuffer_disabled.patch
fix_readbarrier_undefined_symbol_error_on_woa_arm64.patch
chore_fix_-wimplicit-fallthrough.patch
fix_crash_caused_by_gethostnamew_on_windows_7.patch
fix_suppress_clang_-wdeprecated-declarations_in_libuv.patch
fix_serdes_test.patch

View File

@@ -7,10 +7,10 @@ This adds GN build files for Node, so we don't have to build with GYP.
diff --git a/BUILD.gn b/BUILD.gn
new file mode 100644
index 0000000000000000000000000000000000000000..3ae9b93649bb404fbf04dea52ecaa7d664ff03ee
index 0000000000000000000000000000000000000000..9e34a074cfa7dec61c4e11821ba5f1969f393dfb
--- /dev/null
+++ b/BUILD.gn
@@ -0,0 +1,440 @@
@@ -0,0 +1,403 @@
+import("//v8/gni/v8.gni")
+import("node.gni")
+
@@ -53,26 +53,6 @@ index 0000000000000000000000000000000000000000..3ae9b93649bb404fbf04dea52ecaa7d6
+
+ # Allows embedders to override the NODE_MODULE_VERSION define
+ node_module_version = ""
+
+ # Allows downstream packagers (eg. Linux distributions) to build Electron against system shared libraries.
+ use_system_cares = false
+ use_system_nghttp2 = false
+ use_system_llhttp = false
+ use_system_histogram = false
+}
+
+if (is_linux) {
+ import("//build/config/linux/pkg_config.gni")
+ if (use_system_cares) {
+ pkg_config("cares") {
+ packages = [ "libcares" ]
+ }
+ }
+ if (use_system_nghttp2) {
+ pkg_config("nghttp2") {
+ packages = [ "libnghttp2" ]
+ }
+ }
+}
+
+assert(!node_use_dtrace, "node_use_dtrace not supported in GN")
@@ -224,23 +204,17 @@ index 0000000000000000000000000000000000000000..3ae9b93649bb404fbf04dea52ecaa7d6
+component("node_lib") {
+ deps = [
+ ":node_js2c",
+ "deps/cares",
+ "deps/histogram",
+ "deps/googletest:gtest",
+ "deps/llhttp",
+ "deps/nghttp2",
+ "deps/uvwasi",
+ "//third_party/zlib",
+ "//third_party/brotli:dec",
+ "//third_party/brotli:enc",
+ "//v8:v8_libplatform",
+ ]
+ if (use_system_cares) {
+ configs += [ ":cares" ]
+ } else {
+ deps += [ "deps/cares" ]
+ }
+ if (use_system_nghttp2) {
+ configs += [ ":nghttp2" ]
+ } else {
+ deps += [ "deps/nghttp2" ]
+ }
+ public_deps = [
+ "deps/uv",
+ "//electron:electron_js2c",
@@ -250,17 +224,6 @@ index 0000000000000000000000000000000000000000..3ae9b93649bb404fbf04dea52ecaa7d6
+ public_configs = [ ":node_lib_config" ]
+ include_dirs = [ "src" ]
+ libs = []
+ if (use_system_llhttp) {
+ libs += [ "llhttp" ]
+ } else {
+ deps += [ "deps/llhttp" ]
+ }
+ if (use_system_histogram) {
+ libs += [ "hdr_histogram" ]
+ include_dirs += [ "/usr/include/hdr" ]
+ } else {
+ deps += [ "deps/histogram" ]
+ }
+ frameworks = []
+ cflags_cc = [
+ "-Wno-deprecated-declarations",
@@ -709,10 +672,10 @@ index 0000000000000000000000000000000000000000..fb000f8ee7647c375bc190d1729d67bb
+}
diff --git a/deps/nghttp2/BUILD.gn b/deps/nghttp2/BUILD.gn
new file mode 100644
index 0000000000000000000000000000000000000000..8bfecba74d4d90e9fbf0e2cd301118e4adc6cba8
index 0000000000000000000000000000000000000000..9abde472d88923db835b12982b7f2ccb1f260196
--- /dev/null
+++ b/deps/nghttp2/BUILD.gn
@@ -0,0 +1,49 @@
@@ -0,0 +1,47 @@
+config("nghttp2_config") {
+ defines = [ "NGHTTP2_STATICLIB" ]
+ include_dirs = [ "lib/includes" ]
@@ -723,11 +686,9 @@ index 0000000000000000000000000000000000000000..8bfecba74d4d90e9fbf0e2cd301118e4
+ "_U_",
+ "BUILDING_NGHTTP2",
+ "NGHTTP2_STATICLIB",
+ "HAVE_CONFIG_H",
+ ]
+ include_dirs = [ "lib/includes" ]
+ if (is_win) {
+ defines += [ "HAVE_CONFIG_H" ]
+ }
+
+ cflags_c = [
+ "-Wno-implicit-function-declaration",

View File

@@ -1,76 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
Date: Mon, 11 Oct 2021 13:46:24 +0200
Subject: chore: fix -Wimplicit-fallthrough
Upstreamed at https://github.com/nghttp2/nghttp2/pull/1626.
diff --git a/deps/nghttp2/BUILD.gn b/deps/nghttp2/BUILD.gn
index 8bfecba74d4d90e9fbf0e2cd301118e4adc6cba8..63e1149f0c4a39cb944114e5824d6074343301e8 100644
--- a/deps/nghttp2/BUILD.gn
+++ b/deps/nghttp2/BUILD.gn
@@ -16,7 +16,6 @@ static_library("nghttp2") {
cflags_c = [
"-Wno-implicit-function-declaration",
- "-Wno-implicit-fallthrough",
"-Wno-string-plus-int",
"-Wno-unreachable-code-return",
"-Wno-unused-but-set-variable",
diff --git a/deps/nghttp2/lib/nghttp2_hd.c b/deps/nghttp2/lib/nghttp2_hd.c
index 30ee9b88920c0a0bb8f8b714e3deabe0207cac40..010edf48f614c23e971df0f37716275cc1656469 100644
--- a/deps/nghttp2/lib/nghttp2_hd.c
+++ b/deps/nghttp2/lib/nghttp2_hd.c
@@ -1892,7 +1892,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater,
rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
}
- /* fall through */
+ __attribute__((fallthrough));
case NGHTTP2_HD_STATE_INFLATE_START:
case NGHTTP2_HD_STATE_OPCODE:
if ((*in & 0xe0u) == 0x20u) {
@@ -2002,7 +2002,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater,
inflater->left = 0;
inflater->shift = 0;
DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0);
- /* Fall through */
+ __attribute__((fallthrough));
case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN:
rfin = 0;
rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV);
@@ -2086,7 +2086,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater,
inflater->left = 0;
inflater->shift = 0;
DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0);
- /* Fall through */
+ __attribute__((fallthrough));
case NGHTTP2_HD_STATE_READ_VALUELEN:
rfin = 0;
rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV);
diff --git a/deps/nghttp2/lib/nghttp2_session.c b/deps/nghttp2/lib/nghttp2_session.c
index 380a47c1b1e82b015c271e2818aed0baf982aa2d..2f3997709cd07f6f8294f985f60b2e1e4b85a2cf 100644
--- a/deps/nghttp2/lib/nghttp2_session.c
+++ b/deps/nghttp2/lib/nghttp2_session.c
@@ -2644,10 +2644,10 @@ static int session_after_frame_sent1(nghttp2_session *session) {
case NGHTTP2_HCAT_PUSH_RESPONSE:
stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH);
++session->num_outgoing_streams;
- /* Fall through */
+ __attribute__((fallthrough));
case NGHTTP2_HCAT_RESPONSE:
stream->state = NGHTTP2_STREAM_OPENED;
- /* Fall through */
+ __attribute__((fallthrough));
case NGHTTP2_HCAT_HEADERS:
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR);
@@ -5456,7 +5456,7 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
iframe->state = NGHTTP2_IB_READ_HEAD;
- /* Fall through */
+ __attribute__((fallthrough));
case NGHTTP2_IB_READ_HEAD: {
int on_begin_frame_called = 0;

View File

@@ -136,7 +136,7 @@ index 7cd3a2a954ff7d70e6ba7a6f7538648841bc54b2..f89b7158218be60ac10e61484a2d5e5e
diff --git a/deps/uv/src/unix/loop.c b/deps/uv/src/unix/loop.c
index a88e71c339351f2ebcdd6c3f933fc3b1122910ed..353143e5ebecae598425dc036f4458bb7c43bb0b 100644
index a88e71c339351f2ebcdd6c3f933fc3b1122910ed..46fc03264b6cc1a3a4d8faf5ec5a754fc07c9b6d 100644
--- a/deps/uv/src/unix/loop.c
+++ b/deps/uv/src/unix/loop.c
@@ -217,6 +217,11 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
@@ -151,7 +151,7 @@ index a88e71c339351f2ebcdd6c3f933fc3b1122910ed..353143e5ebecae598425dc036f4458bb
if (option != UV_LOOP_BLOCK_SIGNAL)
return UV_ENOSYS;
@@ -226,3 +231,37 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
@@ -226,3 +231,40 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
loop->flags |= UV_LOOP_BLOCK_SIGPROF;
return 0;
}
@@ -183,6 +183,9 @@ index a88e71c339351f2ebcdd6c3f933fc3b1122910ed..353143e5ebecae598425dc036f4458bb
+ if (r == len)
+ return;
+
+ if (!uv_loop_alive(loop))
+ return;
+
+ if (r == -1)
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return;

View File

@@ -48,10 +48,10 @@ const main = async () => {
const child = cp.spawn('node', [
'./node_modules/webpack-cli/bin/cli.js',
'--config', `./build/webpack/${webpackTarget.config}`,
'--stats', 'errors-only',
'--output-path', tmpDir,
'--output-filename', `${webpackTarget.name}.measure.js`,
'--env', 'PRINT_WEBPACK_GRAPH'
'--display', 'errors-only',
`--output-path=${tmpDir}`,
`--output-filename=${webpackTarget.name}.measure.js`,
'--env.PRINT_WEBPACK_GRAPH'
], {
cwd: path.resolve(__dirname, '..')
});

View File

@@ -23,7 +23,7 @@ process.env.PATH = `${process.env.PATH}${path.delimiter}${DEPOT_TOOLS}`;
const IGNORELIST = new Set([
['shell', 'browser', 'resources', 'win', 'resource.h'],
['shell', 'common', 'node_includes.h'],
['spec', 'fixtures', 'pages', 'jquery-3.6.0.min.js'],
['spec-main', 'fixtures', 'pages', 'jquery-3.6.0.min.js'],
['spec', 'ts-smoke', 'electron', 'main.ts'],
['spec', 'ts-smoke', 'electron', 'renderer.ts'],
['spec', 'ts-smoke', 'runner.js']
@@ -113,8 +113,8 @@ const LINTERS = [{
}
}, {
key: 'javascript',
roots: ['build', 'default_app', 'lib', 'npm', 'script', 'spec'],
ignoreRoots: ['spec/node_modules'],
roots: ['build', 'default_app', 'lib', 'npm', 'script', 'spec', 'spec-main'],
ignoreRoots: ['spec/node_modules', 'spec-main/node_modules'],
test: filename => filename.endsWith('.js') || filename.endsWith('.ts'),
run: async (opts, filenames) => {
const eslint = new ESLint({

View File

@@ -13,7 +13,7 @@ const fail = '✗'.red;
const args = require('minimist')(process.argv, {
string: ['runners', 'target'],
boolean: ['buildNativeTests'],
boolean: ['buildNativeTests', 'runTestFilesSeparately'],
unknown: arg => unknownFlags.push(arg)
});
@@ -34,6 +34,7 @@ const NPX_CMD = process.platform === 'win32' ? 'npx.cmd' : 'npx';
const runners = new Map([
['main', { description: 'Main process specs', run: runMainProcessElectronTests }],
['remote', { description: 'Remote based specs', run: runRemoteBasedElectronTests }],
['native', { description: 'Native specs', run: runNativeElectronTests }]
]);
@@ -59,6 +60,7 @@ async function main () {
if (somethingChanged) {
await installSpecModules(path.resolve(__dirname, '..', 'spec'));
await installSpecModules(path.resolve(__dirname, '..', 'spec-main'));
await getSpecHash().then(saveSpecHash);
}
@@ -152,6 +154,26 @@ const specFilter = (file) => {
}
};
async function runTests (specDir, testName) {
if (args.runTestFilesSeparately) {
const getFiles = require('../spec/static/get-files');
const testFiles = await getFiles(path.resolve(__dirname, `../${specDir}`), { filter: specFilter });
const baseElectronDir = path.resolve(__dirname, '..');
unknownArgs.splice(unknownArgs.length, 0, '--files', '');
testFiles.sort().forEach(async (file) => {
unknownArgs.splice((unknownArgs.length - 1), 1, path.relative(baseElectronDir, file));
console.log(`Running tests for ${unknownArgs[unknownArgs.length - 1]}`);
await runTestUsingElectron(specDir, testName);
});
} else {
await runTestUsingElectron(specDir, testName);
}
}
async function runRemoteBasedElectronTests () {
await runTests('spec', 'remote');
}
async function runNativeElectronTests () {
let testTargets = require('./native-test-targets.json');
const outDir = `out/${utils.getOutDir()}`;
@@ -204,7 +226,7 @@ async function runNativeElectronTests () {
}
async function runMainProcessElectronTests () {
await runTestUsingElectron('spec', 'main');
await runTests('spec-main', 'main');
}
async function installSpecModules (dir) {
@@ -239,7 +261,9 @@ function getSpecHash () {
(async () => {
const hasher = crypto.createHash('SHA256');
hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/package.json')));
hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec-main/package.json')));
hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec/yarn.lock')));
hasher.update(fs.readFileSync(path.resolve(__dirname, '../spec-main/yarn.lock')));
hasher.update(fs.readFileSync(path.resolve(__dirname, '../script/spec-runner.js')));
return hasher.digest('hex');
})(),

View File

@@ -715,9 +715,8 @@ void App::OnFinishLaunching(base::Value::Dict launch_info) {
void App::OnPreMainMessageLoopRun() {
content::BrowserChildProcessObserver::Add(this);
if (process_singleton_ && watch_singleton_socket_on_ready_) {
process_singleton_->StartWatching();
watch_singleton_socket_on_ready_ = false;
if (process_singleton_) {
process_singleton_->OnBrowserReady();
}
}
@@ -1125,20 +1124,15 @@ bool App::RequestSingleInstanceLock(gin::Arguments* args) {
#endif
switch (process_singleton_->NotifyOtherProcessOrCreate()) {
case ProcessSingleton::NotifyResult::PROCESS_NONE:
if (content::BrowserThread::IsThreadInitialized(
content::BrowserThread::IO)) {
process_singleton_->StartWatching();
} else {
watch_singleton_socket_on_ready_ = true;
}
return true;
case ProcessSingleton::NotifyResult::LOCK_ERROR:
case ProcessSingleton::NotifyResult::PROFILE_IN_USE:
case ProcessSingleton::NotifyResult::PROCESS_NOTIFIED: {
process_singleton_.reset();
return false;
}
case ProcessSingleton::NotifyResult::PROCESS_NONE:
default: // Shouldn't be needed, but VS warns if it is not there.
return true;
}
}

View File

@@ -264,7 +264,6 @@ class App : public ElectronBrowserClient::Delegate,
bool disable_hw_acceleration_ = false;
bool disable_domain_blocking_for_3DAPIs_ = false;
bool watch_singleton_socket_on_ready_ = false;
};
} // namespace api

View File

@@ -24,6 +24,10 @@
#include "ui/display/win/screen_win.h"
#endif
#if defined(USE_OZONE)
#include "ui/ozone/public/ozone_platform.h"
#endif
namespace electron::api {
gin::WrapperInfo Screen::kWrapperInfo = {gin::kEmbedderNativeGin};
@@ -68,7 +72,18 @@ Screen::~Screen() {
screen_->RemoveObserver(this);
}
gfx::Point Screen::GetCursorScreenPoint() {
gfx::Point Screen::GetCursorScreenPoint(v8::Isolate* isolate) {
#if defined(USE_OZONE)
// Wayland will crash unless a window is created prior to calling
// GetCursorScreenPoint.
if (!ui::OzonePlatform::IsInitialized()) {
gin_helper::ErrorThrower thrower(isolate);
thrower.ThrowError(
"screen.getCursorScreenPoint() cannot be called before a window has "
"been created.");
return gfx::Point();
}
#endif
return screen_->GetCursorScreenPoint();
}

View File

@@ -40,7 +40,7 @@ class Screen : public gin::Wrappable<Screen>,
Screen(v8::Isolate* isolate, display::Screen* screen);
~Screen() override;
gfx::Point GetCursorScreenPoint();
gfx::Point GetCursorScreenPoint(v8::Isolate* isolate);
display::Display GetPrimaryDisplay();
std::vector<display::Display> GetAllDisplays();
display::Display GetDisplayNearestPoint(const gfx::Point& point);

View File

@@ -52,7 +52,6 @@
#include "shell/browser/api/electron_api_net_log.h"
#include "shell/browser/api/electron_api_protocol.h"
#include "shell/browser/api/electron_api_service_worker_context.h"
#include "shell/browser/api/electron_api_web_frame_main.h"
#include "shell/browser/api/electron_api_web_request.h"
#include "shell/browser/browser.h"
#include "shell/browser/electron_browser_context.h"
@@ -66,7 +65,6 @@
#include "shell/common/gin_converters/content_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_converters/media_converter.h"
#include "shell/common/gin_converters/net_converter.h"
#include "shell/common/gin_converters/value_converter.h"
#include "shell/common/gin_helper/dictionary.h"
@@ -75,7 +73,6 @@
#include "shell/common/options_switches.h"
#include "shell/common/process_util.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
@@ -646,22 +643,6 @@ void Session::SetPermissionCheckHandler(v8::Local<v8::Value> val,
permission_manager->SetPermissionCheckHandler(handler);
}
void Session::SetDisplayMediaRequestHandler(v8::Isolate* isolate,
v8::Local<v8::Value> val) {
if (val->IsNull()) {
browser_context_->SetDisplayMediaRequestHandler(
DisplayMediaRequestHandler());
return;
}
DisplayMediaRequestHandler handler;
if (!gin::ConvertFromV8(isolate, val, &handler)) {
gin_helper::ErrorThrower(isolate).ThrowTypeError(
"Display media request handler must be null or a function");
return;
}
browser_context_->SetDisplayMediaRequestHandler(handler);
}
void Session::SetDevicePermissionHandler(v8::Local<v8::Value> val,
gin::Arguments* args) {
ElectronPermissionManager::DeviceCheckHandler handler;
@@ -1217,8 +1198,6 @@ gin::ObjectTemplateBuilder Session::GetObjectTemplateBuilder(
&Session::SetPermissionRequestHandler)
.SetMethod("setPermissionCheckHandler",
&Session::SetPermissionCheckHandler)
.SetMethod("setDisplayMediaRequestHandler",
&Session::SetDisplayMediaRequestHandler)
.SetMethod("setDevicePermissionHandler",
&Session::SetDevicePermissionHandler)
.SetMethod("clearHostResolverCache", &Session::ClearHostResolverCache)

View File

@@ -179,9 +179,6 @@ class Session : public gin::Wrappable<Session>,
#endif
private:
void SetDisplayMediaRequestHandler(v8::Isolate* isolate,
v8::Local<v8::Value> val);
// Cached gin_helper::Wrappable objects.
v8::Global<v8::Value> cookies_;
v8::Global<v8::Value> protocol_;

View File

@@ -175,7 +175,6 @@
#if BUILDFLAG(IS_WIN)
#include "printing/backend/win_helper.h"
#include "shell/browser/native_window_views.h"
#endif
#endif
@@ -1312,7 +1311,7 @@ void WebContents::EnterFullscreenModeForTab(
auto callback =
base::BindRepeating(&WebContents::OnEnterFullscreenModeForTab,
base::Unretained(this), requesting_frame, options);
permission_helper->RequestFullscreenPermission(callback);
permission_helper->RequestFullscreenPermission(requesting_frame, callback);
}
void WebContents::OnEnterFullscreenModeForTab(
@@ -2420,14 +2419,6 @@ void WebContents::OpenDevTools(gin::Arguments* args) {
}
}
#if BUILDFLAG(IS_WIN)
auto* win = static_cast<NativeWindowViews*>(owner_window());
// Force a detached state when WCO is enabled to match Chrome
// behavior and prevent occlusion of DevTools.
if (win && win->IsWindowControlsOverlayEnabled())
state = "detach";
#endif
DCHECK(inspectable_web_contents_);
inspectable_web_contents_->SetDockState(state);
inspectable_web_contents_->ShowDevTools(activate);

View File

@@ -49,6 +49,7 @@
#include "content/public/browser/tts_controller.h"
#include "content/public/browser/tts_platform.h"
#include "content/public/browser/url_loader_request_interceptor.h"
#include "content/public/browser/weak_document_ptr.h"
#include "content/public/common/content_descriptors.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/content_switches.h"
@@ -987,7 +988,7 @@ void ElectronBrowserClient::WebNotificationAllowed(
return;
}
permission_helper->RequestWebNotificationPermission(
base::BindOnce(std::move(callback), web_contents->IsAudioMuted()));
rfh, base::BindOnce(std::move(callback), web_contents->IsAudioMuted()));
}
void ElectronBrowserClient::RenderProcessHostDestroyed(
@@ -1022,6 +1023,7 @@ void OnOpenExternal(const GURL& escaped_url, bool allowed) {
void HandleExternalProtocolInUI(
const GURL& url,
content::WeakDocumentPtr document_ptr,
content::WebContents::OnceGetter web_contents_getter,
bool has_user_gesture) {
content::WebContents* web_contents = std::move(web_contents_getter).Run();
@@ -1033,9 +1035,18 @@ void HandleExternalProtocolInUI(
if (!permission_helper)
return;
content::RenderFrameHost* rfh = document_ptr.AsRenderFrameHostIfValid();
if (!rfh) {
// If the render frame host is not valid it means it was a top level
// navigation and the frame has already been disposed of. In this case we
// take the current main frame and declare it responsible for the
// transition.
rfh = web_contents->GetPrimaryMainFrame();
}
GURL escaped_url(base::EscapeExternalHandlerValue(url.spec()));
auto callback = base::BindOnce(&OnOpenExternal, escaped_url);
permission_helper->RequestOpenExternalPermission(std::move(callback),
permission_helper->RequestOpenExternalPermission(rfh, std::move(callback),
has_user_gesture, url);
}
@@ -1055,6 +1066,9 @@ bool ElectronBrowserClient::HandleExternalProtocol(
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&HandleExternalProtocolInUI, url,
initiator_document
? initiator_document->GetWeakDocumentPtr()
: content::WeakDocumentPtr(),
std::move(web_contents_getter), has_user_gesture));
return true;
}

View File

@@ -31,11 +31,8 @@
#include "content/browser/blob_storage/chrome_blob_storage_context.h" // nogncheck
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/cors_origin_pattern_setter.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents_media_capture_id.h"
#include "media/audio/audio_device_description.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/network_context.mojom.h"
@@ -54,10 +51,7 @@
#include "shell/browser/zoom_level_delegate.h"
#include "shell/common/application_info.h"
#include "shell/common/electron_paths.h"
#include "shell/common/gin_converters/frame_converter.h"
#include "shell/common/gin_helper/error_thrower.h"
#include "shell/common/options_switches.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
#include "extensions/browser/browser_context_keyed_service_factories.h"
@@ -418,131 +412,6 @@ void ElectronBrowserContext::SetSSLConfigClient(
ssl_config_client_ = std::move(client);
}
void ElectronBrowserContext::SetDisplayMediaRequestHandler(
DisplayMediaRequestHandler handler) {
display_media_request_handler_ = handler;
}
void ElectronBrowserContext::DisplayMediaDeviceChosen(
const content::MediaStreamRequest& request,
content::MediaResponseCallback callback,
gin::Arguments* args) {
blink::mojom::StreamDevicesSetPtr stream_devices_set =
blink::mojom::StreamDevicesSet::New();
v8::Local<v8::Value> result;
if (!args->GetNext(&result) || result->IsNullOrUndefined()) {
std::move(callback).Run(
blink::mojom::StreamDevicesSet(),
blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE, nullptr);
return;
}
gin_helper::Dictionary result_dict;
if (!gin::ConvertFromV8(args->isolate(), result, &result_dict)) {
gin_helper::ErrorThrower(args->isolate())
.ThrowTypeError(
"Display Media Request streams callback must be called with null "
"or a valid object");
std::move(callback).Run(
blink::mojom::StreamDevicesSet(),
blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE, nullptr);
return;
}
stream_devices_set->stream_devices.emplace_back(
blink::mojom::StreamDevices::New());
blink::mojom::StreamDevices& devices = *stream_devices_set->stream_devices[0];
bool video_requested =
request.video_type != blink::mojom::MediaStreamType::NO_SERVICE;
bool audio_requested =
request.audio_type != blink::mojom::MediaStreamType::NO_SERVICE;
bool has_video = false;
if (video_requested && result_dict.Has("video")) {
gin_helper::Dictionary video_dict;
std::string id;
std::string name;
content::RenderFrameHost* rfh;
if (result_dict.Get("video", &video_dict) && video_dict.Get("id", &id) &&
video_dict.Get("name", &name)) {
devices.video_device =
blink::MediaStreamDevice(request.video_type, id, name);
} else if (result_dict.Get("video", &rfh)) {
devices.video_device = blink::MediaStreamDevice(
request.video_type,
content::WebContentsMediaCaptureId(rfh->GetProcess()->GetID(),
rfh->GetRoutingID())
.ToString(),
base::UTF16ToUTF8(
content::WebContents::FromRenderFrameHost(rfh)->GetTitle()));
} else {
gin_helper::ErrorThrower(args->isolate())
.ThrowTypeError(
"video must be a WebFrameMain or DesktopCapturerSource");
std::move(callback).Run(
blink::mojom::StreamDevicesSet(),
blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE, nullptr);
return;
}
has_video = true;
}
if (audio_requested && result_dict.Has("audio")) {
gin_helper::Dictionary audio_dict;
std::string id;
std::string name;
content::RenderFrameHost* rfh;
// NB. this is not permitted by the documentation, but is left here as an
// "escape hatch" for providing an arbitrary name/id if needed in the
// future.
if (result_dict.Get("audio", &audio_dict) && audio_dict.Get("id", &id) &&
audio_dict.Get("name", &name)) {
devices.audio_device =
blink::MediaStreamDevice(request.audio_type, id, name);
} else if (result_dict.Get("audio", &rfh)) {
devices.audio_device = blink::MediaStreamDevice(
request.audio_type,
content::WebContentsMediaCaptureId(rfh->GetProcess()->GetID(),
rfh->GetRoutingID(),
/* disable_local_echo= */ true)
.ToString(),
"Tab audio");
} else if (result_dict.Get("audio", &id)) {
devices.audio_device =
blink::MediaStreamDevice(request.audio_type, id, "System audio");
} else {
gin_helper::ErrorThrower(args->isolate())
.ThrowTypeError(
"audio must be a WebFrameMain, \"loopback\" or "
"\"loopbackWithMute\"");
std::move(callback).Run(
blink::mojom::StreamDevicesSet(),
blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE, nullptr);
return;
}
}
if ((video_requested && !has_video)) {
gin_helper::ErrorThrower(args->isolate())
.ThrowTypeError(
"Video was requested, but no video stream was provided");
std::move(callback).Run(
blink::mojom::StreamDevicesSet(),
blink::mojom::MediaStreamRequestResult::CAPTURE_FAILURE, nullptr);
return;
}
std::move(callback).Run(*stream_devices_set,
blink::mojom::MediaStreamRequestResult::OK, nullptr);
}
bool ElectronBrowserContext::ChooseDisplayMediaDevice(
const content::MediaStreamRequest& request,
content::MediaResponseCallback callback) {
if (!display_media_request_handler_)
return false;
DisplayMediaResponseCallbackJs callbackJs =
base::BindOnce(&DisplayMediaDeviceChosen, request, std::move(callback));
display_media_request_handler_.Run(request, std::move(callbackJs));
return true;
}
void ElectronBrowserContext::GrantDevicePermission(
const url::Origin& origin,
const base::Value& device,

View File

@@ -13,10 +13,8 @@
#include "base/memory/weak_ptr.h"
#include "chrome/browser/predictors/preconnect_manager.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/media_stream_request.h"
#include "content/public/browser/resource_context.h"
#include "electron/buildflags/buildflags.h"
#include "gin/arguments.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
@@ -40,13 +38,6 @@ class ElectronExtensionSystem;
}
#endif
namespace v8 {
template <typename T>
class Local;
class Isolate;
class Value;
} // namespace v8
namespace electron {
using DevicePermissionMap =
@@ -60,12 +51,6 @@ class ResolveProxyHelper;
class WebViewManager;
class ProtocolRegistry;
using DisplayMediaResponseCallbackJs =
base::OnceCallback<void(gin::Arguments* args)>;
using DisplayMediaRequestHandler =
base::RepeatingCallback<void(const content::MediaStreamRequest&,
DisplayMediaResponseCallbackJs)>;
class ElectronBrowserContext : public content::BrowserContext {
public:
// disable copy
@@ -165,10 +150,6 @@ class ElectronBrowserContext : public content::BrowserContext {
network::mojom::SSLConfigPtr GetSSLConfig();
void SetSSLConfigClient(mojo::Remote<network::mojom::SSLConfigClient> client);
bool ChooseDisplayMediaDevice(const content::MediaStreamRequest& request,
content::MediaResponseCallback callback);
void SetDisplayMediaRequestHandler(DisplayMediaRequestHandler handler);
~ElectronBrowserContext() override;
// Grants |origin| access to |device|.
@@ -195,11 +176,6 @@ class ElectronBrowserContext : public content::BrowserContext {
bool in_memory,
base::Value::Dict options);
static void DisplayMediaDeviceChosen(
const content::MediaStreamRequest& request,
content::MediaResponseCallback callback,
gin::Arguments* args);
// Initialize pref registry.
void InitPrefs();
@@ -238,8 +214,6 @@ class ElectronBrowserContext : public content::BrowserContext {
network::mojom::SSLConfigPtr ssl_config_;
mojo::Remote<network::mojom::SSLConfigClient> ssl_config_client_;
DisplayMediaRequestHandler display_media_request_handler_;
// In-memory cache that holds objects that have been granted permissions.
DevicePermissionMap granted_devices_;

View File

@@ -50,8 +50,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 22,0,0,20220825
PRODUCTVERSION 22,0,0,20220825
FILEVERSION 21,0,0,3
PRODUCTVERSION 21,0,0,3
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -68,12 +68,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "GitHub, Inc."
VALUE "FileDescription", "Electron"
VALUE "FileVersion", "22.0.0"
VALUE "FileVersion", "21.0.0"
VALUE "InternalName", "electron.exe"
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
VALUE "OriginalFilename", "electron.exe"
VALUE "ProductName", "Electron"
VALUE "ProductVersion", "22.0.0"
VALUE "ProductVersion", "21.0.0"
VALUE "SquirrelAwareVersion", "1"
END
END

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Modified from chrome/browser/ui/views/frame/windows_caption_button.cc
// Modified from chrome/browser/ui/views/frame/windows_10_caption_button.cc
#include "shell/browser/ui/views/win_caption_button.h"

View File

@@ -25,7 +25,7 @@ void DrawRect(gfx::Canvas* canvas,
void DrawRoundRect(gfx::Canvas* canvas,
const gfx::Rect& rect,
float radius,
int radius,
const cc::PaintFlags& flags) {
gfx::RectF rect_f(rect);
float stroke_half_width = flags.getStrokeWidth() / 2;
@@ -74,7 +74,7 @@ void WinIconPainter::PaintMaximizeIcon(gfx::Canvas* canvas,
void WinIconPainter::PaintRestoreIcon(gfx::Canvas* canvas,
const gfx::Rect& symbol_rect,
const cc::PaintFlags& flags) {
const int separation = base::ClampFloor(2 * canvas->image_scale());
const int separation = std::floor(2 * canvas->image_scale());
gfx::Rect icon_rect = symbol_rect;
icon_rect.Inset(gfx::Insets::TLBR(separation, 0, 0, separation));
@@ -113,14 +113,14 @@ void Win11IconPainter::PaintMaximizeIcon(gfx::Canvas* canvas,
cc::PaintFlags paint_flags = flags;
paint_flags.setAntiAlias(true);
const float corner_radius = canvas->image_scale();
const float corner_radius = 2 * canvas->image_scale();
DrawRoundRect(canvas, symbol_rect, corner_radius, flags);
}
void Win11IconPainter::PaintRestoreIcon(gfx::Canvas* canvas,
const gfx::Rect& symbol_rect,
const cc::PaintFlags& flags) {
const int separation = base::ClampFloor(2 * canvas->image_scale());
const int separation = std::floor(2 * canvas->image_scale());
gfx::Rect icon_rect = symbol_rect;
icon_rect.Inset(gfx::Insets::TLBR(separation, 0, 0, separation));
@@ -128,11 +128,14 @@ void Win11IconPainter::PaintRestoreIcon(gfx::Canvas* canvas,
paint_flags.setAntiAlias(true);
// Bottom left ("in front") rounded square.
const float bottom_rect_radius = canvas->image_scale();
const float bottom_rect_radius = 1 * canvas->image_scale();
DrawRoundRect(canvas, icon_rect, bottom_rect_radius, flags);
// Top right ("behind") top+right edges of rounded square (2.5x).
icon_rect.Offset(separation, -separation);
// Apply inset to left+bottom edges since we don't draw arcs for those edges
constexpr int top_rect_inset = 1;
icon_rect.Inset(gfx::Insets::TLBR(0, top_rect_inset, top_rect_inset, 0));
const float top_rect_radius = 2.5f * canvas->image_scale();
DrawRoundRectEdges(canvas, icon_rect, top_rect_radius, flags);

View File

@@ -111,43 +111,19 @@ void MediaAccessAllowed(const content::MediaStreamRequest& request,
request.video_type ==
blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE ||
request.audio_type ==
blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE) {
blink::mojom::MediaStreamType::GUM_TAB_AUDIO_CAPTURE)
HandleUserMediaRequest(request, std::move(callback));
} else if (request.video_type ==
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE ||
request.audio_type ==
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
else if (request.video_type ==
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE ||
request.audio_type ==
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE)
webrtc::MediaStreamDevicesController::RequestPermissions(
request, MediaCaptureDevicesDispatcher::GetInstance(),
base::BindOnce(&OnMediaStreamRequestResponse, std::move(callback)));
} else if (request.video_type ==
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE ||
request.video_type == blink::mojom::MediaStreamType::
DISPLAY_VIDEO_CAPTURE_THIS_TAB ||
request.video_type ==
blink::mojom::MediaStreamType::DISPLAY_VIDEO_CAPTURE_SET ||
request.audio_type ==
blink::mojom::MediaStreamType::DISPLAY_AUDIO_CAPTURE) {
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
request.render_process_id, request.render_frame_id);
if (!rfh)
return;
content::BrowserContext* browser_context = rfh->GetBrowserContext();
ElectronBrowserContext* electron_browser_context =
static_cast<ElectronBrowserContext*>(browser_context);
auto split_callback = base::SplitOnceCallback(std::move(callback));
if (electron_browser_context->ChooseDisplayMediaDevice(
request, std::move(split_callback.second)))
return;
std::move(split_callback.first)
.Run(blink::mojom::StreamDevicesSet(),
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED, nullptr);
} else {
else
std::move(callback).Run(
blink::mojom::StreamDevicesSet(),
blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED, nullptr);
}
} else {
std::move(callback).Run(
blink::mojom::StreamDevicesSet(),
@@ -173,16 +149,16 @@ WebContentsPermissionHelper::WebContentsPermissionHelper(
WebContentsPermissionHelper::~WebContentsPermissionHelper() = default;
void WebContentsPermissionHelper::RequestPermission(
content::RenderFrameHost* requesting_frame,
blink::PermissionType permission,
base::OnceCallback<void(bool)> callback,
bool user_gesture,
base::Value::Dict details) {
auto* rfh = web_contents_->GetPrimaryMainFrame();
auto* permission_manager = static_cast<ElectronPermissionManager*>(
web_contents_->GetBrowserContext()->GetPermissionControllerDelegate());
auto origin = web_contents_->GetLastCommittedURL();
permission_manager->RequestPermissionWithDetails(
permission, rfh, origin, false, std::move(details),
permission, requesting_frame, origin, false, std::move(details),
base::BindOnce(&OnPermissionResponse, std::move(callback)));
}
@@ -198,8 +174,10 @@ bool WebContentsPermissionHelper::CheckPermission(
}
void WebContentsPermissionHelper::RequestFullscreenPermission(
content::RenderFrameHost* requesting_frame,
base::OnceCallback<void(bool)> callback) {
RequestPermission(
requesting_frame,
static_cast<blink::PermissionType>(PermissionType::FULLSCREEN),
std::move(callback));
}
@@ -225,13 +203,17 @@ void WebContentsPermissionHelper::RequestMediaAccessPermission(
// The permission type doesn't matter here, AUDIO_CAPTURE/VIDEO_CAPTURE
// are presented as same type in content_converter.h.
RequestPermission(blink::PermissionType::AUDIO_CAPTURE, std::move(callback),
RequestPermission(content::RenderFrameHost::FromID(request.render_process_id,
request.render_frame_id),
blink::PermissionType::AUDIO_CAPTURE, std::move(callback),
false, std::move(details));
}
void WebContentsPermissionHelper::RequestWebNotificationPermission(
content::RenderFrameHost* requesting_frame,
base::OnceCallback<void(bool)> callback) {
RequestPermission(blink::PermissionType::NOTIFICATIONS, std::move(callback));
RequestPermission(requesting_frame, blink::PermissionType::NOTIFICATIONS,
std::move(callback));
}
void WebContentsPermissionHelper::RequestPointerLockPermission(
@@ -240,6 +222,7 @@ void WebContentsPermissionHelper::RequestPointerLockPermission(
base::OnceCallback<void(content::WebContents*, bool, bool, bool)>
callback) {
RequestPermission(
web_contents_->GetPrimaryMainFrame(),
static_cast<blink::PermissionType>(PermissionType::POINTER_LOCK),
base::BindOnce(std::move(callback), web_contents_, user_gesture,
last_unlocked_by_target),
@@ -247,12 +230,14 @@ void WebContentsPermissionHelper::RequestPointerLockPermission(
}
void WebContentsPermissionHelper::RequestOpenExternalPermission(
content::RenderFrameHost* requesting_frame,
base::OnceCallback<void(bool)> callback,
bool user_gesture,
const GURL& url) {
base::Value::Dict details;
details.Set("externalURL", url.spec());
RequestPermission(
requesting_frame,
static_cast<blink::PermissionType>(PermissionType::OPEN_EXTERNAL),
std::move(callback), user_gesture, std::move(details));
}

View File

@@ -33,7 +33,8 @@ class WebContentsPermissionHelper
};
// Asynchronous Requests
void RequestFullscreenPermission(base::OnceCallback<void(bool)> callback);
void RequestFullscreenPermission(content::RenderFrameHost* requesting_frame,
base::OnceCallback<void(bool)> callback);
void RequestMediaAccessPermission(const content::MediaStreamRequest& request,
content::MediaResponseCallback callback);
void RequestPointerLockPermission(
@@ -42,8 +43,10 @@ class WebContentsPermissionHelper
base::OnceCallback<void(content::WebContents*, bool, bool, bool)>
callback);
void RequestWebNotificationPermission(
content::RenderFrameHost* requesting_frame,
base::OnceCallback<void(bool)> callback);
void RequestOpenExternalPermission(base::OnceCallback<void(bool)> callback,
void RequestOpenExternalPermission(content::RenderFrameHost* requesting_frame,
base::OnceCallback<void(bool)> callback,
bool user_gesture,
const GURL& url);
@@ -56,7 +59,8 @@ class WebContentsPermissionHelper
explicit WebContentsPermissionHelper(content::WebContents* web_contents);
friend class content::WebContentsUserData<WebContentsPermissionHelper>;
void RequestPermission(blink::PermissionType permission,
void RequestPermission(content::RenderFrameHost* requesting_frame,
blink::PermissionType permission,
base::OnceCallback<void(bool)> callback,
bool user_gesture = false,
base::Value::Dict details = {});

View File

@@ -26,19 +26,6 @@ v8::Local<v8::Value> Converter<content::RenderFrameHost*>::ToV8(
return electron::api::WebFrameMain::From(isolate, val).ToV8();
}
// static
bool Converter<content::RenderFrameHost*>::FromV8(
v8::Isolate* isolate,
v8::Local<v8::Value> val,
content::RenderFrameHost** out) {
electron::api::WebFrameMain* web_frame_main = nullptr;
if (!ConvertFromV8(isolate, val, &web_frame_main))
return false;
*out = web_frame_main->render_frame_host();
return true;
}
// static
v8::Local<v8::Value>
Converter<gin_helper::AccessorValue<content::RenderFrameHost*>>::ToV8(

View File

@@ -18,9 +18,6 @@ template <>
struct Converter<content::RenderFrameHost*> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
content::RenderFrameHost* val);
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
content::RenderFrameHost** out);
};
template <>

View File

@@ -1,36 +0,0 @@
// Copyright (c) 2021 Slack Technologies, LLC.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/common/gin_converters/media_converter.h"
#include <string>
#include <utility>
#include "content/public/browser/media_stream_request.h"
#include "content/public/browser/render_frame_host.h"
#include "gin/data_object_builder.h"
#include "shell/common/gin_converters/frame_converter.h"
#include "shell/common/gin_converters/gurl_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
namespace gin {
v8::Local<v8::Value> Converter<content::MediaStreamRequest>::ToV8(
v8::Isolate* isolate,
const content::MediaStreamRequest& request) {
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
request.render_process_id, request.render_frame_id);
return gin::DataObjectBuilder(isolate)
.Set("frame", rfh)
.Set("securityOrigin", request.security_origin)
.Set("userGesture", request.user_gesture)
.Set("videoRequested",
request.video_type != blink::mojom::MediaStreamType::NO_SERVICE)
.Set("audioRequested",
request.audio_type != blink::mojom::MediaStreamType::NO_SERVICE)
.Build();
}
} // namespace gin

View File

@@ -1,26 +0,0 @@
// Copyright (c) 2021 Slack Technologies, LLC.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ELECTRON_SHELL_COMMON_GIN_CONVERTERS_MEDIA_CONVERTER_H_
#define ELECTRON_SHELL_COMMON_GIN_CONVERTERS_MEDIA_CONVERTER_H_
#include "gin/converter.h"
#include "third_party/blink/public/common/mediastream/media_stream_request.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom-forward.h"
namespace content {
struct MediaStreamRequest;
}
namespace gin {
template <>
struct Converter<content::MediaStreamRequest> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const content::MediaStreamRequest& request);
};
} // namespace gin
#endif // ELECTRON_SHELL_COMMON_GIN_CONVERTERS_MEDIA_CONVERTER_H_

View File

@@ -37,6 +37,7 @@
#include "shell/common/mac/main_application_bundle.h"
#include "shell/common/node_includes.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_initializer.h" // nogncheck
#include "third_party/electron_node/src/debug_utils.h"
#if !defined(MAS_BUILD)
#include "shell/common/crash_keys.h"
@@ -136,7 +137,7 @@ void stop_and_close_uv_loop(uv_loop_t* loop) {
break;
DCHECK_EQ(0, uv_loop_alive(loop));
uv_loop_close(loop);
node::CheckedUvLoopClose(loop);
}
bool g_is_initialized = false;

View File

@@ -0,0 +1,60 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/renderer/guest_view_container.h"
#include <map>
#include <utility>
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/renderer/render_frame.h"
#include "ui/gfx/geometry/size.h"
namespace electron {
namespace {
using GuestViewContainerMap = std::map<int, GuestViewContainer*>;
static base::LazyInstance<GuestViewContainerMap>::DestructorAtExit
g_guest_view_container_map = LAZY_INSTANCE_INITIALIZER;
} // namespace
GuestViewContainer::GuestViewContainer(content::RenderFrame* render_frame) {}
GuestViewContainer::~GuestViewContainer() {
if (element_instance_id_ > 0)
g_guest_view_container_map.Get().erase(element_instance_id_);
}
// static
GuestViewContainer* GuestViewContainer::FromID(int element_instance_id) {
GuestViewContainerMap* guest_view_containers =
g_guest_view_container_map.Pointer();
auto it = guest_view_containers->find(element_instance_id);
return it == guest_view_containers->end() ? nullptr : it->second;
}
void GuestViewContainer::RegisterElementResizeCallback(
const ResizeCallback& callback) {
element_resize_callback_ = callback;
}
void GuestViewContainer::SetElementInstanceID(int element_instance_id) {
element_instance_id_ = element_instance_id;
g_guest_view_container_map.Get().insert(
std::make_pair(element_instance_id, this));
}
void GuestViewContainer::DidResizeElement(const gfx::Size& new_size) {
if (element_resize_callback_.is_null())
return;
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(element_resize_callback_, new_size));
}
} // namespace electron

View File

@@ -0,0 +1,47 @@
// Copyright (c) 2015 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ELECTRON_SHELL_RENDERER_GUEST_VIEW_CONTAINER_H_
#define ELECTRON_SHELL_RENDERER_GUEST_VIEW_CONTAINER_H_
#include "base/callback.h"
namespace content {
class RenderFrame;
}
namespace gfx {
class Size;
}
namespace electron {
class GuestViewContainer {
public:
typedef base::RepeatingCallback<void(const gfx::Size&)> ResizeCallback;
explicit GuestViewContainer(content::RenderFrame* render_frame);
virtual ~GuestViewContainer();
// disable copy
GuestViewContainer(const GuestViewContainer&) = delete;
GuestViewContainer& operator=(const GuestViewContainer&) = delete;
static GuestViewContainer* FromID(int element_instance_id);
void RegisterElementResizeCallback(const ResizeCallback& callback);
void SetElementInstanceID(int element_instance_id);
void DidResizeElement(const gfx::Size& new_size);
private:
int element_instance_id_;
ResizeCallback element_resize_callback_;
base::WeakPtrFactory<GuestViewContainer> weak_ptr_factory_{this};
};
} // namespace electron
#endif // ELECTRON_SHELL_RENDERER_GUEST_VIEW_CONTAINER_H_

19
spec-main/.eslintrc Normal file
View File

@@ -0,0 +1,19 @@
{
"env": {
"browser": true,
"mocha": true,
"serviceworker": true
},
"globals": {
"Bindings": true,
"Components": true,
"UI": true,
"WebView": true
},
"plugins": [
"mocha"
],
"rules": {
"mocha/no-exclusive-tests": "error"
}
}

View File

@@ -12,7 +12,7 @@ import { closeWindow, closeAllWindows } from './window-helpers';
import { ifdescribe, ifit, waitUntil } from './spec-helpers';
import split = require('split')
const fixturesPath = path.resolve(__dirname, 'fixtures');
const fixturesPath = path.resolve(__dirname, '../spec/fixtures');
describe('electron module', () => {
it('does not expose internal modules to require', () => {

View File

@@ -7,7 +7,7 @@ import { defer, ifit, startRemoteControlApp } from './spec-helpers';
import { areColorsSimilar, captureScreen, getPixelColor } from './screen-helpers';
describe('BrowserView module', () => {
const fixtures = path.resolve(__dirname, 'fixtures');
const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures');
let w: BrowserWindow;
let view: BrowserView;

View File

@@ -5,7 +5,7 @@ import * as fs from 'fs';
import * as qs from 'querystring';
import * as http from 'http';
import { AddressInfo } from 'net';
import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents } from 'electron/main';
import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents, BrowserWindowConstructorOptions } from 'electron/main';
import { emittedOnce, emittedUntil, emittedNTimes } from './events-helpers';
import { ifit, ifdescribe, defer, delay } from './spec-helpers';
@@ -13,7 +13,7 @@ import { closeWindow, closeAllWindows } from './window-helpers';
import { areColorsSimilar, captureScreen, CHROMA_COLOR_HEX, getPixelColor } from './screen-helpers';
const features = process._linkedBinding('electron_common_features');
const fixtures = path.resolve(__dirname, 'fixtures');
const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures');
const mainFixtures = path.resolve(__dirname, 'fixtures');
// Is the display's scale factor possibly causing rounding of pixel coordinate
@@ -59,7 +59,7 @@ describe('BrowserWindow module', () => {
});
ifit(process.platform === 'linux')('does not crash when setting large window icons', async () => {
const appPath = path.join(fixtures, 'apps', 'xwindow-icon');
const appPath = path.join(__dirname, 'spec-main', 'fixtures', 'apps', 'xwindow-icon');
const appProcess = childProcess.spawn(process.execPath, [appPath]);
await new Promise((resolve) => { appProcess.once('exit', resolve); });
});
@@ -3085,7 +3085,7 @@ describe('BrowserWindow module', () => {
expect(argv).to.include('--enable-sandbox');
});
it('should open windows with the options configured via setWindowOpenHandler handlers', async () => {
it('should open windows with the options configured via new-window event listeners', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
@@ -3176,6 +3176,30 @@ describe('BrowserWindow module', () => {
});
});
it('supports calling preventDefault on new-window events', (done) => {
const w = new BrowserWindow({
show: false,
webPreferences: {
sandbox: true
}
});
const initialWebContents = webContents.getAllWebContents().map((i) => i.id);
w.webContents.once('new-window', (e) => {
e.preventDefault();
// We need to give it some time so the windows get properly disposed (at least on OSX).
setTimeout(() => {
const currentWebContents = webContents.getAllWebContents().map((i) => i.id);
try {
expect(currentWebContents).to.deep.equal(initialWebContents);
done();
} catch (error) {
done(e);
}
}, 100);
});
w.loadFile(path.join(fixtures, 'pages', 'window-open.html'));
});
it('validates process APIs access in sandboxed renderer', async () => {
const w = new BrowserWindow({
show: false,
@@ -3331,7 +3355,7 @@ describe('BrowserWindow module', () => {
w.loadFile(path.join(fixtures, 'api', 'new-window-webview.html'));
await webviewLoaded;
});
it('should open windows with the options configured via setWindowOpenHandler handlers', async () => {
it('should open windows with the options configured via new-window event listeners', async () => {
const preloadPath = path.join(mainFixtures, 'api', 'new-window-preload.js');
w.webContents.setWindowOpenHandler(() => ({
action: 'allow',
@@ -3701,6 +3725,94 @@ describe('BrowserWindow module', () => {
});
});
describe('new-window event', () => {
afterEach(closeAllWindows);
it('emits when window.open is called', (done) => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
w.webContents.once('new-window', (e, url, frameName, disposition, options) => {
e.preventDefault();
try {
expect(url).to.equal('http://host/');
expect(frameName).to.equal('host');
expect((options as any)['this-is-not-a-standard-feature']).to.equal(true);
done();
} catch (e) {
done(e);
}
});
w.loadFile(path.join(fixtures, 'pages', 'window-open.html'));
});
it('emits when window.open is called with no webPreferences', (done) => {
const w = new BrowserWindow({ show: false });
w.webContents.once('new-window', function (e, url, frameName, disposition, options) {
e.preventDefault();
try {
expect(url).to.equal('http://host/');
expect(frameName).to.equal('host');
expect((options as any)['this-is-not-a-standard-feature']).to.equal(true);
done();
} catch (e) {
done(e);
}
});
w.loadFile(path.join(fixtures, 'pages', 'window-open.html'));
});
it('emits when link with target is called', (done) => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
w.webContents.once('new-window', (e, url, frameName) => {
e.preventDefault();
try {
expect(url).to.equal('http://host/');
expect(frameName).to.equal('target');
done();
} catch (e) {
done(e);
}
});
w.loadFile(path.join(fixtures, 'pages', 'target-name.html'));
});
it('includes all properties', async () => {
const w = new BrowserWindow({ show: false });
const p = new Promise<{
url: string,
frameName: string,
disposition: string,
options: BrowserWindowConstructorOptions,
additionalFeatures: string[],
referrer: Electron.Referrer,
postBody: Electron.PostBody
}>((resolve) => {
w.webContents.once('new-window', (e, url, frameName, disposition, options, additionalFeatures, referrer, postBody) => {
e.preventDefault();
resolve({ url, frameName, disposition, options, additionalFeatures, referrer, postBody });
});
});
w.loadURL(`data:text/html,${encodeURIComponent(`
<form target="_blank" method="POST" id="form" action="http://example.com/test">
<input type="text" name="post-test-key" value="post-test-value"></input>
</form>
<script>form.submit()</script>
`)}`);
const { url, frameName, disposition, options, additionalFeatures, referrer, postBody } = await p;
expect(url).to.equal('http://example.com/test');
expect(frameName).to.equal('');
expect(disposition).to.equal('foreground-tab');
expect(options).to.be.an('object').not.null();
expect(referrer.policy).to.equal('strict-origin-when-cross-origin');
expect(referrer.url).to.equal('');
expect(additionalFeatures).to.deep.equal([]);
expect(postBody.data).to.have.length(1);
expect(postBody.data[0].type).to.equal('rawData');
expect((postBody.data[0] as any).bytes).to.deep.equal(Buffer.from('post-test-key=post-test-value'));
expect(postBody.contentType).to.equal('application/x-www-form-urlencoded');
});
});
ifdescribe(process.platform !== 'linux')('max/minimize events', () => {
afterEach(closeAllWindows);
it('emits an event when window is maximized', async () => {

View File

@@ -610,7 +610,7 @@ ifdescribe(!isLinuxOnArm && !process.mas && !process.env.DISABLE_CRASH_REPORTER_
describe('when not started', () => {
it('does not prevent process from crashing', async () => {
const appPath = path.join(__dirname, 'fixtures', 'api', 'cookie-app');
const appPath = path.join(__dirname, '..', 'spec', 'fixtures', 'api', 'cookie-app');
await runApp(appPath);
});
});

View File

@@ -7,7 +7,7 @@ import { closeAllWindows } from './window-helpers';
import { emittedOnce, emittedUntil } from './events-helpers';
describe('debugger module', () => {
const fixtures = path.resolve(__dirname, 'fixtures');
const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures');
let w: BrowserWindow;
beforeEach(() => {

View File

@@ -1,5 +1,5 @@
import { expect } from 'chai';
import * as deprecate from '../lib/common/deprecate';
import { deprecate } from 'electron/main';
describe('deprecate', () => {
let throwing: boolean;

View File

@@ -5,7 +5,7 @@ import { emittedOnce } from './events-helpers';
import { closeWindow } from './window-helpers';
describe('ipcRenderer module', () => {
const fixtures = path.join(__dirname, 'fixtures');
const fixtures = path.join(__dirname, '..', 'spec', 'fixtures');
let w: BrowserWindow;
before(async () => {

View File

@@ -1,361 +0,0 @@
import { expect } from 'chai';
import { BrowserWindow, session, desktopCapturer } from 'electron/main';
import { closeAllWindows } from './window-helpers';
import * as http from 'http';
import { ifdescribe, ifit } from './spec-helpers';
const features = process._linkedBinding('electron_common_features');
ifdescribe(features.isDesktopCapturerEnabled())('setDisplayMediaRequestHandler', () => {
afterEach(closeAllWindows);
// These tests are done on an http server because navigator.userAgentData
// requires a secure context.
let server: http.Server;
let serverUrl: string;
before(async () => {
server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
res.end('');
});
await new Promise<void>(resolve => server.listen(0, '127.0.0.1', resolve));
serverUrl = `http://localhost:${(server.address() as any).port}`;
});
after(() => {
server.close();
});
// NOTE(nornagon): this test fails on our macOS CircleCI runners with the
// error message:
// [ERROR:video_capture_device_client.cc(659)] error@ OnStart@content/browser/media/capture/desktop_capture_device_mac.cc:98, CGDisplayStreamCreate failed, OS message: Value too large to be stored in data type (84)
// This is possibly related to the OS/VM setup that CircleCI uses for macOS.
// Our arm64 runners are in @jkleinsc's office, and are real machines, so the
// test works there.
ifit(!(process.platform === 'darwin' && process.arch === 'x64'))('works when calling getDisplayMedia', async function () {
if ((await desktopCapturer.getSources({ types: ['screen'] })).length === 0) { return this.skip(); }
const ses = session.fromPartition('' + Math.random());
let requestHandlerCalled = false;
let mediaRequest: any = null;
ses.setDisplayMediaRequestHandler((request, callback) => {
requestHandlerCalled = true;
mediaRequest = request;
desktopCapturer.getSources({ types: ['screen'] }).then((sources) => {
// Grant access to the first screen found.
const { id, name } = sources[0];
callback({
video: { id, name }
// TODO: 'loopback' and 'loopbackWithMute' are currently only supported on Windows.
// audio: { id: 'loopback', name: 'System Audio' }
});
});
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getDisplayMedia({
video: true,
audio: false,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(requestHandlerCalled).to.be.true();
expect(mediaRequest.videoRequested).to.be.true();
expect(mediaRequest.audioRequested).to.be.false();
expect(ok).to.be.true(message);
});
it('does not crash when using a bogus ID', async () => {
const ses = session.fromPartition('' + Math.random());
let requestHandlerCalled = false;
ses.setDisplayMediaRequestHandler((request, callback) => {
requestHandlerCalled = true;
callback({
video: { id: 'bogus', name: 'whatever' }
});
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(requestHandlerCalled).to.be.true();
expect(ok).to.be.false();
expect(message).to.equal('Could not start video source');
});
it('does not crash when providing only audio for a video request', async () => {
const ses = session.fromPartition('' + Math.random());
let requestHandlerCalled = false;
let callbackError: any;
ses.setDisplayMediaRequestHandler((request, callback) => {
requestHandlerCalled = true;
try {
callback({
audio: 'loopback'
});
} catch (e) {
callbackError = e;
}
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getDisplayMedia({
video: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(requestHandlerCalled).to.be.true();
expect(ok).to.be.false();
expect(callbackError?.message).to.equal('Video was requested, but no video stream was provided');
});
it('does not crash when providing only an audio stream for an audio+video request', async () => {
const ses = session.fromPartition('' + Math.random());
let requestHandlerCalled = false;
let callbackError: any;
ses.setDisplayMediaRequestHandler((request, callback) => {
requestHandlerCalled = true;
try {
callback({
audio: 'loopback'
});
} catch (e) {
callbackError = e;
}
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(requestHandlerCalled).to.be.true();
expect(ok).to.be.false();
expect(callbackError?.message).to.equal('Video was requested, but no video stream was provided');
});
it('does not crash when providing a non-loopback audio stream', async () => {
const ses = session.fromPartition('' + Math.random());
let requestHandlerCalled = false;
ses.setDisplayMediaRequestHandler((request, callback) => {
requestHandlerCalled = true;
callback({
video: w.webContents.mainFrame,
audio: 'default' as any
});
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(requestHandlerCalled).to.be.true();
expect(ok).to.be.true();
});
it('does not crash when providing no streams', async () => {
const ses = session.fromPartition('' + Math.random());
let requestHandlerCalled = false;
let callbackError: any;
ses.setDisplayMediaRequestHandler((request, callback) => {
requestHandlerCalled = true;
try {
callback({});
} catch (e) {
callbackError = e;
}
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(requestHandlerCalled).to.be.true();
expect(ok).to.be.false();
expect(callbackError.message).to.equal('Video was requested, but no video stream was provided');
});
it('does not crash when using a bogus web-contents-media-stream:// ID', async () => {
const ses = session.fromPartition('' + Math.random());
let requestHandlerCalled = false;
ses.setDisplayMediaRequestHandler((request, callback) => {
requestHandlerCalled = true;
callback({
video: { id: 'web-contents-media-stream://9999:9999', name: 'whatever' }
});
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(requestHandlerCalled).to.be.true();
// This is a little surprising... apparently chrome will generate a stream
// for this non-existent web contents?
expect(ok).to.be.true();
});
it('is not called when calling getUserMedia', async () => {
const ses = session.fromPartition('' + Math.random());
ses.setDisplayMediaRequestHandler(() => {
throw new Error('bad');
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(ok).to.be.true(message);
});
it('works when calling getDisplayMedia with preferCurrentTab', async () => {
const ses = session.fromPartition('' + Math.random());
let requestHandlerCalled = false;
ses.setDisplayMediaRequestHandler((request, callback) => {
requestHandlerCalled = true;
callback({ video: w.webContents.mainFrame });
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getDisplayMedia({
preferCurrentTab: true,
video: true,
audio: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(requestHandlerCalled).to.be.true();
expect(ok).to.be.true(message);
});
ifit(!(process.platform === 'darwin' && process.arch === 'x64'))('can supply a screen response to preferCurrentTab', async () => {
const ses = session.fromPartition('' + Math.random());
let requestHandlerCalled = false;
ses.setDisplayMediaRequestHandler(async (request, callback) => {
requestHandlerCalled = true;
const sources = await desktopCapturer.getSources({ types: ['screen'] });
callback({ video: sources[0] });
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getDisplayMedia({
preferCurrentTab: true,
video: true,
audio: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(requestHandlerCalled).to.be.true();
expect(ok).to.be.true(message);
});
it('can supply a frame response', async () => {
const ses = session.fromPartition('' + Math.random());
let requestHandlerCalled = false;
ses.setDisplayMediaRequestHandler(async (request, callback) => {
requestHandlerCalled = true;
callback({ video: w.webContents.mainFrame });
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getDisplayMedia({
video: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(requestHandlerCalled).to.be.true();
expect(ok).to.be.true(message);
});
it('is not called when calling legacy getUserMedia', async () => {
const ses = session.fromPartition('' + Math.random());
ses.setDisplayMediaRequestHandler(() => {
throw new Error('bad');
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
new Promise((resolve, reject) => navigator.getUserMedia({
video: true,
audio: true,
}, x => resolve({ok: x instanceof MediaStream}), e => reject({ok: false, message: e.message})))
`);
expect(ok).to.be.true(message);
});
it('is not called when calling legacy getUserMedia with desktop capture constraint', async () => {
const ses = session.fromPartition('' + Math.random());
ses.setDisplayMediaRequestHandler(() => {
throw new Error('bad');
});
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
new Promise((resolve, reject) => navigator.getUserMedia({
video: {
mandatory: {
chromeMediaSource: 'desktop'
}
},
}, x => resolve({ok: x instanceof MediaStream}), e => reject({ok: false, message: e.message})))
`);
expect(ok).to.be.true(message);
});
it('works when calling getUserMedia without a media request handler', async () => {
const w = new BrowserWindow({ show: false });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(ok).to.be.true(message);
});
it('works when calling legacy getUserMedia without a media request handler', async () => {
const w = new BrowserWindow({ show: false });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
new Promise((resolve, reject) => navigator.getUserMedia({
video: true,
audio: true,
}, x => resolve({ok: x instanceof MediaStream}), e => reject({ok: false, message: e.message})))
`);
expect(ok).to.be.true(message);
});
it('can remove a displayMediaRequestHandler', async () => {
const ses = session.fromPartition('' + Math.random());
ses.setDisplayMediaRequestHandler(() => {
throw new Error('bad');
});
ses.setDisplayMediaRequestHandler(null);
const w = new BrowserWindow({ show: false, webPreferences: { session: ses } });
await w.loadURL(serverUrl);
const { ok, message } = await w.webContents.executeJavaScript(`
navigator.mediaDevices.getDisplayMedia({
video: true,
}).then(x => ({ok: x instanceof MediaStream}), e => ({ok: false, message: e.message}))
`);
expect(ok).to.be.false();
expect(message).to.equal('Not supported');
});
});

View File

@@ -4,7 +4,7 @@ import { ifdescribe, ifit } from './spec-helpers';
import * as path from 'path';
describe('nativeImage module', () => {
const fixturesPath = path.join(__dirname, 'fixtures');
const fixturesPath = path.join(__dirname, '..', 'spec', 'fixtures');
const imageLogo = {
path: path.join(fixturesPath, 'assets', 'logo.png'),

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