mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Compare commits
15 Commits
v17.0.0-ni
...
test-bake-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
855d43b6f8 | ||
|
|
6d7578e073 | ||
|
|
b5ab394d88 | ||
|
|
1615677ca1 | ||
|
|
dd357b92f5 | ||
|
|
054c11440e | ||
|
|
8e289b7c4d | ||
|
|
ccda7791fa | ||
|
|
71efde8535 | ||
|
|
b89b77b315 | ||
|
|
6074302214 | ||
|
|
f35f82b7cd | ||
|
|
5f37faec41 | ||
|
|
470587a2aa | ||
|
|
a41cd1df0b |
@@ -1 +1 @@
|
||||
17.0.0-nightly.20211005
|
||||
17.0.0-nightly.20210930
|
||||
80
appveyor-bake.yml
Normal file
80
appveyor-bake.yml
Normal file
@@ -0,0 +1,80 @@
|
||||
# The config expects the following environment variables to be set:
|
||||
# - "GN_CONFIG" Build type. One of {'testing', 'release'}.
|
||||
# - "GN_EXTRA_ARGS" Additional gn arguments for a build config,
|
||||
# e.g. 'target_cpu="x86"' to build for a 32bit platform.
|
||||
# https://gn.googlesource.com/gn/+/master/docs/reference.md#target_cpu
|
||||
# Don't forget to set up "NPM_CONFIG_ARCH" and "TARGET_ARCH" accordningly
|
||||
# if you pass a custom value for 'target_cpu'.
|
||||
# - "ELECTRON_RELEASE" Set it to '1' upload binaries on success.
|
||||
# - "NPM_CONFIG_ARCH" E.g. 'x86'. Is used to build native Node.js modules.
|
||||
# Must match 'target_cpu' passed to "GN_EXTRA_ARGS" and "TARGET_ARCH" value.
|
||||
# - "TARGET_ARCH" Choose from {'ia32', 'x64', 'arm', 'arm64', 'mips64el'}.
|
||||
# Is used in some publishing scripts, but does NOT affect the Electron binary.
|
||||
# Must match 'target_cpu' passed to "GN_EXTRA_ARGS" and "NPM_CONFIG_ARCH" value.
|
||||
# - "UPLOAD_TO_S3" Set it to '1' upload a release to the S3 bucket.
|
||||
# Otherwise the release will be uploaded to the Github Releases.
|
||||
# (The value is only checked if "ELECTRON_RELEASE" is defined.)
|
||||
#
|
||||
# The publishing scripts expect access tokens to be defined as env vars,
|
||||
# but those are not covered here.
|
||||
#
|
||||
# AppVeyor docs on variables:
|
||||
# https://www.appveyor.com/docs/environment-variables/
|
||||
# https://www.appveyor.com/docs/build-configuration/#secure-variables
|
||||
# https://www.appveyor.com/docs/build-configuration/#custom-environment-variables
|
||||
|
||||
# Uncomment these lines to enable RDP
|
||||
#on_finish:
|
||||
# - ps: >-
|
||||
# if ($env:TARGET_ARCH -eq 'x64') {
|
||||
# echo "SETTING BAKE IMAGE"
|
||||
# $env:APPVEYOR_RDP_PASSWORD = "electron"
|
||||
# $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
# }
|
||||
version: 1.0.{build}
|
||||
build_cloud: electron-16-core2
|
||||
image: base-electron
|
||||
environment:
|
||||
GIT_CACHE_PATH: C:\Users\appveyor\libcc_cache
|
||||
ELECTRON_OUT_DIR: Default
|
||||
ELECTRON_ENABLE_STACK_DUMPING: 1
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
MOCHA_MULTI_REPORTERS: mocha-appveyor-reporter, tap
|
||||
GOMA_FALLBACK_ON_AUTH_FAILURE: true
|
||||
DEPOT_TOOLS_WIN_TOOLCHAIN: 0
|
||||
PYTHONIOENCODING: UTF-8
|
||||
build_script:
|
||||
- echo "Building $env:GN_CONFIG build"
|
||||
- git config --global core.longpaths true
|
||||
- cd ..
|
||||
- mkdir src
|
||||
- ps: git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
- ps: $env:PATH="$pwd\depot_tools;$env:PATH"
|
||||
- ps: Move-Item $env:APPVEYOR_BUILD_FOLDER -Destination src\electron
|
||||
- src\electron\script\setup-win-for-dev.bat
|
||||
- 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"
|
||||
- gclient sync --with_branch_heads --with_tags
|
||||
after_build:
|
||||
- ps: |
|
||||
if ($env:TARGET_ARCH -eq 'x64') {
|
||||
echo "SETTING BAKE IMAGE"
|
||||
$env:APPVEYOR_BAKE_IMAGE = 'electron-test-bake-image-jk'
|
||||
} else {
|
||||
echo "SKIPPING BAKE IMAGE for $env:TARGET_ARCH"
|
||||
}
|
||||
|
||||
on_image_bake:
|
||||
- ps: >-
|
||||
echo "Baking image: $env:APPVEYOR_BAKE_IMAGE at dir $PWD"
|
||||
Remove-Item $pwd\depot_tools
|
||||
Remove-Item $pwd\src\electron
|
||||
106
appveyor.yml
106
appveyor.yml
@@ -24,47 +24,31 @@
|
||||
# https://www.appveyor.com/docs/build-configuration/#custom-environment-variables
|
||||
|
||||
# Uncomment these lines to enable RDP
|
||||
#on_finish:
|
||||
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
||||
on_finish:
|
||||
- ps: >-
|
||||
if ($env:TARGET_ARCH -eq 'x64') {
|
||||
echo "SETTING BAKE IMAGE"
|
||||
$env:APPVEYOR_RDP_PASSWORD = "electron"
|
||||
$blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
}
|
||||
version: 1.0.{build}
|
||||
build_cloud: electron-16-core
|
||||
image: vs2019bt-16.6.2
|
||||
build_cloud: electron-16-core2
|
||||
image: electron-test-bake-image
|
||||
environment:
|
||||
GIT_CACHE_PATH: C:\Users\electron\libcc_cache
|
||||
GIT_CACHE_PATH: C:\Users\appveyor\libcc_cache
|
||||
ELECTRON_OUT_DIR: Default
|
||||
ELECTRON_ENABLE_STACK_DUMPING: 1
|
||||
MOCHA_REPORTER: mocha-multi-reporters
|
||||
MOCHA_MULTI_REPORTERS: mocha-appveyor-reporter, tap
|
||||
GOMA_FALLBACK_ON_AUTH_FAILURE: true
|
||||
notifications:
|
||||
- provider: Webhook
|
||||
url: https://electron-mission-control.herokuapp.com/rest/appveyor-hook
|
||||
method: POST
|
||||
headers:
|
||||
x-mission-control-secret:
|
||||
secure: 90BLVPcqhJPG7d24v0q/RRray6W3wDQ8uVQlQjOHaBWkw1i8FoA1lsjr2C/v1dVok+tS2Pi6KxDctPUkwIb4T27u4RhvmcPzQhVpfwVJAG9oNtq+yKN7vzHfg7k/pojEzVdJpQLzeJGcSrZu7VY39Q==
|
||||
on_build_success: false
|
||||
on_build_failure: true
|
||||
on_build_status_changed: false
|
||||
DEPOT_TOOLS_WIN_TOOLCHAIN: 0
|
||||
PYTHONIOENCODING: UTF-8
|
||||
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
|
||||
|
||||
$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
|
||||
}
|
||||
}
|
||||
- echo "Building $env:GN_CONFIG build"
|
||||
- git config --global core.longpaths true
|
||||
- cd ..
|
||||
- mkdir src
|
||||
- update_depot_tools.bat
|
||||
# - ps: git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
- ps: $env:PATH="$pwd\depot_tools;$env:PATH"
|
||||
- ps: Move-Item $env:APPVEYOR_BUILD_FOLDER -Destination src\electron
|
||||
- ps: $env:CHROMIUM_BUILDTOOLS_PATH="$pwd\src\buildtools"
|
||||
- ps: >-
|
||||
@@ -77,58 +61,7 @@ build_script:
|
||||
--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"
|
||||
}
|
||||
}
|
||||
- gclient sync --with_branch_heads --with_tags
|
||||
- ps: >-
|
||||
if (Test-Path 'env:RAW_GOMA_AUTH') {
|
||||
$env:GOMA_OAUTH2_CONFIG_FILE = "$pwd\.goma_oauth2_config"
|
||||
@@ -142,9 +75,8 @@ build_script:
|
||||
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
|
||||
- cd src
|
||||
- ps: node -e "console.log(require('./src/utils/goma.js').ensure())"
|
||||
- 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
|
||||
@@ -205,7 +137,7 @@ test_script:
|
||||
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"
|
||||
Write-Host "Skipping tests for $env:GN_CONFIG build"
|
||||
}
|
||||
- cd electron
|
||||
# CalculateNativeWinOcclusion is disabled due to https://bugs.chromium.org/p/chromium/issues/detail?id=1139022
|
||||
@@ -230,4 +162,4 @@ deploy_script:
|
||||
}
|
||||
} elseif (Test-Path Env:\TEST_WOA) {
|
||||
node script/release/ci-release-build.js --job=electron-woa-testing --ci=VSTS --armTest --appveyorJobId=$env:APPVEYOR_JOB_ID $env:APPVEYOR_REPO_BRANCH
|
||||
}
|
||||
}
|
||||
@@ -500,6 +500,16 @@ gets emitted.
|
||||
**Note:** Extra command line arguments might be added by Chromium,
|
||||
such as `--original-process-start-time`.
|
||||
|
||||
### Event: 'desktop-capturer-get-sources'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
|
||||
Emitted when `desktopCapturer.getSources()` is called in the renderer process of `webContents`.
|
||||
Calling `event.preventDefault()` will make it return empty sources.
|
||||
|
||||
## Methods
|
||||
|
||||
The `app` object has the following methods:
|
||||
@@ -1062,7 +1072,7 @@ indicates success while any other value indicates failure according to Chromium
|
||||
Linux.
|
||||
* `secureDnsMode` String (optional) - Can be "off", "automatic" or "secure".
|
||||
Configures the DNS-over-HTTP mode. When "off", no DoH lookups will be
|
||||
performed. When "automatic", DoH lookups will be performed first if DoH is
|
||||
performed. When "automatic", DoH lookups will be peformed first if DoH is
|
||||
available, and insecure DNS lookups will be performed as a fallback. When
|
||||
"secure", only DoH lookups will be performed. Defaults to "automatic".
|
||||
* `secureDnsServers` String[] (optional) - A list of DNS-over-HTTP
|
||||
|
||||
@@ -61,6 +61,12 @@ throttling in one window, you can take the hack of
|
||||
|
||||
Forces the maximum disk space to be used by the disk cache, in bytes.
|
||||
|
||||
### --enable-api-filtering-logging
|
||||
|
||||
Enables caller stack logging for the following APIs (filtering events):
|
||||
|
||||
* `desktopCapturer.getSources()` / `desktop-capturer-get-sources`
|
||||
|
||||
### --enable-logging[=file]
|
||||
|
||||
Prints Chromium's logging to stderr (or a log file).
|
||||
|
||||
@@ -3,49 +3,40 @@
|
||||
> Access information about media sources that can be used to capture audio and
|
||||
> video from the desktop using the [`navigator.mediaDevices.getUserMedia`] API.
|
||||
|
||||
Process: [Main](../glossary.md#main-process)
|
||||
Process: [Main](../glossary.md#main-process), [Renderer](../glossary.md#renderer-process)
|
||||
|
||||
The following example shows how to capture video from a desktop window whose
|
||||
title is `Electron`:
|
||||
|
||||
```javascript
|
||||
// In the main process.
|
||||
// In the renderer process.
|
||||
const { desktopCapturer } = require('electron')
|
||||
|
||||
desktopCapturer.getSources({ types: ['window', 'screen'] }).then(async sources => {
|
||||
for (const source of sources) {
|
||||
if (source.name === 'Electron') {
|
||||
mainWindow.webContents.send('SET_SOURCE', source.id)
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'desktop',
|
||||
chromeMediaSourceId: source.id,
|
||||
minWidth: 1280,
|
||||
maxWidth: 1280,
|
||||
minHeight: 720,
|
||||
maxHeight: 720
|
||||
}
|
||||
}
|
||||
})
|
||||
handleStream(stream)
|
||||
} catch (e) {
|
||||
handleError(e)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
```javascript
|
||||
// In the preload script.
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
ipcRenderer.on('SET_SOURCE', async (event, sourceId) => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'desktop',
|
||||
chromeMediaSourceId: sourceId,
|
||||
minWidth: 1280,
|
||||
maxWidth: 1280,
|
||||
minHeight: 720,
|
||||
maxHeight: 720
|
||||
}
|
||||
}
|
||||
})
|
||||
handleStream(stream)
|
||||
} catch (e) {
|
||||
handleError(e)
|
||||
}
|
||||
})
|
||||
|
||||
function handleStream (stream) {
|
||||
const video = document.querySelector('video')
|
||||
|
||||
@@ -856,6 +856,15 @@ Returns:
|
||||
|
||||
Emitted when the renderer process sends a synchronous message via `ipcRenderer.sendSync()`.
|
||||
|
||||
#### Event: 'desktop-capturer-get-sources'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
|
||||
Emitted when `desktopCapturer.getSources()` is called in the renderer process.
|
||||
Calling `event.preventDefault()` will make it return empty sources.
|
||||
|
||||
#### Event: 'preferred-size-changed'
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Modules to control application life and create native browser window
|
||||
const { app, BrowserWindow, ipcMain, shell, dialog } = require('electron')
|
||||
const { app, BrowserWindow, ipcMain, shell } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
let mainWindow;
|
||||
|
||||
@@ -142,6 +142,7 @@ auto_filenames = {
|
||||
"lib/common/web-view-methods.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/web-frame.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
@@ -223,6 +224,7 @@ auto_filenames = {
|
||||
"lib/browser/api/web-contents.ts",
|
||||
"lib/browser/api/web-frame-main.ts",
|
||||
"lib/browser/default-menu.ts",
|
||||
"lib/browser/desktop-capturer.ts",
|
||||
"lib/browser/devtools.ts",
|
||||
"lib/browser/guest-view-manager.ts",
|
||||
"lib/browser/guest-window-manager.ts",
|
||||
@@ -269,6 +271,7 @@ auto_filenames = {
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
@@ -306,6 +309,7 @@ auto_filenames = {
|
||||
"lib/common/webpack-provider.ts",
|
||||
"lib/renderer/api/context-bridge.ts",
|
||||
"lib/renderer/api/crash-reporter.ts",
|
||||
"lib/renderer/api/desktop-capturer.ts",
|
||||
"lib/renderer/api/exports/electron.ts",
|
||||
"lib/renderer/api/ipc-renderer.ts",
|
||||
"lib/renderer/api/module-list.ts",
|
||||
|
||||
@@ -1,72 +1,5 @@
|
||||
const { createDesktopCapturer } = process._linkedBinding('electron_browser_desktop_capturer');
|
||||
import { getSourcesImpl } from '@electron/internal/browser/desktop-capturer';
|
||||
|
||||
const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b);
|
||||
|
||||
let currentlyRunning: {
|
||||
options: ElectronInternal.GetSourcesOptions;
|
||||
getSources: Promise<ElectronInternal.GetSourcesResult[]>;
|
||||
}[] = [];
|
||||
|
||||
// |options.types| can't be empty and must be an array
|
||||
function isValid (options: Electron.SourcesOptions) {
|
||||
const types = options ? options.types : undefined;
|
||||
return Array.isArray(types);
|
||||
}
|
||||
|
||||
export async function getSources (args: Electron.SourcesOptions) {
|
||||
if (!isValid(args)) throw new Error('Invalid options');
|
||||
|
||||
const captureWindow = args.types.includes('window');
|
||||
const captureScreen = args.types.includes('screen');
|
||||
|
||||
const { thumbnailSize = { width: 150, height: 150 } } = args;
|
||||
const { fetchWindowIcons = false } = args;
|
||||
|
||||
const options = {
|
||||
captureWindow,
|
||||
captureScreen,
|
||||
thumbnailSize,
|
||||
fetchWindowIcons
|
||||
};
|
||||
|
||||
for (const running of currentlyRunning) {
|
||||
if (deepEqual(running.options, options)) {
|
||||
// If a request is currently running for the same options
|
||||
// return that promise
|
||||
return running.getSources;
|
||||
}
|
||||
}
|
||||
|
||||
const getSources = new Promise<ElectronInternal.GetSourcesResult[]>((resolve, reject) => {
|
||||
let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer();
|
||||
|
||||
const stopRunning = () => {
|
||||
if (capturer) {
|
||||
delete capturer._onerror;
|
||||
delete capturer._onfinished;
|
||||
capturer = null;
|
||||
}
|
||||
// Remove from currentlyRunning once we resolve or reject
|
||||
currentlyRunning = currentlyRunning.filter(running => running.options !== options);
|
||||
};
|
||||
|
||||
capturer._onerror = (error: string) => {
|
||||
stopRunning();
|
||||
reject(error);
|
||||
};
|
||||
|
||||
capturer._onfinished = (sources: Electron.DesktopCapturerSource[]) => {
|
||||
stopRunning();
|
||||
resolve(sources);
|
||||
};
|
||||
|
||||
capturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons);
|
||||
});
|
||||
|
||||
currentlyRunning.push({
|
||||
options,
|
||||
getSources
|
||||
});
|
||||
|
||||
return getSources;
|
||||
export async function getSources (options: Electron.SourcesOptions) {
|
||||
return getSourcesImpl(null, options);
|
||||
}
|
||||
|
||||
82
lib/browser/desktop-capturer.ts
Normal file
82
lib/browser/desktop-capturer.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
const { createDesktopCapturer } = process._linkedBinding('electron_browser_desktop_capturer');
|
||||
|
||||
const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b);
|
||||
|
||||
let currentlyRunning: {
|
||||
options: ElectronInternal.GetSourcesOptions;
|
||||
getSources: Promise<ElectronInternal.GetSourcesResult[]>;
|
||||
}[] = [];
|
||||
|
||||
// |options.types| can't be empty and must be an array
|
||||
function isValid (options: Electron.SourcesOptions) {
|
||||
const types = options ? options.types : undefined;
|
||||
return Array.isArray(types);
|
||||
}
|
||||
|
||||
export const getSourcesImpl = (sender: Electron.WebContents | null, args: Electron.SourcesOptions) => {
|
||||
if (!isValid(args)) throw new Error('Invalid options');
|
||||
|
||||
const captureWindow = args.types.includes('window');
|
||||
const captureScreen = args.types.includes('screen');
|
||||
|
||||
const { thumbnailSize = { width: 150, height: 150 } } = args;
|
||||
const { fetchWindowIcons = false } = args;
|
||||
|
||||
const options = {
|
||||
captureWindow,
|
||||
captureScreen,
|
||||
thumbnailSize,
|
||||
fetchWindowIcons
|
||||
};
|
||||
|
||||
for (const running of currentlyRunning) {
|
||||
if (deepEqual(running.options, options)) {
|
||||
// If a request is currently running for the same options
|
||||
// return that promise
|
||||
return running.getSources;
|
||||
}
|
||||
}
|
||||
|
||||
const getSources = new Promise<ElectronInternal.GetSourcesResult[]>((resolve, reject) => {
|
||||
let capturer: ElectronInternal.DesktopCapturer | null = createDesktopCapturer();
|
||||
|
||||
const stopRunning = () => {
|
||||
if (capturer) {
|
||||
delete capturer._onerror;
|
||||
delete capturer._onfinished;
|
||||
capturer = null;
|
||||
}
|
||||
// Remove from currentlyRunning once we resolve or reject
|
||||
currentlyRunning = currentlyRunning.filter(running => running.options !== options);
|
||||
if (sender) {
|
||||
sender.removeListener('destroyed', stopRunning);
|
||||
}
|
||||
};
|
||||
|
||||
capturer._onerror = (error: string) => {
|
||||
stopRunning();
|
||||
reject(error);
|
||||
};
|
||||
|
||||
capturer._onfinished = (sources: Electron.DesktopCapturerSource[]) => {
|
||||
stopRunning();
|
||||
resolve(sources);
|
||||
};
|
||||
|
||||
capturer.startHandling(captureWindow, captureScreen, thumbnailSize, fetchWindowIcons);
|
||||
|
||||
// If the WebContents is destroyed before receiving result, just remove the
|
||||
// reference to emit and the capturer itself so that it never dispatches
|
||||
// back to the renderer
|
||||
if (sender) {
|
||||
sender.once('destroyed', stopRunning);
|
||||
}
|
||||
});
|
||||
|
||||
currentlyRunning.push({
|
||||
options,
|
||||
getSources
|
||||
});
|
||||
|
||||
return getSources;
|
||||
};
|
||||
@@ -1,9 +1,30 @@
|
||||
import { app } from 'electron/main';
|
||||
import type { WebContents } from 'electron/main';
|
||||
import { clipboard } from 'electron/common';
|
||||
import * as fs from 'fs';
|
||||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
||||
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
import type * as desktopCapturerModule from '@electron/internal/browser/desktop-capturer';
|
||||
|
||||
const eventBinding = process._linkedBinding('electron_browser_event');
|
||||
|
||||
const emitCustomEvent = function (contents: WebContents, eventName: string, ...args: any[]) {
|
||||
const event = eventBinding.createWithSender(contents);
|
||||
|
||||
app.emit(eventName, event, contents, ...args);
|
||||
contents.emit(eventName, event, ...args);
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
const logStack = function (contents: WebContents, code: string, stack: string) {
|
||||
if (stack) {
|
||||
console.warn(`WebContents (${contents.id}): ${code}`, stack);
|
||||
}
|
||||
};
|
||||
|
||||
// Implements window.close()
|
||||
ipcMainInternal.on(IPC_MESSAGES.BROWSER_WINDOW_CLOSE, function (event) {
|
||||
const window = event.sender.getOwnerBrowserWindow();
|
||||
@@ -37,6 +58,22 @@ ipcMainUtils.handleSync(IPC_MESSAGES.BROWSER_CLIPBOARD_SYNC, function (event, me
|
||||
return (clipboard as any)[method](...args);
|
||||
});
|
||||
|
||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||
const desktopCapturer = require('@electron/internal/browser/desktop-capturer') as typeof desktopCapturerModule;
|
||||
|
||||
ipcMainInternal.handle(IPC_MESSAGES.DESKTOP_CAPTURER_GET_SOURCES, async function (event, options: Electron.SourcesOptions, stack: string) {
|
||||
logStack(event.sender, 'desktopCapturer.getSources()', stack);
|
||||
const customEvent = emitCustomEvent(event.sender, 'desktop-capturer-get-sources');
|
||||
|
||||
if (customEvent.defaultPrevented) {
|
||||
console.error('Blocked desktopCapturer.getSources()');
|
||||
return [];
|
||||
}
|
||||
|
||||
return await desktopCapturer.getSourcesImpl(event.sender, options);
|
||||
});
|
||||
}
|
||||
|
||||
const getPreloadScript = async function (preloadPath: string) {
|
||||
let preloadSrc = null;
|
||||
let preloadError = null;
|
||||
|
||||
@@ -28,4 +28,6 @@ export const enum IPC_MESSAGES {
|
||||
INSPECTOR_CONFIRM = 'INSPECTOR_CONFIRM',
|
||||
INSPECTOR_CONTEXT_MENU = 'INSPECTOR_CONTEXT_MENU',
|
||||
INSPECTOR_SELECT_FILE = 'INSPECTOR_SELECT_FILE',
|
||||
|
||||
DESKTOP_CAPTURER_GET_SOURCES = 'DESKTOP_CAPTURER_GET_SOURCES',
|
||||
}
|
||||
|
||||
24
lib/renderer/api/desktop-capturer.ts
Normal file
24
lib/renderer/api/desktop-capturer.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import deprecate from '@electron/internal/common/api/deprecate';
|
||||
import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
|
||||
|
||||
const { hasSwitch } = process._linkedBinding('electron_common_command_line');
|
||||
|
||||
const enableStacks = hasSwitch('enable-api-filtering-logging');
|
||||
|
||||
function getCurrentStack () {
|
||||
const target = {};
|
||||
if (enableStacks) {
|
||||
Error.captureStackTrace(target, getCurrentStack);
|
||||
}
|
||||
return (target as any).stack;
|
||||
}
|
||||
|
||||
let warned = process.noDeprecation;
|
||||
export async function getSources (options: Electron.SourcesOptions) {
|
||||
if (!warned) {
|
||||
deprecate.log('The use of \'desktopCapturer.getSources\' in the renderer process is deprecated and will be removed. See https://www.electronjs.org/docs/breaking-changes#removed-desktopcapturergetsources-in-the-renderer for more details.');
|
||||
warned = true;
|
||||
}
|
||||
return await ipcRendererInternal.invoke(IPC_MESSAGES.DESKTOP_CAPTURER_GET_SOURCES, options, getCurrentStack());
|
||||
}
|
||||
@@ -5,3 +5,10 @@ export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
{ name: 'ipcRenderer', loader: () => require('./ipc-renderer') },
|
||||
{ name: 'webFrame', loader: () => require('./web-frame') }
|
||||
];
|
||||
|
||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||
rendererModuleList.push({
|
||||
name: 'desktopCapturer',
|
||||
loader: () => require('@electron/internal/renderer/api/desktop-capturer')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,3 +26,10 @@ export const moduleList: ElectronInternal.ModuleEntry[] = [
|
||||
private: true
|
||||
}
|
||||
];
|
||||
|
||||
if (BUILDFLAG(ENABLE_DESKTOP_CAPTURER)) {
|
||||
moduleList.push({
|
||||
name: 'desktopCapturer',
|
||||
loader: () => require('@electron/internal/renderer/api/desktop-capturer')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "17.0.0-nightly.20211005",
|
||||
"version": "17.0.0-nightly.20210930",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -3,4 +3,3 @@ expose_aes-cfb.patch
|
||||
expose_des-ede3.patch
|
||||
fix_sync_evp_get_cipherbynid_and_evp_get_cipherbyname.patch
|
||||
add_maskhash_to_rsa_pss_params_st_for_compat.patch
|
||||
enable_x509_v_flag_trusted_first_flag.patch
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Juan Cruz Viotti <jv@jviotti.com>
|
||||
Date: Thu, 30 Sep 2021 13:39:23 -0400
|
||||
Subject: Enable X509_V_FLAG_TRUSTED_FIRST flag
|
||||
|
||||
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
|
||||
|
||||
diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c
|
||||
index 5a881d64c30076404cc800fff9e943bb0b30d2ac..29d5341efc8eb7ae6f90bdde5a8032e99f75c98e 100644
|
||||
--- a/crypto/x509/x509_vpm.c
|
||||
+++ b/crypto/x509/x509_vpm.c
|
||||
@@ -528,7 +528,7 @@ static const X509_VERIFY_PARAM default_table[] = {
|
||||
(char *)"default", /* X509 default parameters */
|
||||
0, /* Check time */
|
||||
0, /* internal flags */
|
||||
- 0, /* flags */
|
||||
+ X509_V_FLAG_TRUSTED_FIRST, /* flags */
|
||||
0, /* purpose */
|
||||
0, /* trust */
|
||||
100, /* depth */
|
||||
@@ -1,9 +1,8 @@
|
||||
REM Parameters vs_buildtools.exe download link and wsdk version
|
||||
@ECHO OFF
|
||||
|
||||
SET buildtools_link=https://download.visualstudio.microsoft.com/download/pr/d7691cc1-82e6-434f-8e9f-a612f85b4b76/c62179f8cbbb58d4af22c21e8d4e122165f21615f529c94fad5cc7e012f1ef08/vs_BuildTools.exe
|
||||
SET wsdk10_link=https://go.microsoft.com/fwlink/p/?LinkId=845298
|
||||
SET wsdk=10SDK.18362
|
||||
SET wsdk10_link=https://go.microsoft.com/fwlink/?linkid=2120843
|
||||
SET wsdk=10SDK.19041
|
||||
|
||||
REM Check for disk space
|
||||
Rem 543210987654321
|
||||
@@ -44,24 +43,6 @@ IF NOT "%1"=="" (
|
||||
|
||||
if not exist "C:\TEMP\" mkdir C:\TEMP
|
||||
|
||||
REM Download vs_buildtools.exe to C:\TEMP\vs_buildtools.exe
|
||||
powershell -command "& { iwr %buildtools_link% -OutFile C:\TEMP\vs_buildtools.exe }"
|
||||
|
||||
REM Install Visual Studio Toolchain
|
||||
C:\TEMP\vs_buildtools.exe --quiet --wait --norestart --nocache ^
|
||||
--installPath "%ProgramFiles(x86)%/Microsoft Visual Studio/2019/Community" ^
|
||||
--add Microsoft.VisualStudio.Workload.VCTools ^
|
||||
--add Microsoft.VisualStudio.Component.VC.140 ^
|
||||
--add Microsoft.VisualStudio.Component.VC.ATLMFC ^
|
||||
--add Microsoft.VisualStudio.Component.VC.Tools.ARM64 ^
|
||||
--add Microsoft.VisualStudio.Component.VC.MFC.ARM64 ^
|
||||
--add Microsoft.VisualStudio.Component.Windows%wsdk% ^
|
||||
--includeRecommended
|
||||
|
||||
REM Install Windows SDK
|
||||
powershell -command "& { iwr %wsdk10_link% -OutFile C:\TEMP\wsdk10.exe }"
|
||||
C:\TEMP\wsdk10.exe /features /quiet
|
||||
|
||||
REM Install chocolatey to further install dependencies
|
||||
set chocolateyUseWindowsCompression='true'
|
||||
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" ^
|
||||
@@ -69,8 +50,15 @@ set chocolateyUseWindowsCompression='true'
|
||||
-Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))"
|
||||
SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
|
||||
|
||||
REM Install Visual Studio Toolchain
|
||||
choco install visualstudio2019buildtools --package-parameters "--quiet --wait --norestart --nocache --installPath ""%ProgramFiles(x86)%/Microsoft Visual Studio/2019/Community"" --add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.140 --add Microsoft.VisualStudio.Component.VC.ATLMFC --add Microsoft.VisualStudio.Component.VC.Tools.ARM64 --add Microsoft.VisualStudio.Component.VC.MFC.ARM64 --add Microsoft.VisualStudio.Component.Windows%wsdk% --includeRecommended"
|
||||
|
||||
REM Install Windows SDK
|
||||
powershell -command "& { iwr %wsdk10_link% -OutFile C:\TEMP\wsdk10.exe }"
|
||||
C:\TEMP\wsdk10.exe /features /quiet
|
||||
|
||||
REM Install nodejs python git and yarn needed dependencies
|
||||
choco install -y nodejs python2 git yarn windows-sdk-10-version-1903-windbg
|
||||
choco install -y nodejs python2 git yarn choco install windows-sdk-10-version-2004-windbg
|
||||
call C:\ProgramData\chocolatey\bin\RefreshEnv.cmd
|
||||
SET PATH=C:\Python27\;C:\Python27\Scripts;%PATH%
|
||||
|
||||
|
||||
@@ -47,10 +47,7 @@ printing::PrinterList GetPrinterList() {
|
||||
// TODO(deepak1556): Deprecate this api in favor of an
|
||||
// async version and post a non blocing task call.
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
printing::mojom::ResultCode code =
|
||||
print_backend->EnumeratePrinters(&printers);
|
||||
if (code != printing::mojom::ResultCode::kSuccess)
|
||||
LOG(INFO) << "Failed to enumerate printers";
|
||||
print_backend->EnumeratePrinters(&printers);
|
||||
}
|
||||
return printers;
|
||||
}
|
||||
|
||||
@@ -424,7 +424,7 @@ bool IsDeviceNameValid(const std::u16string& device_name) {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::pair<std::string, std::u16string> GetDefaultPrinterAsync() {
|
||||
std::u16string GetDefaultPrinterAsync() {
|
||||
#if defined(OS_WIN)
|
||||
// Blocking is needed here because Windows printer drivers are oftentimes
|
||||
// not thread-safe and have to be accessed on the UI thread.
|
||||
@@ -435,25 +435,18 @@ std::pair<std::string, std::u16string> GetDefaultPrinterAsync() {
|
||||
printing::PrintBackend::CreateInstance(
|
||||
g_browser_process->GetApplicationLocale());
|
||||
std::string printer_name;
|
||||
printing::mojom::ResultCode code =
|
||||
print_backend->GetDefaultPrinterName(printer_name);
|
||||
print_backend->GetDefaultPrinterName(printer_name);
|
||||
|
||||
// We don't want to return if this fails since some devices won't have a
|
||||
// default printer.
|
||||
if (code != printing::mojom::ResultCode::kSuccess)
|
||||
LOG(ERROR) << "Failed to get default printer name";
|
||||
|
||||
// Check for existing printers and pick the first one should it exist.
|
||||
// Some devices won't have a default printer, so we should
|
||||
// also check for existing printers and pick the first
|
||||
// one should it exist.
|
||||
if (printer_name.empty()) {
|
||||
printing::PrinterList printers;
|
||||
if (print_backend->EnumeratePrinters(&printers) !=
|
||||
printing::mojom::ResultCode::kSuccess)
|
||||
return std::make_pair("Failed to enumerate printers", std::u16string());
|
||||
print_backend->EnumeratePrinters(&printers);
|
||||
if (!printers.empty())
|
||||
printer_name = printers.front().printer_name;
|
||||
}
|
||||
|
||||
return std::make_pair(std::string(), base::UTF8ToUTF16(printer_name));
|
||||
return base::UTF8ToUTF16(printer_name);
|
||||
}
|
||||
|
||||
// Copied from
|
||||
@@ -2442,8 +2435,7 @@ void WebContents::OnGetDefaultPrinter(
|
||||
printing::CompletionCallback print_callback,
|
||||
std::u16string device_name,
|
||||
bool silent,
|
||||
// <error, default_printer>
|
||||
std::pair<std::string, std::u16string> info) {
|
||||
std::u16string default_printer) {
|
||||
// The content::WebContents might be already deleted at this point, and the
|
||||
// PrintViewManagerElectron class does not do null check.
|
||||
if (!web_contents()) {
|
||||
@@ -2452,14 +2444,8 @@ void WebContents::OnGetDefaultPrinter(
|
||||
return;
|
||||
}
|
||||
|
||||
if (!info.first.empty()) {
|
||||
if (print_callback)
|
||||
std::move(print_callback).Run(false, info.first);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user has passed a deviceName use it, otherwise use default printer.
|
||||
std::u16string printer_name = device_name.empty() ? info.second : device_name;
|
||||
std::u16string printer_name =
|
||||
device_name.empty() ? default_printer : device_name;
|
||||
|
||||
// If there are no valid printers available on the network, we bail.
|
||||
if (printer_name.empty() || !IsDeviceNameValid(printer_name)) {
|
||||
|
||||
@@ -221,8 +221,7 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||
printing::CompletionCallback print_callback,
|
||||
std::u16string device_name,
|
||||
bool silent,
|
||||
// <error, default_printer_name>
|
||||
std::pair<std::string, std::u16string> info);
|
||||
std::u16string default_printer);
|
||||
void Print(gin::Arguments* args);
|
||||
// Print current page as PDF.
|
||||
v8::Local<v8::Promise> PrintToPDF(base::DictionaryValue settings);
|
||||
|
||||
@@ -592,7 +592,8 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
|
||||
switches::kStandardSchemes, switches::kEnableSandbox,
|
||||
switches::kSecureSchemes, switches::kBypassCSPSchemes,
|
||||
switches::kCORSSchemes, switches::kFetchSchemes,
|
||||
switches::kServiceWorkerSchemes, switches::kStreamingSchemes};
|
||||
switches::kServiceWorkerSchemes, switches::kEnableApiFilteringLogging,
|
||||
switches::kStreamingSchemes};
|
||||
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
|
||||
kCommonSwitchNames,
|
||||
base::size(kCommonSwitchNames));
|
||||
|
||||
@@ -50,8 +50,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 17,0,0,20211005
|
||||
PRODUCTVERSION 17,0,0,20211005
|
||||
FILEVERSION 17,0,0,20210930
|
||||
PRODUCTVERSION 17,0,0,20210930
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
||||
@@ -80,7 +80,7 @@ v8::Local<v8::Value> Converter<ContextMenuParamsWithRenderFrameHost>::ToV8(
|
||||
const auto& params = val.first;
|
||||
content::RenderFrameHost* render_frame_host = val.second;
|
||||
gin_helper::Dictionary dict = gin::Dictionary::CreateEmpty(isolate);
|
||||
dict.SetGetter("frame", render_frame_host, v8::DontEnum);
|
||||
dict.SetGetter("frame", render_frame_host);
|
||||
dict.Set("x", params.x);
|
||||
dict.Set("y", params.y);
|
||||
dict.Set("linkURL", params.link_url);
|
||||
|
||||
@@ -111,9 +111,7 @@ class Dictionary : public gin::Dictionary {
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
bool SetGetter(const K& key,
|
||||
const V& val,
|
||||
v8::PropertyAttribute attribute = v8::None) {
|
||||
bool SetGetter(const K& key, const V& val) {
|
||||
AccessorValue<V> acc_value;
|
||||
acc_value.Value = val;
|
||||
|
||||
@@ -138,7 +136,7 @@ class Dictionary : public gin::Dictionary {
|
||||
if (gin::TryConvertToV8(info.GetIsolate(), val, &v8_value))
|
||||
info.GetReturnValue().Set(v8_value);
|
||||
},
|
||||
nullptr, v8_value_accessor, v8::DEFAULT, attribute)
|
||||
NULL, v8_value_accessor)
|
||||
.ToChecked();
|
||||
}
|
||||
|
||||
|
||||
@@ -241,6 +241,8 @@ const char kAppUserModelId[] = "app-user-model-id";
|
||||
// The application path
|
||||
const char kAppPath[] = "app-path";
|
||||
|
||||
const char kEnableApiFilteringLogging[] = "enable-api-filtering-logging";
|
||||
|
||||
// The command line switch versions of the options.
|
||||
const char kScrollBounce[] = "scroll-bounce";
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ extern const char kCORSSchemes[];
|
||||
extern const char kStreamingSchemes[];
|
||||
extern const char kAppUserModelId[];
|
||||
extern const char kAppPath[];
|
||||
extern const char kEnableApiFilteringLogging[];
|
||||
|
||||
extern const char kScrollBounce[];
|
||||
extern const char kNodeIntegrationInWorker[];
|
||||
|
||||
@@ -12,6 +12,8 @@ import { closeWindow, closeAllWindows } from './window-helpers';
|
||||
import { ifdescribe, ifit } from './spec-helpers';
|
||||
import split = require('split')
|
||||
|
||||
const features = process._linkedBinding('electron_common_features');
|
||||
|
||||
const fixturesPath = path.resolve(__dirname, '../spec/fixtures');
|
||||
|
||||
describe('electron module', () => {
|
||||
@@ -460,6 +462,25 @@ describe('app module', () => {
|
||||
expect(webContents).to.equal(w.webContents);
|
||||
expect(details.reason).to.be.oneOf(['crashed', 'abnormal-exit']);
|
||||
});
|
||||
|
||||
ifdescribe(features.isDesktopCapturerEnabled())('desktopCapturer module filtering', () => {
|
||||
it('should emit desktop-capturer-get-sources event when desktopCapturer.getSources() is invoked', async () => {
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false
|
||||
}
|
||||
});
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
const promise = emittedOnce(app, 'desktop-capturer-get-sources');
|
||||
w.webContents.executeJavaScript('require(\'electron\').desktopCapturer.getSources({ types: [\'screen\'] })');
|
||||
|
||||
const [, webContents] = await promise;
|
||||
expect(webContents).to.equal(w.webContents);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('app.badgeCount', () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { expect } from 'chai';
|
||||
import { screen, desktopCapturer, BrowserWindow } from 'electron/main';
|
||||
import { screen, BrowserWindow, SourcesOptions } from 'electron/main';
|
||||
import { desktopCapturer } from 'electron/common';
|
||||
import { emittedOnce } from './events-helpers';
|
||||
import { ifdescribe, ifit } from './spec-helpers';
|
||||
import { closeAllWindows } from './window-helpers';
|
||||
@@ -22,55 +23,76 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||
|
||||
after(closeAllWindows);
|
||||
|
||||
// TODO(nornagon): figure out why this test is failing on Linux and re-enable it.
|
||||
ifit(process.platform !== 'linux')('should return a non-empty array of sources', async () => {
|
||||
const sources = await desktopCapturer.getSources({ types: ['window', 'screen'] });
|
||||
expect(sources).to.be.an('array').that.is.not.empty();
|
||||
});
|
||||
const getSources: typeof desktopCapturer.getSources = (options: SourcesOptions) => {
|
||||
return w.webContents.executeJavaScript(`
|
||||
require('electron').desktopCapturer.getSources(${JSON.stringify(options)}).then(m => JSON.parse(JSON.stringify(m)))
|
||||
`);
|
||||
};
|
||||
|
||||
it('throws an error for invalid options', async () => {
|
||||
const promise = desktopCapturer.getSources(['window', 'screen'] as any);
|
||||
await expect(promise).to.be.eventually.rejectedWith(Error, 'Invalid options');
|
||||
});
|
||||
const generateSpecs = (description: string, getSources: typeof desktopCapturer.getSources) => {
|
||||
describe(description, () => {
|
||||
// TODO(nornagon): figure out why this test is failing on Linux and re-enable it.
|
||||
ifit(process.platform !== 'linux')('should return a non-empty array of sources', async () => {
|
||||
const sources = await getSources({ types: ['window', 'screen'] });
|
||||
expect(sources).to.be.an('array').that.is.not.empty();
|
||||
});
|
||||
|
||||
// TODO(nornagon): figure out why this test is failing on Linux and re-enable it.
|
||||
ifit(process.platform !== 'linux')('does not throw an error when called more than once (regression)', async () => {
|
||||
const sources1 = await desktopCapturer.getSources({ types: ['window', 'screen'] });
|
||||
expect(sources1).to.be.an('array').that.is.not.empty();
|
||||
it('throws an error for invalid options', async () => {
|
||||
const promise = getSources(['window', 'screen'] as any);
|
||||
await expect(promise).to.be.eventually.rejectedWith(Error, 'Invalid options');
|
||||
});
|
||||
|
||||
const sources2 = await desktopCapturer.getSources({ types: ['window', 'screen'] });
|
||||
expect(sources2).to.be.an('array').that.is.not.empty();
|
||||
});
|
||||
// TODO(nornagon): figure out why this test is failing on Linux and re-enable it.
|
||||
ifit(process.platform !== 'linux')('does not throw an error when called more than once (regression)', async () => {
|
||||
const sources1 = await getSources({ types: ['window', 'screen'] });
|
||||
expect(sources1).to.be.an('array').that.is.not.empty();
|
||||
|
||||
ifit(process.platform !== 'linux')('responds to subsequent calls of different options', async () => {
|
||||
const promise1 = desktopCapturer.getSources({ types: ['window'] });
|
||||
await expect(promise1).to.eventually.be.fulfilled();
|
||||
const sources2 = await getSources({ types: ['window', 'screen'] });
|
||||
expect(sources2).to.be.an('array').that.is.not.empty();
|
||||
});
|
||||
|
||||
const promise2 = desktopCapturer.getSources({ types: ['screen'] });
|
||||
await expect(promise2).to.eventually.be.fulfilled();
|
||||
});
|
||||
ifit(process.platform !== 'linux')('responds to subsequent calls of different options', async () => {
|
||||
const promise1 = getSources({ types: ['window'] });
|
||||
await expect(promise1).to.eventually.be.fulfilled();
|
||||
|
||||
// Linux doesn't return any window sources.
|
||||
ifit(process.platform !== 'linux')('returns an empty display_id for window sources on Windows and Mac', async () => {
|
||||
const w = new BrowserWindow({ width: 200, height: 200 });
|
||||
await w.loadURL('about:blank');
|
||||
const promise2 = getSources({ types: ['screen'] });
|
||||
await expect(promise2).to.eventually.be.fulfilled();
|
||||
});
|
||||
|
||||
const sources = await desktopCapturer.getSources({ types: ['window'] });
|
||||
w.destroy();
|
||||
expect(sources).to.be.an('array').that.is.not.empty();
|
||||
for (const { display_id: displayId } of sources) {
|
||||
expect(displayId).to.be.a('string').and.be.empty();
|
||||
}
|
||||
});
|
||||
// Linux doesn't return any window sources.
|
||||
ifit(process.platform !== 'linux')('returns an empty display_id for window sources on Windows and Mac', async () => {
|
||||
const w = new BrowserWindow({ width: 200, height: 200 });
|
||||
await w.loadURL('about:blank');
|
||||
|
||||
ifit(process.platform !== 'linux')('returns display_ids matching the Screen API on Windows and Mac', async () => {
|
||||
const displays = screen.getAllDisplays();
|
||||
const sources = await desktopCapturer.getSources({ types: ['screen'] });
|
||||
expect(sources).to.be.an('array').of.length(displays.length);
|
||||
const sources = await getSources({ types: ['window'] });
|
||||
w.destroy();
|
||||
expect(sources).to.be.an('array').that.is.not.empty();
|
||||
for (const { display_id: displayId } of sources) {
|
||||
expect(displayId).to.be.a('string').and.be.empty();
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < sources.length; i++) {
|
||||
expect(sources[i].display_id).to.equal(displays[i].id.toString());
|
||||
}
|
||||
ifit(process.platform !== 'linux')('returns display_ids matching the Screen API on Windows and Mac', async () => {
|
||||
const displays = screen.getAllDisplays();
|
||||
const sources = await getSources({ types: ['screen'] });
|
||||
expect(sources).to.be.an('array').of.length(displays.length);
|
||||
|
||||
for (let i = 0; i < sources.length; i++) {
|
||||
expect(sources[i].display_id).to.equal(displays[i].id.toString());
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
generateSpecs('in renderer process', getSources);
|
||||
generateSpecs('in main process', desktopCapturer.getSources);
|
||||
|
||||
ifit(process.platform !== 'linux')('returns an empty source list if blocked by the main process', async () => {
|
||||
w.webContents.once('desktop-capturer-get-sources', (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
const sources = await getSources({ types: ['screen'] });
|
||||
expect(sources).to.be.empty();
|
||||
});
|
||||
|
||||
it('disabling thumbnail should return empty images', async () => {
|
||||
@@ -79,10 +101,14 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||
w2.show();
|
||||
await wShown;
|
||||
|
||||
const isEmpties: boolean[] = (await desktopCapturer.getSources({
|
||||
types: ['window', 'screen'],
|
||||
thumbnailSize: { width: 0, height: 0 }
|
||||
})).map(s => s.thumbnail.constructor.name === 'NativeImage' && s.thumbnail.isEmpty());
|
||||
const isEmpties: boolean[] = await w.webContents.executeJavaScript(`
|
||||
require('electron').desktopCapturer.getSources({
|
||||
types: ['window', 'screen'],
|
||||
thumbnailSize: { width: 0, height: 0 }
|
||||
}).then((sources) => {
|
||||
return sources.map(s => s.thumbnail.constructor.name === 'NativeImage' && s.thumbnail.isEmpty())
|
||||
})
|
||||
`);
|
||||
|
||||
w2.destroy();
|
||||
expect(isEmpties).to.be.an('array').that.is.not.empty();
|
||||
@@ -99,7 +125,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||
await wFocused;
|
||||
|
||||
const mediaSourceId = w.getMediaSourceId();
|
||||
const sources = await desktopCapturer.getSources({
|
||||
const sources = await getSources({
|
||||
types: ['window'],
|
||||
thumbnailSize: { width: 0, height: 0 }
|
||||
});
|
||||
@@ -135,7 +161,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||
const ids = mediaSourceId.split(':');
|
||||
expect(ids[1]).to.not.equal(ids[2]);
|
||||
|
||||
const sources = await desktopCapturer.getSources({
|
||||
const sources = await getSources({
|
||||
types: ['window'],
|
||||
thumbnailSize: { width: 0, height: 0 }
|
||||
});
|
||||
@@ -180,7 +206,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||
|
||||
// DesktopCapturer.getSources() returns sources sorted from foreground to
|
||||
// background, i.e. top to bottom.
|
||||
let sources = await desktopCapturer.getSources({
|
||||
let sources = await getSources({
|
||||
types: ['window'],
|
||||
thumbnailSize: { width: 0, height: 0 }
|
||||
});
|
||||
@@ -226,7 +252,7 @@ ifdescribe(!process.arch.includes('arm') && process.platform !== 'win32')('deskt
|
||||
}
|
||||
});
|
||||
|
||||
sources = await desktopCapturer.getSources({
|
||||
sources = await getSources({
|
||||
types: ['window'],
|
||||
thumbnailSize: { width: 0, height: 0 }
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user