mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
42 Commits
v22.0.0-ni
...
v21.0.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
605ee9e28a | ||
|
|
e48878ea63 | ||
|
|
e78aa6af16 | ||
|
|
fb0bbf0100 | ||
|
|
5e8b4bd86f | ||
|
|
da47103ad7 | ||
|
|
7b62386296 | ||
|
|
018bc0993e | ||
|
|
f9b02358c6 | ||
|
|
23aa6e5084 | ||
|
|
65a2c18560 | ||
|
|
c8b3e23f31 | ||
|
|
4ab9295e62 | ||
|
|
0e79733404 | ||
|
|
38d408a26b | ||
|
|
0ad176fd13 | ||
|
|
a3cfd1a206 | ||
|
|
63fe34c703 | ||
|
|
c99a52eab4 | ||
|
|
95eb6ea772 | ||
|
|
8d6c29ecbc | ||
|
|
637824e086 | ||
|
|
cb6fa8268f | ||
|
|
76e8536ca8 | ||
|
|
b075434447 | ||
|
|
9673d29ebe | ||
|
|
5e6d152231 | ||
|
|
b31a4b4394 | ||
|
|
28ca18a912 | ||
|
|
8caea9006a | ||
|
|
855536bcb9 | ||
|
|
1978a0b5fe | ||
|
|
fe70152b43 | ||
|
|
9b38a25206 | ||
|
|
a66d3bf206 | ||
|
|
752b10f16f | ||
|
|
5803a67963 | ||
|
|
40058af821 | ||
|
|
64b40ea69c | ||
|
|
7c6e16975f | ||
|
|
c9e7f33f7e | ||
|
|
98aa0ef090 |
@@ -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
|
||||
|
||||
15
.github/workflows/electron_woa_testing.yml
vendored
15
.github/workflows/electron_woa_testing.yml
vendored
@@ -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
4
.gitignore
vendored
@@ -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
|
||||
@@ -1 +1 @@
|
||||
22.0.0-nightly.20220825
|
||||
21.0.0-beta.3
|
||||
@@ -5,7 +5,7 @@
|
||||
[](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
|
||||
|
||||
458
appveyor.yml
458
appveyor.yml
@@ -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 )
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
root_extra_deps = [ "//electron/spec-chromium:spec" ]
|
||||
root_extra_deps = [ "//electron/spec" ]
|
||||
|
||||
dcheck_always_on = true
|
||||
is_debug = false
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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" ]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
3
docs/api/structures/new-window-web-contents-event.md
Normal file
3
docs/api/structures/new-window-web-contents-event.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# NewWindowWebContentsEvent Object extends `Event`
|
||||
|
||||
* `newGuest` BrowserWindow (optional)
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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 dependency’s 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.
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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', {
|
||||
|
||||
@@ -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
135
lib/common/api/deprecate.ts
Normal 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;
|
||||
@@ -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 }
|
||||
];
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
];
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
16
package.json
16
package.json
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
179
patches/chromium/cherry-pick-9b5207569882.patch
Normal file
179
patches/chromium/cherry-pick-9b5207569882.patch
Normal 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);
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, '..')
|
||||
});
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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');
|
||||
})(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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 = {});
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 <>
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
|
||||
60
shell/renderer/guest_view_container.cc
Normal file
60
shell/renderer/guest_view_container.cc
Normal 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
|
||||
47
shell/renderer/guest_view_container.h
Normal file
47
shell/renderer/guest_view_container.h
Normal 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
19
spec-main/.eslintrc
Normal 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"
|
||||
}
|
||||
}
|
||||
0
spec/.gitignore → spec-main/.gitignore
vendored
0
spec/.gitignore → spec-main/.gitignore
vendored
@@ -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', () => {
|
||||
@@ -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;
|
||||
@@ -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 () => {
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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(() => {
|
||||
@@ -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;
|
||||
@@ -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 () => {
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -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
Reference in New Issue
Block a user