Compare commits

..

28 Commits

Author SHA1 Message Date
trop[bot]
18bd295873 docs: document View.removeChildView edge case (#43725)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Niklas Wenzel <dev@nikwen.de>
2024-09-16 12:53:10 +02:00
trop[bot]
748f293400 refactor: remove use of deprecated API BuildServiceInstanceFor() (#43707)
* refactor: update BadgeManagerFactory

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

* refactor: update NetworkContextServiceFactory

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

* refactor: update ElectronExtensionSystemFactory

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

* refactor: update UsbChooserContextFactory

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

* refactor: update UsbHidChooserContextFactory

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

* refactor: update SerialChooserContextFactory

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

* refactor: update FileSystemAccessPermissionContextFactory

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

* empty commit

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-13 15:26:10 -05:00
trop[bot]
7a04a77ed0 refactor: avoid minor code repetition in native_image.cc (#43704)
chore: delegate handle creation in NativeImage::Resize()

chore: delegate handle creation in NativeImage::Crop()

chore: delegate handle creation in NativeImage::CreateEmpty()

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-12 20:13:27 +02:00
trop[bot]
810a6d6ef4 fix: EyeDropper working in devtools (#43700)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-09-12 19:20:34 +02:00
trop[bot]
27aea84411 fix: native View wrapper crash missing when adding child view (#43696)
fix: native View wrapper crash missing when adding child view

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-09-12 12:48:16 +02:00
trop[bot]
2aa2611f76 feat: add support for system picker in setDisplayMediaRequestHandler (#43680)
* tmp

Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>

* feat: add support for system picker in setDisplayMediaRequestHandler

Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>

* oops

Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>

* Apply suggestions from code review

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

Co-authored-by: Samuel Attard <sam@electronjs.org>

* stuff

Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>

* well...

Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>

* seems legit

Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>

* chore: update patch to handle screenCapturer

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* feat: modify API to use useSystemPicker

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* fix: gate ScreenCaptureKitPicker to macos 15 or higher

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* fix: don't use native picker with legacy media selection

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* chore: code review, boolean set & docs update

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* fix: add cancelCallback

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* docs: clarify session & desktopCapturer docs

Co-authored-by: Keeley Hammond <khammond@slack-corp.com>

* chore: update patches

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>
Co-authored-by: Samuel Attard <sam@electronjs.org>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
2024-09-11 09:57:39 -07:00
trop[bot]
57aeb9dfc6 fix: ensure SetPluginCanSave updated in PDFs (#43686)
fix: ensure SetPluginCanSave updated in PDFs

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-09-11 17:56:23 +02:00
trop[bot]
454218bd2b build: compile Node.js with C++20 support (#43684)
* build: compile with C++20 support

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

* build: update build-image-sha for gcc 10

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-09-11 17:10:33 +02:00
trop[bot]
c56673f83d fix: -Wunsafe-buffer-usage warning in V8Serializer::Serialize() (#43675)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-10 19:00:47 -05:00
trop[bot]
87fc32f0aa fix: restore Chromium default Content-Disposition header parsing (#43670)
* fix: restore Chromium default Content-Disposition header parsing

Co-authored-by: Milan Burda <milan.burda@gmail.com>

* Update api-web-request-spec.ts

Co-authored-by: Milan Burda <milan.burda@gmail.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Milan Burda <milan.burda@gmail.com>
2024-09-10 12:10:53 -04:00
trop[bot]
c7380437aa fix: -Wunsafe-buffer-usage warnings in asar file IO (#43650)
* fix: -Wunsafe-buffer-usage warnings in ScopedTemporaryFile::InitFromFile()

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

* fix: -Wunsafe-buffer-usage warnings in Archive::Init()

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-09 17:27:49 -05:00
trop[bot]
511dece7ff fix: out-of-scope Local handle in node::CallbackScope (#43640)
refactor: use an EscapableHandleScope

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-09 17:30:34 -04:00
trop[bot]
12ce546ded fix: update BrowserView#lastWindowSize after window resize (#43636)
fix: update BrowserView#lastWindowSize after window resize (#43462)

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Zonglong Liu <83216456+mai-121@users.noreply.github.com>
2024-09-09 17:40:27 +02:00
trop[bot]
67da1f2f9f fix: UvHandle move semantics (#43634)
reassign the uv_handle_t of the source

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-09 10:35:58 -05:00
trop[bot]
8d9ed52c72 perf: avoid redundant Promise.GetContext calls (#43620)
refactor: avoid redundant Promise.GetContext calls

Several Promise methods call `GetContext()` multiple times. From looking
at the assembly in obj/electron/electron_lib/promise.o, these redundant
calls are actually being made -- they aren't optmized out.

This PR keeps the return value in a local variable to avoid extra calls.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-08 10:37:42 -05:00
trop[bot]
8d76b8dba9 refactor: take a uint8_t span in ValidateIntegrityOrDie() (#43612)
refactor: take a uint8_t span in ValidateIntegrityOrDie()

Doing some groundwork for fixing unsafe base::File() APIs:

- Change ValidateIntegrityOrDie() to take a span<const uint8_t> arg.
  We'll need this to migrate asar's base::File API calls away from the
  ones tagged `UNSAFE_BUFFER_USAGE` because the safe counterparts use
  span<uint8_t> too.

- Simplify ValidateIntegrityOrDie()'s implementation by using
  crypto::SHA256Hash() instead of reinventing the wheel.

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-06 21:56:27 -05:00
trop[bot]
6f68a40430 refactor: NativeWindow::Create() returns a unique_ptr (#43607)
refactor: NativeWindow::Create() returns a unique_ptr

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-06 14:20:09 -05:00
trop[bot]
cf8a97a924 fix: confirm a v8::Value is a v8::Object before casting it (#43601)
fix: confirm a v8::Value is a v8::Object before casting it

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-06 14:18:26 -05:00
trop[bot]
37a7cb2c11 fix: ensure version of xdg-dialog-portal with defaultPath support (#43595)
fix: ensure version of xdg-dialog-portal with defaultPath support

Closes https://github.com/electron/electron/issues/43310

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-09-06 13:58:55 -04:00
trop[bot]
d92770a275 fix: delete UvTaskRunner's timers only after they're closed (#43597)
* fix: free UvTaskRunner timers only after they are closed

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

* refactor: UvTaskRunner now holds UvHandles

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-06 08:37:33 -05:00
trop[bot]
60264f30d8 build: use proper targets for building (#43588)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
2024-09-06 12:07:55 +02:00
trop[bot]
b31a4dcdf3 docs: explain ipcRenderer behavior in context-bridge.md (#43584)
* docs: explain ipcRenderer behavior in context-bridge.md

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>

* Update context-bridge.md

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>

* Update context-bridge.md

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>

* Update docs/api/context-bridge.md

Co-authored-by: Erik Moura <erikian@erikian.dev>

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>

* Update context-bridge.md

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>

* Update context-bridge.md

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>

* Update context-bridge.md

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>

* Update docs/api/context-bridge.md

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

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>

* Update docs/api/context-bridge.md

Co-authored-by: David Sanders <dsanders11@ucsbalum.com>

Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Kilian Valkhof <kilian@kilianvalkhof.com>
2024-09-05 17:05:46 -04:00
trop[bot]
1d3200d7da fix: Launch apps with XDG_ACTIVATION_TOKEN in ozone/wayland (#43579)
* fix: Launch apps with XDG_ACTIVATION_TOKEN in ozone/wayland

Ensure apps are launched with the activation token received from
xdg_activation_v1 protocol.

Co-authored-by: Orko Garai <orko@igalia.com>

* add focus_launched_process option

Co-authored-by: Orko Garai <orko@igalia.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Orko Garai <orko@igalia.com>
2024-09-05 15:00:15 -05:00
trop[bot]
bf10a437bc build: fix telemetry error when using autoninja (#43574)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
2024-09-05 09:08:38 -04:00
trop[bot]
60247e685d perf: use v8::Local<v8::Object> as the key in ObjectCache (#43568)
* perf: use v8::Object* as direct keys instead of using hash + a linked list

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

* refactor: use v8::Local<v8::Object> as the key

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-05 10:08:42 +02:00
trop[bot]
5d340577b6 fix: systemMediaPermissionDenied should not check camera perms when the request is asking for screen share (#43543)
* fix: systemMediaPermissionDenied: should check for screen capture perms instead of camera

Co-authored-by: George Xu <george.xu@slack-corp.com>

* Revert "fix: systemMediaPermissionDenied: should check for screen capture perms instead of camera"

This reverts commit e9cc672165.

Co-authored-by: George Xu <george.xu@slack-corp.com>

* should only do these checks for audio or video, but not screenshare

Co-authored-by: George Xu <george.xu@slack-corp.com>

* no service

Co-authored-by: George Xu <george.xu@slack-corp.com>

* oops

Co-authored-by: George Xu <george.xu@slack-corp.com>

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: George Xu <george.xu@slack-corp.com>
2024-09-04 22:52:18 -05:00
trop[bot]
40316df338 fix: -Wunsafe-buffer-usage warnings in IsUrlArg() (#43539)
* fix: -Wunsafe-buffer-usage warnings in IsUrlArg()

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

* chore: improve code comments for CheckCommandLineArguments()

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

* chore: reduce diffs from main

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

* refactor: CheckCommandLineArguments takes a StringVector arg

Fixes another buffer warning!

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

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-04 22:51:54 -05:00
trop[bot]
e92eb210bb refactor: declare gin::Wrapper subclasses as final (#43565)
As per the gin docs: "Wrappable<T> explicitly does not support further
subclassing of T. Subclasses of Wrappable<T> should be declared final."

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Charles Kerr <charles@charleskerr.com>
2024-09-04 21:26:25 -05:00
108 changed files with 970 additions and 398 deletions

View File

@@ -69,7 +69,7 @@ runs:
shell: bash
run: |
cd src
e build electron:electron_dist_zip -j $NUMBER_OF_NINJA_PROCESSES
e build --target electron:electron_dist_zip -j $NUMBER_OF_NINJA_PROCESSES
if [ "${{ inputs.is-asan }}" != "true" ]; then
target_os=${{ inputs.target-platform == 'linux' && 'linux' || 'mac'}}
if [ "${{ inputs.artifact-platform }}" = "mas" ]; then
@@ -81,7 +81,7 @@ runs:
shell: bash
run: |
cd src
e build electron:electron_mksnapshot -j $NUMBER_OF_NINJA_PROCESSES
e build --target electron:electron_mksnapshot -j $NUMBER_OF_NINJA_PROCESSES
gn desc out/Default v8:run_mksnapshot_default args > out/Default/mksnapshot_args
# Remove unused args from mksnapshot_args
SEDOPTION="-i"
@@ -104,7 +104,7 @@ runs:
fi
fi
e build electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES
e build --target electron:electron_mksnapshot_zip -j $NUMBER_OF_NINJA_PROCESSES
(cd out/Default; zip mksnapshot.zip mksnapshot_args gen/v8/embedded.S)
- name: Generate Cross-Arch Snapshot (arm/arm64) ${{ inputs.step-suffix }}
shell: bash
@@ -130,24 +130,24 @@ runs:
shell: bash
run: |
cd src
e build electron:electron_chromedriver -j $NUMBER_OF_NINJA_PROCESSES
e build electron:electron_chromedriver_zip
e build --target electron:electron_chromedriver -j $NUMBER_OF_NINJA_PROCESSES
e build --target electron:electron_chromedriver_zip
- name: Build Node.js headers ${{ inputs.step-suffix }}
shell: bash
run: |
cd src
e build electron:node_headers
e build --target electron:node_headers
- name: Generate & Zip Symbols ${{ inputs.step-suffix }}
shell: bash
run: |
# Generate breakpad symbols on release builds
if [ "${{ inputs.generate-symbols }}" = "true" ]; then
e build electron:electron_symbols
e build --target electron:electron_symbols
fi
cd src
export BUILD_PATH="$(pwd)/out/Default"
e build electron:licenses
e build electron:electron_version_file
e build --target electron:licenses
e build --target electron:electron_version_file
if [ "${{ inputs.is-release }}" = "true" ]; then
DELETE_DSYMS_AFTER_ZIP=1 electron/script/zip-symbols.py -b $BUILD_PATH
else

View File

@@ -53,7 +53,7 @@ jobs:
id: set-output
run: |
if [ -z "${{ inputs.build-image-sha }}" ]; then
echo "build-image-sha=cf814a4d2501e8e843caea071a6b70a48e78b855" >> "$GITHUB_OUTPUT"
echo "build-image-sha=77262e58c37631ab082482f42c33cdf68c6c394b" >> "$GITHUB_OUTPUT"
else
echo "build-image-sha=${{ inputs.build-image-sha }}" >> "$GITHUB_OUTPUT"
fi

View File

@@ -95,6 +95,8 @@ for:
- git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
- ps: New-Item -Name depot_tools\.disable_auto_update -ItemType File
- depot_tools\bootstrap\win_tools.bat
- ps: |
Set-Content -Path $pwd\depot_tools\build_telemetry.cfg -Value '{"user": "info@electronjs.org", "status": "opt-out", "countdown": 10, "version": 1}'
- ps: $env:PATH="$pwd\depot_tools;$env:PATH"
- ps: >-
if (Test-Path -Path "$pwd\src\electron") {

View File

@@ -93,6 +93,8 @@ for:
- git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
- ps: New-Item -Name depot_tools\.disable_auto_update -ItemType File
- depot_tools\bootstrap\win_tools.bat
- ps: |
Set-Content -Path $pwd\depot_tools\build_telemetry.cfg -Value '{"user": "info@electronjs.org", "status": "opt-out", "countdown": 10, "version": 1}'
- ps: $env:PATH="$pwd\depot_tools;$env:PATH"
- ps: >-
if (Test-Path -Path "$pwd\src\electron") {

View File

@@ -147,6 +147,25 @@ has been included below for completeness:
If the type you care about is not in the above table, it is probably not supported.
### Exposing ipcRenderer
Attempting to send the entire `ipcRenderer` module as an object over the `contextBridge` will result in
an empty object on the receiving side of the bridge. Sending over `ipcRenderer` in full can let any
code send any message, which is a security footgun. To interact through `ipcRenderer`, provide a safe wrapper
like below:
```js
// Preload (Isolated World)
contextBridge.exposeInMainWorld('electron', {
onMyEventName: (callback) => ipcRenderer.on('MyEventName', (e, ...args) => callback(args))
})
```
```js @ts-nocheck
// Renderer (Main World)
window.electron.onMyEventName(data => { /* ... */ })
```
### Exposing Node Global Symbols
The `contextBridge` can be used by the preload script to give your renderer access to Node APIs.

View File

@@ -20,7 +20,11 @@ app.whenReady().then(() => {
// Grant access to the first screen found.
callback({ video: sources[0], audio: 'loopback' })
})
})
// If true, use the system picker if available.
// Note: this is currently experimental. If the system picker
// is available, it will be used and the media request handler
// will not be invoked.
}, { useSystemPicker: true })
mainWindow.loadFile('index.html')
})

View File

@@ -953,7 +953,7 @@ session.fromPartition('some-partition').setPermissionCheckHandler((webContents,
})
```
#### `ses.setDisplayMediaRequestHandler(handler)`
#### `ses.setDisplayMediaRequestHandler(handler[, opts])`
* `handler` Function | null
* `request` Object
@@ -980,12 +980,18 @@ session.fromPartition('some-partition').setPermissionCheckHandler((webContents,
and this is set to `true`, then local playback of audio will not be muted (e.g. using `MediaRecorder`
to record `WebFrameMain` with this flag set to `true` will allow audio to pass through to the speakers
while recording). Default is `false`.
* `opts` Object (optional) _macOS_ _Experimental_
* `useSystemPicker` Boolean - true if the available native system picker should be used. Default is `false`. _macOS_ _Experimental_
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.
`useSystemPicker` allows an application to use the system picker instead of providing a specific video source from `getSources`.
This option is experimental, and currently available for MacOS 15+ only. If the system picker is available and `useSystemPicker`
is set to `true`, the handler will not be invoked.
```js
const { session, desktopCapturer } = require('electron')
@@ -994,7 +1000,11 @@ session.defaultSession.setDisplayMediaRequestHandler((request, callback) => {
// Grant access to the first screen found.
callback({ video: sources[0] })
})
})
// Use the system picker if available.
// Note: this is currently experimental. If the system picker
// is available, it will be used and the media request handler
// will not be invoked.
}, { useSystemPicker: true })
```
Passing a [WebFrameMain](web-frame-main.md) object as a video or audio stream

View File

@@ -55,6 +55,8 @@ it becomes the topmost view.
* `view` View - Child view to remove.
If the view passed as a parameter is not a child of this view, this method is a no-op.
#### `view.setBounds(bounds)`
* `bounds` [Rectangle](structures/rectangle.md) - New bounds of the View.

View File

@@ -270,6 +270,7 @@ filenames = {
"shell/browser/api/electron_api_debugger.h",
"shell/browser/api/electron_api_desktop_capturer.cc",
"shell/browser/api/electron_api_desktop_capturer.h",
"shell/browser/api/electron_api_desktop_capturer_mac.mm",
"shell/browser/api/electron_api_dialog.cc",
"shell/browser/api/electron_api_download_item.cc",
"shell/browser/api/electron_api_download_item.h",

View File

@@ -145,6 +145,12 @@ export default class BrowserView {
if (this.#autoHorizontalProportion || this.#autoVerticalProportion) {
this.#webContentsView.setBounds(newViewBounds);
}
// Update #lastWindowSize value after browser windows resize
this.#lastWindowSize = {
width: newBounds.width,
height: newBounds.height
};
}
get webContentsView () {

View File

@@ -1,5 +1,5 @@
import { BrowserWindow } from 'electron/main';
const { createDesktopCapturer } = process._linkedBinding('electron_browser_desktop_capturer');
const { createDesktopCapturer, isDisplayMediaSystemPickerAvailable } = process._linkedBinding('electron_browser_desktop_capturer');
const deepEqual = (a: ElectronInternal.GetSourcesOptions, b: ElectronInternal.GetSourcesOptions) => JSON.stringify(a) === JSON.stringify(b);
@@ -13,6 +13,8 @@ function isValid (options: Electron.SourcesOptions) {
return Array.isArray(options?.types);
}
export { isDisplayMediaSystemPickerAvailable };
export async function getSources (args: Electron.SourcesOptions) {
if (!isValid(args)) throw new Error('Invalid options');

View File

@@ -1,11 +1,37 @@
import { fetchWithSession } from '@electron/internal/browser/api/net-fetch';
import { net } from 'electron/main';
const { fromPartition, fromPath, Session } = process._linkedBinding('electron_browser_session');
const { isDisplayMediaSystemPickerAvailable } = process._linkedBinding('electron_browser_desktop_capturer');
// Fake video source that activates the native system picker
// This is used to get around the need for a screen/window
// id in Chrome's desktopCapturer.
let fakeVideoSourceId = -1;
const systemPickerVideoSource = Object.create(null);
Object.defineProperty(systemPickerVideoSource, 'id', {
get () {
return `window:${fakeVideoSourceId--}:0`;
}
});
systemPickerVideoSource.name = '';
Object.freeze(systemPickerVideoSource);
Session.prototype.fetch = function (input: RequestInfo, init?: RequestInit) {
return fetchWithSession(input, init, this, net.request);
};
Session.prototype.setDisplayMediaRequestHandler = function (handler, opts) {
if (!handler) return this._setDisplayMediaRequestHandler(handler, opts);
this._setDisplayMediaRequestHandler(async (req, callback) => {
if (opts && opts.useSystemPicker && isDisplayMediaSystemPickerAvailable()) {
return callback({ video: systemPickerVideoSource });
}
return handler(req, callback);
}, opts);
};
export default {
fromPartition,
fromPath,

View File

@@ -129,3 +129,4 @@ feat_enable_passing_exit_code_on_service_process_crash.patch
chore_remove_reference_to_chrome_browser_themes.patch
feat_enable_customizing_symbol_color_in_framecaptionbutton.patch
build_expose_webplugininfo_interface_to_electron.patch
feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch

View File

@@ -199,10 +199,21 @@ index 58985ce62dc569256bad5e94de9c0d125fc470d0..33436784b691c860d58f8b4dfcc6718e
&SelectFileDialogLinuxKde::OnSelectSingleFolderDialogResponse, this,
parent));
diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.cc b/ui/shell_dialogs/select_file_dialog_linux_portal.cc
index 61ddcbf7bf57e423099c7d392a19b3ec79b5d03f..8c3f4058ad7e9f6460c8d0516a150db612e8f574 100644
index 61ddcbf7bf57e423099c7d392a19b3ec79b5d03f..920d0610943091f850e44e3e0481abd7fe08f881 100644
--- a/ui/shell_dialogs/select_file_dialog_linux_portal.cc
+++ b/ui/shell_dialogs/select_file_dialog_linux_portal.cc
@@ -221,6 +221,8 @@ void SelectFileDialogLinuxPortal::SelectFileImpl(
@@ -44,7 +44,9 @@ constexpr char kMethodStartServiceByName[] = "StartServiceByName";
constexpr char kXdgPortalService[] = "org.freedesktop.portal.Desktop";
constexpr char kXdgPortalObject[] = "/org/freedesktop/portal/desktop";
-constexpr int kXdgPortalRequiredVersion = 3;
+// Version 4 includes support for current_folder option to the OpenFile method via
+// https://github.com/flatpak/xdg-desktop-portal/commit/71165a5.
+constexpr int kXdgPortalRequiredVersion = 4;
constexpr char kXdgPortalRequestInterfaceName[] =
"org.freedesktop.portal.Request";
@@ -221,6 +223,8 @@ void SelectFileDialogLinuxPortal::SelectFileImpl(
weak_factory_.GetWeakPtr()));
info_->type = type;
info_->main_task_runner = base::SequencedTaskRunner::GetCurrentDefault();
@@ -211,7 +222,7 @@ index 61ddcbf7bf57e423099c7d392a19b3ec79b5d03f..8c3f4058ad7e9f6460c8d0516a150db6
if (owning_window) {
if (auto* root = owning_window->GetRootWindow()) {
@@ -557,7 +559,9 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions(
@@ -557,7 +561,9 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions(
response_handle_token);
if (type == SelectFileDialog::Type::SELECT_UPLOAD_FOLDER) {
@@ -222,7 +233,7 @@ index 61ddcbf7bf57e423099c7d392a19b3ec79b5d03f..8c3f4058ad7e9f6460c8d0516a150db6
l10n_util::GetStringUTF8(
IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON));
}
@@ -566,6 +570,8 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions(
@@ -566,6 +572,8 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions(
type == SelectFileDialog::Type::SELECT_UPLOAD_FOLDER ||
type == SelectFileDialog::Type::SELECT_EXISTING_FOLDER) {
AppendBoolOption(&options_writer, kFileChooserOptionDirectory, true);

View File

@@ -0,0 +1,331 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <marshallofsound@electronjs.org>
Date: Thu, 8 Aug 2024 08:39:10 -0700
Subject: feat: allow usage of SCContentSharingPicker on supported platforms
This is implemented as a magic "window id" that instead of pulling an SCStream manually
instead farms out to the screen picker.
diff --git a/content/browser/media/capture/desktop_capture_device_mac.cc b/content/browser/media/capture/desktop_capture_device_mac.cc
index 88c56f4dfcc1f8517ef1e8b6f1d37f5ba4d0b2c7..a75493a6d4d8ce8340a2d820eff5eed4e6a95109 100644
--- a/content/browser/media/capture/desktop_capture_device_mac.cc
+++ b/content/browser/media/capture/desktop_capture_device_mac.cc
@@ -28,7 +28,7 @@ class DesktopCaptureDeviceMac : public IOSurfaceCaptureDeviceBase {
~DesktopCaptureDeviceMac() override = default;
// IOSurfaceCaptureDeviceBase:
- void OnStart() override {
+ void OnStart(std::optional<bool> use_native_picker) override {
requested_format_ = capture_params().requested_format;
requested_format_.pixel_format = media::PIXEL_FORMAT_NV12;
DCHECK_GT(requested_format_.frame_size.GetArea(), 0);
diff --git a/content/browser/media/capture/io_surface_capture_device_base_mac.cc b/content/browser/media/capture/io_surface_capture_device_base_mac.cc
index 8a774911ce0f610b2c993976d108f840696c1d02..5ead7287e2d765d043f8b9c0229a2ee825d9f544 100644
--- a/content/browser/media/capture/io_surface_capture_device_base_mac.cc
+++ b/content/browser/media/capture/io_surface_capture_device_base_mac.cc
@@ -20,7 +20,7 @@ void IOSurfaceCaptureDeviceBase::AllocateAndStart(
client_ = std::move(client);
capture_params_ = params;
- OnStart();
+ OnStart(params.use_native_picker);
}
void IOSurfaceCaptureDeviceBase::StopAndDeAllocate() {
diff --git a/content/browser/media/capture/io_surface_capture_device_base_mac.h b/content/browser/media/capture/io_surface_capture_device_base_mac.h
index 8ac12480f663a74dfbdcf7128a582a81b4474d25..db6802a2603e1d3c3039e49737438124bf2ee1f1 100644
--- a/content/browser/media/capture/io_surface_capture_device_base_mac.h
+++ b/content/browser/media/capture/io_surface_capture_device_base_mac.h
@@ -25,7 +25,7 @@ class CONTENT_EXPORT IOSurfaceCaptureDeviceBase
~IOSurfaceCaptureDeviceBase() override;
// OnStart is called by AllocateAndStart.
- virtual void OnStart() = 0;
+ virtual void OnStart(std::optional<bool> use_native_picker) = 0;
// OnStop is called by StopAndDeAllocate.
virtual void OnStop() = 0;
diff --git a/content/browser/media/capture/screen_capture_kit_device_mac.mm b/content/browser/media/capture/screen_capture_kit_device_mac.mm
index b6129282c6807702cf88e0a3e2ba233e41a20960..1c2d0c6dd4101fe0bac69e3018bbbedadce224cc 100644
--- a/content/browser/media/capture/screen_capture_kit_device_mac.mm
+++ b/content/browser/media/capture/screen_capture_kit_device_mac.mm
@@ -24,24 +24,83 @@
std::optional<gfx::Size>,
std::optional<gfx::Rect>)>;
using ErrorCallback = base::RepeatingClosure;
+using CancelCallback = base::RepeatingClosure;
+
+API_AVAILABLE(macos(15.0))
+@interface ScreenCaptureKitPickerHelper
+ : NSObject <SCContentSharingPickerObserver>
+
+- (void)contentSharingPicker:(SCContentSharingPicker *)picker
+ didCancelForStream:(SCStream *)stream;
+
+- (void)contentSharingPicker:(SCContentSharingPicker *)picker
+ didUpdateWithFilter:(SCContentFilter *)filter
+ forStream:(SCStream *)stream;
+
+- (void)contentSharingPickerStartDidFailWithError:(NSError *)error;
+
+@end
+
+@implementation ScreenCaptureKitPickerHelper {
+ base::RepeatingCallback<void(SCContentFilter *)> _pickerCallback;
+ ErrorCallback _errorCallback;
+ CancelCallback _cancelCallback;
+}
+
+- (void)contentSharingPicker:(SCContentSharingPicker *)picker
+ didCancelForStream:(SCStream *)stream {
+ // TODO: This doesn't appear to be called on Apple's side;
+ // implement this logic
+ _cancelCallback.Run();
+}
+
+- (void)contentSharingPicker:(SCContentSharingPicker *)picker
+ didUpdateWithFilter:(SCContentFilter *)filter
+ forStream:(SCStream *)stream {
+ if (stream == nil) {
+ _pickerCallback.Run(filter);
+ [picker removeObserver:self];
+ }
+}
+
+- (void)contentSharingPickerStartDidFailWithError:(NSError *)error {
+ _errorCallback.Run();
+}
+
+- (instancetype)initWithStreamPickCallback:(base::RepeatingCallback<void(SCContentFilter *)>)pickerCallback
+ cancelCallback:(CancelCallback)cancelCallback
+ errorCallback:(ErrorCallback)errorCallback {
+ if (self = [super init]) {
+ _pickerCallback = pickerCallback;
+ _cancelCallback = cancelCallback;
+ _errorCallback = errorCallback;
+ }
+ return self;
+}
+
+@end
API_AVAILABLE(macos(12.3))
@interface ScreenCaptureKitDeviceHelper
: NSObject <SCStreamDelegate, SCStreamOutput>
- (instancetype)initWithSampleCallback:(SampleCallback)sampleCallback
+ cancelCallback:(CancelCallback)cancelCallback
errorCallback:(ErrorCallback)errorCallback;
@end
@implementation ScreenCaptureKitDeviceHelper {
SampleCallback _sampleCallback;
+ CancelCallback _cancelCallback;
ErrorCallback _errorCallback;
}
- (instancetype)initWithSampleCallback:(SampleCallback)sampleCallback
+ cancelCallback:(CancelCallback)cancelCallback
errorCallback:(ErrorCallback)errorCallback {
if (self = [super init]) {
_sampleCallback = sampleCallback;
+ _cancelCallback = cancelCallback;
_errorCallback = errorCallback;
}
return self;
@@ -141,7 +200,8 @@ + (SCStreamConfiguration*)streamConfigurationWithFrameSize:(gfx::Size)frameSize
class API_AVAILABLE(macos(12.3)) ScreenCaptureKitDeviceMac
: public IOSurfaceCaptureDeviceBase,
- public ScreenCaptureKitResetStreamInterface {
+ public ScreenCaptureKitResetStreamInterface
+ {
public:
explicit ScreenCaptureKitDeviceMac(const DesktopMediaID& source,
SCContentFilter* filter)
@@ -152,18 +212,41 @@ explicit ScreenCaptureKitDeviceMac(const DesktopMediaID& source,
device_task_runner_,
base::BindRepeating(&ScreenCaptureKitDeviceMac::OnStreamSample,
weak_factory_.GetWeakPtr()));
+ CancelCallback cancel_callback = base::BindPostTask(
+ device_task_runner_,
+ base::BindRepeating(&ScreenCaptureKitDeviceMac::OnStreamError,
+ weak_factory_.GetWeakPtr()));
ErrorCallback error_callback = base::BindPostTask(
device_task_runner_,
base::BindRepeating(&ScreenCaptureKitDeviceMac::OnStreamError,
weak_factory_.GetWeakPtr()));
helper_ = [[ScreenCaptureKitDeviceHelper alloc]
initWithSampleCallback:sample_callback
+ cancelCallback:cancel_callback
errorCallback:error_callback];
+
+ if (@available(macOS 15.0, *)) {
+ auto picker_callback = base::BindPostTask(
+ device_task_runner_,
+ base::BindRepeating(&ScreenCaptureKitDeviceMac::OnContentFilterReady, weak_factory_.GetWeakPtr())
+ );
+ auto* picker_observer = [[ScreenCaptureKitPickerHelper alloc] initWithStreamPickCallback:picker_callback cancelCallback:cancel_callback errorCallback:error_callback];
+ [[SCContentSharingPicker sharedPicker] addObserver:picker_observer];
+ }
}
ScreenCaptureKitDeviceMac(const ScreenCaptureKitDeviceMac&) = delete;
ScreenCaptureKitDeviceMac& operator=(const ScreenCaptureKitDeviceMac&) =
delete;
- ~ScreenCaptureKitDeviceMac() override = default;
+ ~ScreenCaptureKitDeviceMac() override {
+ if (@available(macOS 15.0, *)) {
+ auto* picker = [SCContentSharingPicker sharedPicker];
+ ScreenCaptureKitDeviceMac::active_streams_--;
+ picker.maximumStreamCount = @(ScreenCaptureKitDeviceMac::active_streams_);
+ if (ScreenCaptureKitDeviceMac::active_streams_ == 0 && picker.active) {
+ picker.active = false;
+ }
+ }
+ }
void OnShareableContentCreated(SCShareableContent* content) {
DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
@@ -232,7 +315,7 @@ void CreateStream(SCContentFilter* filter) {
return;
}
- if (@available(macOS 14.0, *)) {
+ if (@available(macOS 15.0, *)) {
// Update the content size. This step is neccessary when used together
// with SCContentSharingPicker. If the Chrome picker is used, it will
// change to retina resolution if applicable.
@@ -241,6 +324,9 @@ void CreateStream(SCContentFilter* filter) {
filter.contentRect.size.height * filter.pointPixelScale);
}
+ OnContentFilterReady(filter);
+ }
+ void OnContentFilterReady(SCContentFilter* filter) {
gfx::RectF dest_rect_in_frame;
actual_capture_format_ = capture_params().requested_format;
actual_capture_format_.pixel_format = media::PIXEL_FORMAT_NV12;
@@ -254,6 +340,7 @@ void CreateStream(SCContentFilter* filter) {
stream_ = [[SCStream alloc] initWithFilter:filter
configuration:config
delegate:helper_];
+
{
NSError* error = nil;
bool add_stream_output_result =
@@ -395,7 +482,7 @@ void OnStreamError() {
if (fullscreen_module_) {
fullscreen_module_->Reset();
}
- OnStart();
+ OnStart(std::nullopt);
} else {
client()->OnError(media::VideoCaptureError::kScreenCaptureKitStreamError,
FROM_HERE, "Stream delegate called didStopWithError");
@@ -418,23 +505,39 @@ void OnUpdateConfigurationError() {
}
// IOSurfaceCaptureDeviceBase:
- void OnStart() override {
+ void OnStart(std::optional<bool> use_native_picker) override {
DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
- if (filter_) {
- // SCContentSharingPicker is used where filter_ is set on creation.
- CreateStream(filter_);
- } else {
- // Chrome picker is used.
- auto content_callback = base::BindPostTask(
- device_task_runner_,
- base::BindRepeating(
- &ScreenCaptureKitDeviceMac::OnShareableContentCreated,
- weak_factory_.GetWeakPtr()));
- auto handler = ^(SCShareableContent* content, NSError* error) {
- content_callback.Run(content);
- };
- [SCShareableContent getShareableContentWithCompletionHandler:handler];
+
+ if (@available(macOS 15.0, *)) {
+ constexpr bool DefaultUseNativePicker = true;
+ if (use_native_picker.value_or(DefaultUseNativePicker) && source_.id < 0 && source_.window_id == 0) {
+ auto* picker = [SCContentSharingPicker sharedPicker];
+ ScreenCaptureKitDeviceMac::active_streams_++;
+ picker.maximumStreamCount = @(ScreenCaptureKitDeviceMac::active_streams_);
+ if (!picker.active) {
+ picker.active = true;
+ }
+ NSMutableArray<NSNumber*>* exclude_ns_windows = [NSMutableArray array];
+ [[[[NSApplication sharedApplication] windows] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSWindow* win, NSDictionary *bindings) {
+ return [win sharingType] == NSWindowSharingNone;
+ }]] enumerateObjectsUsingBlock:^(NSWindow* win, NSUInteger idx, BOOL *stop) {
+ [exclude_ns_windows addObject:@([win windowNumber])];
+ }];
+ picker.defaultConfiguration.excludedWindowIDs = exclude_ns_windows;
+ [picker present];
+ return;
+ }
}
+
+ auto content_callback = base::BindPostTask(
+ device_task_runner_,
+ base::BindRepeating(
+ &ScreenCaptureKitDeviceMac::OnShareableContentCreated,
+ weak_factory_.GetWeakPtr()));
+ auto handler = ^(SCShareableContent* content, NSError* error) {
+ content_callback.Run(content);
+ };
+ [SCShareableContent getShareableContentWithCompletionHandler:handler];
}
void OnStop() override {
DCHECK(device_task_runner_->RunsTasksInCurrentSequence());
@@ -492,6 +595,8 @@ void ResetStreamTo(SCWindow* window) override {
}
private:
+ static int active_streams_;
+
const DesktopMediaID source_;
SCContentFilter* const filter_;
const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
@@ -521,6 +626,8 @@ void ResetStreamTo(SCWindow* window) override {
base::WeakPtrFactory<ScreenCaptureKitDeviceMac> weak_factory_{this};
};
+int ScreenCaptureKitDeviceMac::active_streams_ = 0;
+
} // namespace
// Although ScreenCaptureKit is available in 12.3 there were some bugs that
diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
index 7adf8264cfa9980c4a8414bf0f8bfa9ad70ec0b3..d162612dc70a2b57190aaf558aca8f46cbdedcad 100644
--- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
@@ -360,13 +360,15 @@ void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync(
std::move(after_start_capture_callback));
break;
#else
+ media::VideoCaptureParams updated_params = params;
+ updated_params.use_native_picker = stream_type != blink::mojom::MediaStreamType::GUM_DESKTOP_VIDEO_CAPTURE;
// All cases other than tab capture or Aura desktop/window capture.
TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
"UsingDesktopCapturer", TRACE_EVENT_SCOPE_THREAD);
start_capture_closure = base::BindOnce(
&InProcessVideoCaptureDeviceLauncher::
DoStartDesktopCaptureOnDeviceThread,
- base::Unretained(this), desktop_id, params,
+ base::Unretained(this), desktop_id, updated_params,
CreateDeviceClient(media::VideoCaptureBufferType::kSharedMemory,
kMaxNumberOfBuffers, std::move(receiver),
std::move(receiver_on_io_thread)),
diff --git a/media/capture/video_capture_types.h b/media/capture/video_capture_types.h
index f2b75f5b2f547ad135c1288bf3639b26dedc8053..ef18724d9f2ea68a47b66fc3981f58a73ac1b51d 100644
--- a/media/capture/video_capture_types.h
+++ b/media/capture/video_capture_types.h
@@ -355,6 +355,8 @@ struct CAPTURE_EXPORT VideoCaptureParams {
// Flag indicating whether HiDPI mode should be enabled for tab capture
// sessions.
bool is_high_dpi_enabled = true;
+
+ std::optional<bool> use_native_picker;
};
CAPTURE_EXPORT std::ostream& operator<<(

View File

@@ -52,3 +52,4 @@ src_account_for_openssl_unexpected_version.patch
src_stop_using_deprecated_fields_of_fastapicallbackoptions.patch
build_don_t_redefine_win32_lean_and_mean.patch
src_use_supported_api_to_get_stalled_tla_messages.patch
build_compile_with_c_20_support.patch

View File

@@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
Date: Wed, 4 Sep 2024 16:39:23 +0200
Subject: build: compile with C++20 support
Refs https://github.com/nodejs/node/pull/45427
V8 requires C++20 support as of https://chromium-review.googlesource.com/c/v8/v8/+/5587859.
This can be removed when Electron upgrades to a version of Node.js containing the required V8 version.
diff --git a/common.gypi b/common.gypi
index 8736ad12eec294070a5160a64248044cd16347c9..216200c279c599f6dee228120ff5f3943fa52ffd 100644
--- a/common.gypi
+++ b/common.gypi
@@ -307,7 +307,7 @@
'VCCLCompilerTool': {
'AdditionalOptions': [
'/Zc:__cplusplus',
- '-std:c++17'
+ '-std:c++20'
],
'BufferSecurityCheck': 'true',
'DebugInformationFormat': 1, # /Z7 embed info in .obj files
@@ -489,7 +489,7 @@
}],
[ 'OS in "linux freebsd openbsd solaris android aix os400 cloudabi"', {
'cflags': [ '-Wall', '-Wextra', '-Wno-unused-parameter', ],
- 'cflags_cc': [ '-fno-rtti', '-fno-exceptions', '-std=gnu++17' ],
+ 'cflags_cc': [ '-fno-rtti', '-fno-exceptions', '-std=gnu++20' ],
'defines': [ '__STDC_FORMAT_MACROS' ],
'ldflags': [ '-rdynamic' ],
'target_conditions': [
@@ -660,7 +660,7 @@
['clang==1', {
'xcode_settings': {
'GCC_VERSION': 'com.apple.compilers.llvm.clang.1_0',
- 'CLANG_CXX_LANGUAGE_STANDARD': 'gnu++17', # -std=gnu++17
+ 'CLANG_CXX_LANGUAGE_STANDARD': 'gnu++20', # -std=gnu++20
'CLANG_CXX_LIBRARY': 'libc++',
},
}],

View File

@@ -186,13 +186,9 @@ async function runMainProcessElectronTests () {
}
async function installSpecModules (dir) {
// v8 headers use c++17 so override the gyp default of -std=c++14,
// but don't clobber any other CXXFLAGS that were passed into spec-runner.js
const CXXFLAGS = ['-std=c++17', process.env.CXXFLAGS].filter(x => !!x).join(' ');
const env = {
...process.env,
CXXFLAGS,
CXXFLAGS: process.env.CXXFLAGS,
npm_config_msvs_version: '2019',
npm_config_yes: 'true'
};

View File

@@ -4,6 +4,7 @@
#include "shell/app/command_line_args.h"
#include <algorithm>
#include <locale>
#include "sandbox/policy/switches.h"
@@ -11,46 +12,44 @@
namespace {
bool IsUrlArg(const base::CommandLine::CharType* arg) {
// the first character must be a letter for this to be a URL
auto c = *arg;
if (std::isalpha(c, std::locale::classic())) {
for (auto* p = arg + 1; *p; ++p) {
c = *p;
#if BUILDFLAG(IS_WIN)
constexpr auto DashDash = base::CommandLine::StringViewType{L"--"};
#else
constexpr auto DashDash = base::CommandLine::StringViewType{"--"};
#endif
// colon indicates that the argument starts with a URI scheme
if (c == ':') {
// it could also be a Windows filesystem path
if (p == arg + 1)
break;
// we say it's a URL arg if it starts with a URI scheme that:
// 1. starts with an alpha, and
// 2. contains no spaces, and
// 3. is longer than one char (to ensure it's not a Windows drive path)
bool IsUrlArg(const base::CommandLine::StringViewType arg) {
const auto scheme_end = arg.find(':');
if (scheme_end == base::CommandLine::StringViewType::npos)
return false;
return true;
}
// white-space before a colon means it's not a URL
if (std::isspace(c, std::locale::classic()))
break;
}
}
return false;
const auto& c_locale = std::locale::classic();
const auto isspace = [&](auto ch) { return std::isspace(ch, c_locale); };
const auto scheme = arg.substr(0U, scheme_end);
return std::size(scheme) > 1U && std::isalpha(scheme.front(), c_locale) &&
std::ranges::none_of(scheme, isspace);
}
} // namespace
namespace electron {
bool CheckCommandLineArguments(int argc, base::CommandLine::CharType** argv) {
const base::CommandLine::StringType dashdash(2, '-');
// Check for CVE-2018-1000006 issues. Return true iff argv looks safe.
// Sample exploit: 'exodus://aaaaaaaaa" --gpu-launcher="cmd" --aaaaa='
// Prevent it by returning false if any arg except '--' follows a URL arg.
// More info at https://www.electronjs.org/blog/protocol-handler-fix
bool CheckCommandLineArguments(const base::CommandLine::StringVector& argv) {
bool block_args = false;
for (int i = 0; i < argc; ++i) {
if (argv[i] == dashdash)
for (const auto& arg : argv) {
if (arg == DashDash)
break;
if (block_args) {
if (block_args)
return false;
} else if (IsUrlArg(argv[i])) {
if (IsUrlArg(arg))
block_args = true;
}
}
return true;
}

View File

@@ -9,7 +9,7 @@
namespace electron {
bool CheckCommandLineArguments(int argc, base::CommandLine::CharType** argv);
bool CheckCommandLineArguments(const base::CommandLine::StringVector& argv);
bool IsSandboxEnabled(base::CommandLine* command_line);
} // namespace electron

View File

@@ -224,7 +224,7 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) {
CHECK_EQ(fiber_status, FiberStatus::kSuccess);
#endif // defined(ARCH_CPU_32_BITS)
if (!electron::CheckCommandLineArguments(arguments.argc, arguments.argv))
if (!electron::CheckCommandLineArguments(command_line->argv()))
return -1;
sandbox::SandboxInterfaceInfo sandbox_info = {nullptr};

View File

@@ -2,31 +2,33 @@
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/app/uv_task_runner.h"
#include <utility>
#include "base/location.h"
#include "base/time/time.h"
#include "shell/app/uv_task_runner.h"
namespace electron {
UvTaskRunner::UvTaskRunner(uv_loop_t* loop) : loop_(loop) {}
UvTaskRunner::UvTaskRunner(uv_loop_t* loop) : loop_{loop} {}
UvTaskRunner::~UvTaskRunner() {
for (auto& iter : tasks_) {
uv_unref(reinterpret_cast<uv_handle_t*>(iter.first));
delete iter.first;
}
}
UvTaskRunner::~UvTaskRunner() = default;
bool UvTaskRunner::PostDelayedTask(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) {
auto* timer = new uv_timer_t;
auto on_timeout = [](uv_timer_t* timer) {
auto& tasks = static_cast<UvTaskRunner*>(timer->data)->tasks_;
if (auto iter = tasks.find(timer); iter != tasks.end())
std::move(tasks.extract(iter).mapped()).Run();
};
auto timer = UvHandle<uv_timer_t>{};
timer->data = this;
uv_timer_init(loop_, timer);
uv_timer_start(timer, UvTaskRunner::OnTimeout, delay.InMilliseconds(), 0);
tasks_[timer] = std::move(task);
uv_timer_init(loop_, timer.get());
uv_timer_start(timer.get(), on_timeout, delay.InMilliseconds(), 0);
tasks_.insert_or_assign(std::move(timer), std::move(task));
return true;
}
@@ -40,22 +42,4 @@ bool UvTaskRunner::PostNonNestableDelayedTask(const base::Location& from_here,
return PostDelayedTask(from_here, std::move(task), delay);
}
// static
void UvTaskRunner::OnTimeout(uv_timer_t* timer) {
auto& tasks = static_cast<UvTaskRunner*>(timer->data)->tasks_;
const auto iter = tasks.find(timer);
if (iter == std::end(tasks))
return;
std::move(iter->second).Run();
tasks.erase(iter);
uv_timer_stop(timer);
uv_close(reinterpret_cast<uv_handle_t*>(timer), UvTaskRunner::OnClose);
}
// static
void UvTaskRunner::OnClose(uv_handle_t* handle) {
delete reinterpret_cast<uv_timer_t*>(handle);
}
} // namespace electron

View File

@@ -9,7 +9,7 @@
#include "base/memory/raw_ptr.h"
#include "base/task/single_thread_task_runner.h"
#include "uv.h" // NOLINT(build/include_directory)
#include "shell/common/node_bindings.h"
namespace base {
class Location;
@@ -38,12 +38,10 @@ class UvTaskRunner : public base::SingleThreadTaskRunner {
private:
~UvTaskRunner() override;
static void OnTimeout(uv_timer_t* timer);
static void OnClose(uv_handle_t* handle);
raw_ptr<uv_loop_t> loop_;
std::map<uv_timer_t*, base::OnceClosure> tasks_;
std::map<UvHandle<uv_timer_t>, base::OnceClosure, UvHandleCompare> tasks_;
};
} // namespace electron

View File

@@ -57,12 +57,12 @@ enum class JumpListResult : int;
namespace api {
class App : public ElectronBrowserClient::Delegate,
public gin::Wrappable<App>,
public gin_helper::EventEmitterMixin<App>,
private BrowserObserver,
private content::GpuDataManagerObserver,
private content::BrowserChildProcessObserver {
class App final : public ElectronBrowserClient::Delegate,
public gin::Wrappable<App>,
public gin_helper::EventEmitterMixin<App>,
private BrowserObserver,
private content::GpuDataManagerObserver,
private content::BrowserChildProcessObserver {
public:
using FileIconCallback =
base::RepeatingCallback<void(v8::Local<v8::Value>, const gfx::Image&)>;

View File

@@ -19,10 +19,10 @@ class Handle;
namespace electron::api {
class AutoUpdater : public gin::Wrappable<AutoUpdater>,
public gin_helper::EventEmitterMixin<AutoUpdater>,
public auto_updater::Delegate,
private WindowListObserver {
class AutoUpdater final : public gin::Wrappable<AutoUpdater>,
public gin_helper::EventEmitterMixin<AutoUpdater>,
public auto_updater::Delegate,
private WindowListObserver {
public:
static gin::Handle<AutoUpdater> Create(v8::Isolate* isolate);

View File

@@ -98,8 +98,8 @@ BaseWindow::BaseWindow(v8::Isolate* isolate,
}
// Creates NativeWindow.
window_.reset(NativeWindow::Create(
options, parent.IsEmpty() ? nullptr : parent->window_.get()));
window_ = NativeWindow::Create(
options, parent.IsEmpty() ? nullptr : parent->window_.get());
window_->AddObserver(this);
SetContentView(View::Create(isolate));

View File

@@ -31,8 +31,8 @@ class ElectronBrowserContext;
namespace api {
class Cookies : public gin::Wrappable<Cookies>,
public gin_helper::EventEmitterMixin<Cookies> {
class Cookies final : public gin::Wrappable<Cookies>,
public gin_helper::EventEmitterMixin<Cookies> {
public:
static gin::Handle<Cookies> Create(v8::Isolate* isolate,
ElectronBrowserContext* browser_context);

View File

@@ -20,7 +20,7 @@ class Handle;
namespace electron::api {
// Retains reference to the data pipe.
class DataPipeHolder : public gin::Wrappable<DataPipeHolder> {
class DataPipeHolder final : public gin::Wrappable<DataPipeHolder> {
public:
// gin::Wrappable
static gin::WrapperInfo kWrapperInfo;

View File

@@ -32,10 +32,10 @@ class Promise;
namespace electron::api {
class Debugger : public gin::Wrappable<Debugger>,
public gin_helper::EventEmitterMixin<Debugger>,
public content::DevToolsAgentHostClient,
private content::WebContentsObserver {
class Debugger final : public gin::Wrappable<Debugger>,
public gin_helper::EventEmitterMixin<Debugger>,
public content::DevToolsAgentHostClient,
private content::WebContentsObserver {
public:
static gin::Handle<Debugger> Create(v8::Isolate* isolate,
content::WebContents* web_contents);

View File

@@ -503,6 +503,13 @@ gin::Handle<DesktopCapturer> DesktopCapturer::Create(v8::Isolate* isolate) {
return handle;
}
// static
#if !BUILDFLAG(IS_MAC)
bool DesktopCapturer::IsDisplayMediaSystemPickerAvailable() {
return false;
}
#endif
gin::ObjectTemplateBuilder DesktopCapturer::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return gin::Wrappable<DesktopCapturer>::GetObjectTemplateBuilder(isolate)
@@ -524,6 +531,9 @@ void Initialize(v8::Local<v8::Object> exports,
gin_helper::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("createDesktopCapturer",
&electron::api::DesktopCapturer::Create);
dict.SetMethod(
"isDisplayMediaSystemPickerAvailable",
&electron::api::DesktopCapturer::IsDisplayMediaSystemPickerAvailable);
}
} // namespace

View File

@@ -21,9 +21,9 @@ class Handle;
namespace electron::api {
class DesktopCapturer : public gin::Wrappable<DesktopCapturer>,
public gin_helper::Pinnable<DesktopCapturer>,
private DesktopMediaListObserver {
class DesktopCapturer final : public gin::Wrappable<DesktopCapturer>,
public gin_helper::Pinnable<DesktopCapturer>,
private DesktopMediaListObserver {
public:
struct Source {
DesktopMediaList::Source media_list_source;
@@ -36,6 +36,8 @@ class DesktopCapturer : public gin::Wrappable<DesktopCapturer>,
static gin::Handle<DesktopCapturer> Create(v8::Isolate* isolate);
static bool IsDisplayMediaSystemPickerAvailable();
void StartHandling(bool capture_window,
bool capture_screen,
const gfx::Size& thumbnail_size,

View File

@@ -0,0 +1,17 @@
// Copyright (c) 2024 Salesforce, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "shell/browser/api/electron_api_desktop_capturer.h"
namespace electron::api {
// static
bool DesktopCapturer::IsDisplayMediaSystemPickerAvailable() {
if (@available(macOS 15.0, *)) {
return true;
}
return false;
}
} // namespace electron::api

View File

@@ -25,10 +25,10 @@ class Handle;
namespace electron::api {
class DownloadItem : public gin::Wrappable<DownloadItem>,
public gin_helper::Pinnable<DownloadItem>,
public gin_helper::EventEmitterMixin<DownloadItem>,
private download::DownloadItem::Observer {
class DownloadItem final : public gin::Wrappable<DownloadItem>,
public gin_helper::Pinnable<DownloadItem>,
public gin_helper::EventEmitterMixin<DownloadItem>,
private download::DownloadItem::Observer {
public:
static gin::Handle<DownloadItem> FromOrCreate(v8::Isolate* isolate,
download::DownloadItem* item);

View File

@@ -20,8 +20,9 @@ class Handle;
namespace electron::api {
class GlobalShortcut : private extensions::GlobalShortcutListener::Observer,
public gin::Wrappable<GlobalShortcut> {
class GlobalShortcut final
: private extensions::GlobalShortcutListener::Observer,
public gin::Wrappable<GlobalShortcut> {
public:
static gin::Handle<GlobalShortcut> Create(v8::Isolate* isolate);

View File

@@ -22,9 +22,9 @@ class Handle;
namespace electron::api {
class InAppPurchase : public gin::Wrappable<InAppPurchase>,
public gin_helper::EventEmitterMixin<InAppPurchase>,
private in_app_purchase::TransactionObserver {
class InAppPurchase final : public gin::Wrappable<InAppPurchase>,
public gin_helper::EventEmitterMixin<InAppPurchase>,
private in_app_purchase::TransactionObserver {
public:
static gin::Handle<InAppPurchase> Create(v8::Isolate* isolate);

View File

@@ -18,9 +18,9 @@ class handle;
namespace electron::api {
class NativeTheme : public gin::Wrappable<NativeTheme>,
public gin_helper::EventEmitterMixin<NativeTheme>,
private ui::NativeThemeObserver {
class NativeTheme final : public gin::Wrappable<NativeTheme>,
public gin_helper::EventEmitterMixin<NativeTheme>,
private ui::NativeThemeObserver {
public:
static gin::Handle<NativeTheme> Create(v8::Isolate* isolate);

View File

@@ -35,7 +35,7 @@ class ElectronBrowserContext;
namespace api {
// The code is referenced from the net_log::NetExportFileWriter class.
class NetLog : public gin::Wrappable<NetLog> {
class NetLog final : public gin::Wrappable<NetLog> {
public:
static gin::Handle<NetLog> Create(v8::Isolate* isolate,
ElectronBrowserContext* browser_context);

View File

@@ -30,11 +30,11 @@ class ErrorThrower;
namespace electron::api {
class Notification : public gin::Wrappable<Notification>,
public gin_helper::EventEmitterMixin<Notification>,
public gin_helper::Constructible<Notification>,
public gin_helper::CleanedUpAtExit,
public NotificationDelegate {
class Notification final : public gin::Wrappable<Notification>,
public gin_helper::EventEmitterMixin<Notification>,
public gin_helper::Constructible<Notification>,
public gin_helper::CleanedUpAtExit,
public NotificationDelegate {
public:
static bool IsSupported();

View File

@@ -17,12 +17,12 @@
namespace electron::api {
class PowerMonitor : public gin::Wrappable<PowerMonitor>,
public gin_helper::EventEmitterMixin<PowerMonitor>,
public gin_helper::Pinnable<PowerMonitor>,
private base::PowerStateObserver,
private base::PowerSuspendObserver,
private base::PowerThermalObserver {
class PowerMonitor final : public gin::Wrappable<PowerMonitor>,
public gin_helper::EventEmitterMixin<PowerMonitor>,
public gin_helper::Pinnable<PowerMonitor>,
private base::PowerStateObserver,
private base::PowerSuspendObserver,
private base::PowerThermalObserver {
public:
static v8::Local<v8::Value> Create(v8::Isolate* isolate);

View File

@@ -19,7 +19,7 @@ class Handle;
namespace electron::api {
class PowerSaveBlocker : public gin::Wrappable<PowerSaveBlocker> {
class PowerSaveBlocker final : public gin::Wrappable<PowerSaveBlocker> {
public:
static gin::Handle<PowerSaveBlocker> Create(v8::Isolate* isolate);

View File

@@ -45,8 +45,8 @@ enum class ProtocolError {
};
// Protocol implementation based on network services.
class Protocol : public gin::Wrappable<Protocol>,
public gin_helper::Constructible<Protocol> {
class Protocol final : public gin::Wrappable<Protocol>,
public gin_helper::Constructible<Protocol> {
public:
static gin::Handle<Protocol> Create(v8::Isolate* isolate,
ElectronBrowserContext* browser_context);

View File

@@ -21,7 +21,7 @@ class Handle;
namespace electron::api {
class PushNotifications
class PushNotifications final
: public ElectronBrowserClient::Delegate,
public gin::Wrappable<PushNotifications>,
public gin_helper::EventEmitterMixin<PushNotifications>,

View File

@@ -25,9 +25,9 @@ class ErrorThrower;
namespace electron::api {
class Screen : public gin::Wrappable<Screen>,
public gin_helper::EventEmitterMixin<Screen>,
private display::DisplayObserver {
class Screen final : public gin::Wrappable<Screen>,
public gin_helper::EventEmitterMixin<Screen>,
private display::DisplayObserver {
public:
static v8::Local<v8::Value> Create(gin_helper::ErrorThrower error_thrower);

View File

@@ -22,7 +22,7 @@ class ElectronBrowserContext;
namespace api {
class ServiceWorkerContext
class ServiceWorkerContext final
: public gin::Wrappable<ServiceWorkerContext>,
public gin_helper::EventEmitterMixin<ServiceWorkerContext>,
private content::ServiceWorkerContextObserver {

View File

@@ -1607,7 +1607,7 @@ void Session::FillObjectTemplate(v8::Isolate* isolate,
&Session::SetPermissionRequestHandler)
.SetMethod("setPermissionCheckHandler",
&Session::SetPermissionCheckHandler)
.SetMethod("setDisplayMediaRequestHandler",
.SetMethod("_setDisplayMediaRequestHandler",
&Session::SetDisplayMediaRequestHandler)
.SetMethod("setDevicePermissionHandler",
&Session::SetDevicePermissionHandler)

View File

@@ -60,18 +60,18 @@ class ElectronBrowserContext;
namespace api {
class Session : public gin::Wrappable<Session>,
public gin_helper::Pinnable<Session>,
public gin_helper::Constructible<Session>,
public gin_helper::EventEmitterMixin<Session>,
public gin_helper::CleanedUpAtExit,
class Session final : public gin::Wrappable<Session>,
public gin_helper::Pinnable<Session>,
public gin_helper::Constructible<Session>,
public gin_helper::EventEmitterMixin<Session>,
public gin_helper::CleanedUpAtExit,
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
private SpellcheckHunspellDictionary::Observer,
private SpellcheckHunspellDictionary::Observer,
#endif
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
private extensions::ExtensionRegistryObserver,
private extensions::ExtensionRegistryObserver,
#endif
private content::DownloadManager::Observer {
private content::DownloadManager::Observer {
public:
// Gets or creates Session from the |browser_context|.
static gin::Handle<Session> CreateFrom(

View File

@@ -37,7 +37,7 @@ enum class NotificationCenterKind {
};
#endif
class SystemPreferences
class SystemPreferences final
: public gin::Wrappable<SystemPreferences>,
public gin_helper::EventEmitterMixin<SystemPreferences>
#if BUILDFLAG(IS_WIN)

View File

@@ -38,12 +38,12 @@ namespace electron::api {
class Menu;
class Tray : public gin::Wrappable<Tray>,
public gin_helper::EventEmitterMixin<Tray>,
public gin_helper::Constructible<Tray>,
public gin_helper::CleanedUpAtExit,
public gin_helper::Pinnable<Tray>,
private TrayIconObserver {
class Tray final : public gin::Wrappable<Tray>,
public gin_helper::EventEmitterMixin<Tray>,
public gin_helper::Constructible<Tray>,
public gin_helper::CleanedUpAtExit,
public gin_helper::Pinnable<Tray>,
private TrayIconObserver {
public:
// gin_helper::Constructible
static gin::Handle<Tray> New(gin_helper::ErrorThrower thrower,

View File

@@ -39,7 +39,7 @@ class Connector;
namespace electron::api {
class UtilityProcessWrapper
class UtilityProcessWrapper final
: public gin::Wrappable<UtilityProcessWrapper>,
public gin_helper::Pinnable<UtilityProcessWrapper>,
public gin_helper::EventEmitterMixin<UtilityProcessWrapper>,

View File

@@ -1887,7 +1887,7 @@ namespace {
// This object wraps the InvokeCallback so that if it gets GC'd by V8, we can
// still call the callback and send an error. Not doing so causes a Mojo DCHECK,
// since Mojo requires callbacks to be called before they are destroyed.
class ReplyChannel : public gin::Wrappable<ReplyChannel> {
class ReplyChannel final : public gin::Wrappable<ReplyChannel> {
public:
using InvokeCallback = electron::mojom::ElectronApiIPC::InvokeCallback;
static gin::Handle<ReplyChannel> Create(v8::Isolate* isolate,

View File

@@ -109,19 +109,19 @@ class BaseWindow;
class FrameSubscriber;
// Wrapper around the content::WebContents.
class WebContents : public ExclusiveAccessContext,
public gin::Wrappable<WebContents>,
public gin_helper::EventEmitterMixin<WebContents>,
public gin_helper::Constructible<WebContents>,
public gin_helper::Pinnable<WebContents>,
public gin_helper::CleanedUpAtExit,
public content::WebContentsObserver,
public content::WebContentsDelegate,
private content::RenderWidgetHost::InputEventObserver,
public content::JavaScriptDialogManager,
public InspectableWebContentsDelegate,
public InspectableWebContentsViewDelegate,
public BackgroundThrottlingSource {
class WebContents final : public ExclusiveAccessContext,
public gin::Wrappable<WebContents>,
public gin_helper::EventEmitterMixin<WebContents>,
public gin_helper::Constructible<WebContents>,
public gin_helper::Pinnable<WebContents>,
public gin_helper::CleanedUpAtExit,
public content::WebContentsObserver,
public content::WebContentsDelegate,
private content::RenderWidgetHost::InputEventObserver,
public content::JavaScriptDialogManager,
public InspectableWebContentsDelegate,
public InspectableWebContentsViewDelegate,
public BackgroundThrottlingSource {
public:
enum class Type {
kBackgroundPage, // An extension background page.

View File

@@ -38,10 +38,10 @@ namespace electron::api {
class WebContents;
// Bindings for accessing frames from the main process.
class WebFrameMain : public gin::Wrappable<WebFrameMain>,
public gin_helper::EventEmitterMixin<WebFrameMain>,
public gin_helper::Pinnable<WebFrameMain>,
public gin_helper::Constructible<WebFrameMain> {
class WebFrameMain final : public gin::Wrappable<WebFrameMain>,
public gin_helper::EventEmitterMixin<WebFrameMain>,
public gin_helper::Pinnable<WebFrameMain>,
public gin_helper::Constructible<WebFrameMain> {
public:
// Create a new WebFrameMain and return the V8 wrapper of it.
static gin::Handle<WebFrameMain> New(v8::Isolate* isolate);

View File

@@ -21,7 +21,6 @@
#include "gin/dictionary.h"
#include "gin/handle.h"
#include "gin/object_template_builder.h"
#include "net/http/http_content_disposition.h"
#include "shell/browser/api/electron_api_session.h"
#include "shell/browser/api/electron_api_web_contents.h"
#include "shell/browser/api/electron_api_web_frame_main.h"
@@ -100,22 +99,6 @@ v8::Local<v8::Value> HttpResponseHeadersToV8(
std::string key;
std::string value;
while (headers->EnumerateHeaderLines(&iter, &key, &value)) {
// Note that Web servers not developed with nodejs allow non-utf8
// characters in content-disposition's filename field. Use Chromium's
// HttpContentDisposition class to decode the correct encoding instead of
// arbitrarily converting it to UTF8. It should also be noted that if the
// encoding is not specified, HttpContentDisposition will transcode
// according to the system's encoding.
if (base::EqualsCaseInsensitiveASCII("Content-Disposition", key) &&
!value.empty()) {
net::HttpContentDisposition header(value, std::string());
std::string decodedFilename =
header.is_attachment() ? " attachment" : " inline";
// The filename must be encased in double quotes for serialization
// to happen correctly.
std::string filename = "\"" + header.filename() + "\"";
value = decodedFilename + "; filename=" + filename;
}
response_headers.EnsureList(key)->Append(value);
}
}

View File

@@ -31,7 +31,8 @@ class Handle;
namespace electron::api {
class WebRequest : public gin::Wrappable<WebRequest>, public WebRequestAPI {
class WebRequest final : public gin::Wrappable<WebRequest>,
public WebRequestAPI {
public:
// Return the WebRequest object attached to |browser_context|, create if there
// is no one.

View File

@@ -30,12 +30,12 @@ namespace electron {
namespace {
bool IsValidWrappable(const v8::Local<v8::Value>& obj) {
v8::Local<v8::Object> port = v8::Local<v8::Object>::Cast(obj);
if (!port->IsObject())
bool IsValidWrappable(const v8::Local<v8::Value>& val) {
if (!val->IsObject())
return false;
v8::Local<v8::Object> port = val.As<v8::Object>();
if (port->InternalFieldCount() != gin::kNumberOfInternalFields)
return false;

View File

@@ -27,9 +27,9 @@ class Connector;
namespace electron {
// A non-blink version of blink::MessagePort.
class MessagePort : public gin::Wrappable<MessagePort>,
public gin_helper::CleanedUpAtExit,
public mojo::MessageReceiver {
class MessagePort final : public gin::Wrappable<MessagePort>,
public gin_helper::CleanedUpAtExit,
public mojo::MessageReceiver {
public:
~MessagePort() override;
static gin::Handle<MessagePort> Create(v8::Isolate* isolate);

View File

@@ -31,9 +31,10 @@ BadgeManagerFactory::BadgeManagerFactory()
BadgeManagerFactory::~BadgeManagerFactory() = default;
KeyedService* BadgeManagerFactory::BuildServiceInstanceFor(
std::unique_ptr<KeyedService>
BadgeManagerFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
return new BadgeManager();
return std::make_unique<BadgeManager>();
}
} // namespace badging

View File

@@ -36,7 +36,7 @@ class BadgeManagerFactory : public BrowserContextKeyedServiceFactory {
~BadgeManagerFactory() override;
// BrowserContextKeyedServiceFactory
KeyedService* BuildServiceInstanceFor(
std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
};

View File

@@ -4,8 +4,14 @@
#include "shell/browser/electron_pdf_document_helper_client.h"
#include "chrome/browser/pdf/pdf_viewer_stream_manager.h"
#include "chrome/common/content_restriction.h"
#include "components/pdf/browser/pdf_frame_util.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
#include "pdf/content_restriction.h"
#include "pdf/pdf_features.h"
#include "shell/browser/api/electron_api_web_contents.h"
ElectronPDFDocumentHelperClient::ElectronPDFDocumentHelperClient() = default;
@@ -15,8 +21,8 @@ void ElectronPDFDocumentHelperClient::UpdateContentRestrictions(
content::RenderFrameHost* render_frame_host,
int content_restrictions) {
// UpdateContentRestrictions potentially gets called twice from
// pdf/pdf_view_web_plugin.cc. The first time it is potentially called is
// when loading starts and it is called with a restriction on printing. The
// pdf/pdf_view_web_plugin.cc. The first time it is potentially called is
// when loading starts and it is called with a restriction on printing. The
// second time it is called is when loading is finished and if printing is
// allowed there won't be a printing restriction passed, so we can use this
// second call to notify that the pdf document is ready to print.
@@ -31,3 +37,29 @@ void ElectronPDFDocumentHelperClient::UpdateContentRestrictions(
}
}
}
void ElectronPDFDocumentHelperClient::SetPluginCanSave(
content::RenderFrameHost* render_frame_host,
bool can_save) {
if (chrome_pdf::features::IsOopifPdfEnabled()) {
auto* pdf_viewer_stream_manager =
pdf::PdfViewerStreamManager::FromWebContents(
content::WebContents::FromRenderFrameHost(render_frame_host));
if (!pdf_viewer_stream_manager) {
return;
}
content::RenderFrameHost* embedder_host =
pdf_frame_util::GetEmbedderHost(render_frame_host);
CHECK(embedder_host);
pdf_viewer_stream_manager->SetPluginCanSave(embedder_host, can_save);
return;
}
auto* guest_view =
extensions::MimeHandlerViewGuest::FromRenderFrameHost(render_frame_host);
if (guest_view) {
guest_view->SetPluginCanSave(can_save);
}
}

View File

@@ -17,13 +17,12 @@ class ElectronPDFDocumentHelperClient : public pdf::PDFDocumentHelperClient {
private:
// pdf::PDFDocumentHelperClient
void UpdateContentRestrictions(content::RenderFrameHost* render_frame_host,
int content_restrictions) override;
void OnPDFHasUnsupportedFeature(content::WebContents* contents) override {}
void OnSaveURL(content::WebContents* contents) override {}
void SetPluginCanSave(content::RenderFrameHost* render_frame_host,
bool can_save) override {}
bool can_save) override;
};
#endif // ELECTRON_SHELL_BROWSER_ELECTRON_PDF_DOCUMENT_HELPER_CLIENT_H_

View File

@@ -35,9 +35,10 @@ ElectronExtensionSystemFactory::ElectronExtensionSystemFactory()
ElectronExtensionSystemFactory::~ElectronExtensionSystemFactory() = default;
KeyedService* ElectronExtensionSystemFactory::BuildServiceInstanceFor(
std::unique_ptr<KeyedService>
ElectronExtensionSystemFactory::BuildServiceInstanceForBrowserContext(
BrowserContext* context) const {
return new ElectronExtensionSystem(context);
return std::make_unique<ElectronExtensionSystem>(context);
}
BrowserContext* ElectronExtensionSystemFactory::GetBrowserContextToUse(

View File

@@ -5,6 +5,8 @@
#ifndef ELECTRON_SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_SYSTEM_FACTORY_H_
#define ELECTRON_SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSION_SYSTEM_FACTORY_H_
#include <memory>
#include "extensions/browser/extension_system_provider.h"
namespace base {
@@ -36,7 +38,7 @@ class ElectronExtensionSystemFactory : public ExtensionSystemProvider {
~ElectronExtensionSystemFactory() override;
// BrowserContextKeyedServiceFactory implementation:
KeyedService* BuildServiceInstanceFor(
std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;

View File

@@ -58,6 +58,11 @@ void InitializeFeatureList() {
if (platform_specific_enable_features.size() > 0) {
enable_features += std::string(",") + platform_specific_enable_features;
}
std::string platform_specific_disable_features =
DisablePlatformSpecificFeatures();
if (platform_specific_disable_features.size() > 0) {
disable_features += std::string(",") + platform_specific_disable_features;
}
base::FeatureList::InitInstance(enable_features, disable_features);
}
@@ -73,6 +78,9 @@ void InitializeFieldTrials() {
std::string EnablePlatformSpecificFeatures() {
return "";
}
std::string DisablePlatformSpecificFeatures() {
return "";
}
#endif
} // namespace electron

View File

@@ -11,6 +11,7 @@ namespace electron {
void InitializeFeatureList();
void InitializeFieldTrials();
std::string EnablePlatformSpecificFeatures();
std::string DisablePlatformSpecificFeatures();
} // namespace electron
#endif // ELECTRON_SHELL_BROWSER_FEATURE_LIST_H_

View File

@@ -31,4 +31,13 @@ std::string EnablePlatformSpecificFeatures() {
return "";
}
std::string DisablePlatformSpecificFeatures() {
if (@available(macOS 14.4, *)) {
// Required to stop timing out getDisplayMedia while waiting for
// the user to select a window with the picker
return "TimeoutHangingVideoCaptureStarts";
}
return "";
}
} // namespace electron

View File

@@ -36,15 +36,9 @@ FileSystemAccessPermissionContextFactory::
FileSystemAccessPermissionContextFactory::
~FileSystemAccessPermissionContextFactory() = default;
// static
KeyedService* FileSystemAccessPermissionContextFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return BuildInstanceFor(context).release();
}
std::unique_ptr<KeyedService>
FileSystemAccessPermissionContextFactory::BuildInstanceFor(
content::BrowserContext* context) {
FileSystemAccessPermissionContextFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
return std::make_unique<FileSystemAccessPermissionContext>(context);
}

View File

@@ -5,6 +5,8 @@
#ifndef ELECTRON_SHELL_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_PERMISSION_CONTEXT_FACTORY_H_
#define ELECTRON_SHELL_BROWSER_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_PERMISSION_CONTEXT_FACTORY_H_
#include <memory>
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "shell/browser/file_system_access/file_system_access_permission_context.h"
@@ -18,9 +20,6 @@ class FileSystemAccessPermissionContextFactory
content::BrowserContext* context);
static FileSystemAccessPermissionContextFactory* GetInstance();
static std::unique_ptr<KeyedService> BuildInstanceFor(
content::BrowserContext* context);
FileSystemAccessPermissionContextFactory(
const FileSystemAccessPermissionContextFactory&) = delete;
FileSystemAccessPermissionContextFactory& operator=(
@@ -33,7 +32,7 @@ class FileSystemAccessPermissionContextFactory
~FileSystemAccessPermissionContextFactory() override;
// BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor(
std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
};

View File

@@ -37,11 +37,11 @@ HidChooserContextFactory::HidChooserContextFactory()
HidChooserContextFactory::~HidChooserContextFactory() = default;
KeyedService* HidChooserContextFactory::BuildServiceInstanceFor(
std::unique_ptr<KeyedService>
HidChooserContextFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
auto* browser_context =
static_cast<electron::ElectronBrowserContext*>(context);
return new HidChooserContext(browser_context);
return std::make_unique<HidChooserContext>(
static_cast<electron::ElectronBrowserContext*>(context));
}
content::BrowserContext* HidChooserContextFactory::GetBrowserContextToUse(

View File

@@ -5,6 +5,8 @@
#ifndef ELECTRON_SHELL_BROWSER_HID_HID_CHOOSER_CONTEXT_FACTORY_H_
#define ELECTRON_SHELL_BROWSER_HID_HID_CHOOSER_CONTEXT_FACTORY_H_
#include <memory>
#include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
@@ -31,7 +33,7 @@ class HidChooserContextFactory : public BrowserContextKeyedServiceFactory {
~HidChooserContextFactory() override;
// BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor(
std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;

View File

@@ -71,8 +71,9 @@ class NativeWindow : public base::SupportsUserData,
// Create window with existing WebContents, the caller is responsible for
// managing the window's live.
static NativeWindow* Create(const gin_helper::Dictionary& options,
NativeWindow* parent = nullptr);
static std::unique_ptr<NativeWindow> Create(
const gin_helper::Dictionary& options,
NativeWindow* parent = nullptr);
void InitFromOptions(const gin_helper::Dictionary& options);

View File

@@ -1814,9 +1814,10 @@ std::optional<gfx::Rect> NativeWindowMac::GetWindowControlsOverlayRect() {
}
// static
NativeWindow* NativeWindow::Create(const gin_helper::Dictionary& options,
NativeWindow* parent) {
return new NativeWindowMac(options, parent);
std::unique_ptr<NativeWindow> NativeWindow::Create(
const gin_helper::Dictionary& options,
NativeWindow* parent) {
return std::make_unique<NativeWindowMac>(options, parent);
}
} // namespace electron

View File

@@ -1789,9 +1789,10 @@ void NativeWindowViews::MoveBehindTaskBarIfNeeded() {
}
// static
NativeWindow* NativeWindow::Create(const gin_helper::Dictionary& options,
NativeWindow* parent) {
return new NativeWindowViews(options, parent);
std::unique_ptr<NativeWindow> NativeWindow::Create(
const gin_helper::Dictionary& options,
NativeWindow* parent) {
return std::make_unique<NativeWindowViews>(options, parent);
}
} // namespace electron

View File

@@ -29,9 +29,10 @@ NetworkContextServiceFactory::NetworkContextServiceFactory()
NetworkContextServiceFactory::~NetworkContextServiceFactory() = default;
KeyedService* NetworkContextServiceFactory::BuildServiceInstanceFor(
std::unique_ptr<KeyedService>
NetworkContextServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
return new NetworkContextService(
return std::make_unique<NetworkContextService>(
static_cast<ElectronBrowserContext*>(context));
}

View File

@@ -5,6 +5,8 @@
#ifndef ELECTRON_SHELL_BROWSER_NET_NETWORK_CONTEXT_SERVICE_FACTORY_H_
#define ELECTRON_SHELL_BROWSER_NET_NETWORK_CONTEXT_SERVICE_FACTORY_H_
#include <memory>
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
class KeyedService;
@@ -43,7 +45,7 @@ class NetworkContextServiceFactory : public BrowserContextKeyedServiceFactory {
~NetworkContextServiceFactory() override;
// BrowserContextKeyedServiceFactory implementation:
KeyedService* BuildServiceInstanceFor(
std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;

View File

@@ -18,11 +18,11 @@ SerialChooserContextFactory::SerialChooserContextFactory()
SerialChooserContextFactory::~SerialChooserContextFactory() = default;
KeyedService* SerialChooserContextFactory::BuildServiceInstanceFor(
std::unique_ptr<KeyedService>
SerialChooserContextFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
auto* browser_context =
static_cast<electron::ElectronBrowserContext*>(context);
return new SerialChooserContext(browser_context);
return std::make_unique<SerialChooserContext>(
static_cast<electron::ElectronBrowserContext*>(context));
}
// static

View File

@@ -5,6 +5,8 @@
#ifndef ELECTRON_SHELL_BROWSER_SERIAL_SERIAL_CHOOSER_CONTEXT_FACTORY_H_
#define ELECTRON_SHELL_BROWSER_SERIAL_SERIAL_CHOOSER_CONTEXT_FACTORY_H_
#include <memory>
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "shell/browser/serial/serial_chooser_context.h"
@@ -35,7 +37,7 @@ class SerialChooserContextFactory : public BrowserContextKeyedServiceFactory {
delete;
// BrowserContextKeyedServiceFactory methods:
KeyedService* BuildServiceInstanceFor(
std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const override;
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;

View File

@@ -15,13 +15,9 @@ DelayedNativeViewHost::~DelayedNativeViewHost() = default;
void DelayedNativeViewHost::ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) {
// NativeViewHost doesn't expect to have children, so filter the
// ViewHierarchyChanged events before passing them on.
if (details.child == this) {
NativeViewHost::ViewHierarchyChanged(details);
if (details.is_add && GetWidget() && !native_view())
Attach(native_view_);
}
NativeViewHost::ViewHierarchyChanged(details);
if (details.is_add && GetWidget() && !native_view())
Attach(native_view_);
}
bool DelayedNativeViewHost::OnMousePressed(const ui::MouseEvent& ui_event) {

View File

@@ -785,6 +785,7 @@ void InspectableWebContents::SetEyeDropperActive(bool active) {
if (delegate_)
delegate_->DevToolsSetEyeDropperActive(active);
}
void InspectableWebContents::ZoomIn() {
double new_level = GetNextZoomLevel(GetDevToolsZoomLevel(), false);
SetZoomLevelForWebContents(GetDevToolsWebContents(), new_level);
@@ -966,6 +967,13 @@ void InspectableWebContents::CloseContents(content::WebContents* source) {
CloseDevTools();
}
std::unique_ptr<content::EyeDropper> InspectableWebContents::OpenEyeDropper(
content::RenderFrameHost* frame,
content::EyeDropperListener* listener) {
auto* delegate = web_contents_->GetDelegate();
return delegate ? delegate->OpenEyeDropper(frame, listener) : nullptr;
}
void InspectableWebContents::RunFileChooser(
content::RenderFrameHost* render_frame_host,
scoped_refptr<content::FileSelectListener> listener,

View File

@@ -208,6 +208,9 @@ class InspectableWebContents
bool HandleKeyboardEvent(content::WebContents*,
const input::NativeWebKeyboardEvent&) override;
void CloseContents(content::WebContents* source) override;
std::unique_ptr<content::EyeDropper> OpenEyeDropper(
content::RenderFrameHost* frame,
content::EyeDropperListener* listener) override;
void RunFileChooser(content::RenderFrameHost* render_frame_host,
scoped_refptr<content::FileSelectListener> listener,
const blink::mojom::FileChooserParams& params) override;

View File

@@ -18,11 +18,11 @@ UsbChooserContextFactory::UsbChooserContextFactory()
UsbChooserContextFactory::~UsbChooserContextFactory() = default;
KeyedService* UsbChooserContextFactory::BuildServiceInstanceFor(
std::unique_ptr<KeyedService>
UsbChooserContextFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
auto* browser_context =
static_cast<electron::ElectronBrowserContext*>(context);
return new UsbChooserContext(browser_context);
return std::make_unique<UsbChooserContext>(
static_cast<electron::ElectronBrowserContext*>(context));
}
// static

View File

@@ -5,6 +5,8 @@
#ifndef ELECTRON_SHELL_BROWSER_USB_USB_CHOOSER_CONTEXT_FACTORY_H_
#define ELECTRON_SHELL_BROWSER_USB_USB_CHOOSER_CONTEXT_FACTORY_H_
#include <memory>
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
namespace base {
@@ -34,7 +36,7 @@ class UsbChooserContextFactory : public BrowserContextKeyedServiceFactory {
~UsbChooserContextFactory() override;
// BrowserContextKeyedServiceFactory methods:
KeyedService* BuildServiceInstanceFor(
std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
content::BrowserContext* profile) const override;
};

View File

@@ -57,7 +57,7 @@ namespace {
#if BUILDFLAG(IS_MAC)
bool SystemMediaPermissionDenied(const content::MediaStreamRequest& request) {
if (request.audio_type != MediaStreamType::NO_SERVICE) {
if (request.audio_type == MediaStreamType::DEVICE_AUDIO_CAPTURE) {
const auto system_audio_permission =
system_media_permissions::CheckSystemAudioCapturePermission();
return system_audio_permission ==
@@ -65,7 +65,7 @@ bool SystemMediaPermissionDenied(const content::MediaStreamRequest& request) {
system_audio_permission ==
system_media_permissions::SystemPermission::kDenied;
}
if (request.video_type != MediaStreamType::NO_SERVICE) {
if (request.video_type == MediaStreamType::DEVICE_VIDEO_CAPTURE) {
const auto system_video_permission =
system_media_permissions::CheckSystemVideoCapturePermission();
return system_video_permission ==
@@ -73,6 +73,7 @@ bool SystemMediaPermissionDenied(const content::MediaStreamRequest& request) {
system_video_permission ==
system_media_permissions::SystemPermission::kDenied;
}
return false;
}
#endif

View File

@@ -374,18 +374,15 @@ gin::Handle<NativeImage> NativeImage::Resize(gin::Arguments* args,
else if (quality && *quality == "better")
method = skia::ImageOperations::ResizeMethod::RESIZE_BETTER;
gfx::ImageSkia resized = gfx::ImageSkiaOperations::CreateResizedImage(
image_.AsImageSkia(), method, size);
return gin::CreateHandle(
args->isolate(), new NativeImage(args->isolate(), gfx::Image(resized)));
return Create(args->isolate(),
gfx::Image{gfx::ImageSkiaOperations::CreateResizedImage(
image_.AsImageSkia(), method, size)});
}
gin::Handle<NativeImage> NativeImage::Crop(v8::Isolate* isolate,
const gfx::Rect& rect) {
gfx::ImageSkia cropped =
gfx::ImageSkiaOperations::ExtractSubset(image_.AsImageSkia(), rect);
return gin::CreateHandle(isolate,
new NativeImage(isolate, gfx::Image(cropped)));
return Create(isolate, gfx::Image{gfx::ImageSkiaOperations::ExtractSubset(
image_.AsImageSkia(), rect)});
}
void NativeImage::AddRepresentation(const gin_helper::Dictionary& options) {
@@ -437,7 +434,7 @@ bool NativeImage::IsTemplateImage() {
// static
gin::Handle<NativeImage> NativeImage::CreateEmpty(v8::Isolate* isolate) {
return gin::CreateHandle(isolate, new NativeImage(isolate, gfx::Image()));
return Create(isolate, gfx::Image{});
}
// static

View File

@@ -45,7 +45,7 @@ class ErrorThrower;
namespace electron::api {
class NativeImage : public gin::Wrappable<NativeImage> {
class NativeImage final : public gin::Wrappable<NativeImage> {
public:
NativeImage(v8::Isolate* isolate, const gfx::Image& image);
#if BUILDFLAG(IS_WIN)

View File

@@ -162,8 +162,9 @@ class BufferDataSource : public mojo::DataPipeProducer::DataSource {
std::vector<char> buffer_;
};
class JSChunkedDataPipeGetter : public gin::Wrappable<JSChunkedDataPipeGetter>,
public network::mojom::ChunkedDataPipeGetter {
class JSChunkedDataPipeGetter final
: public gin::Wrappable<JSChunkedDataPipeGetter>,
public network::mojom::ChunkedDataPipeGetter {
public:
static gin::Handle<JSChunkedDataPipeGetter> Create(
v8::Isolate* isolate,

View File

@@ -47,7 +47,7 @@ class ElectronBrowserContext;
namespace electron::api {
/** Wraps a SimpleURLLoader to make it usable from JavaScript */
class SimpleURLLoaderWrapper
class SimpleURLLoaderWrapper final
: public gin::Wrappable<SimpleURLLoaderWrapper>,
public gin_helper::EventEmitterMixin<SimpleURLLoaderWrapper>,
private network::SimpleURLLoaderStreamConsumer,

View File

@@ -201,22 +201,19 @@ bool Archive::Init() {
return false;
}
std::vector<char> buf;
int len;
std::vector<uint8_t> buf;
buf.resize(8);
{
electron::ScopedAllowBlockingForElectron allow_blocking;
len = file_.ReadAtCurrentPos(buf.data(), buf.size());
}
if (len != static_cast<int>(buf.size())) {
PLOG(ERROR) << "Failed to read header size from " << path_.value();
return false;
if (!file_.ReadAtCurrentPosAndCheck(buf)) {
PLOG(ERROR) << "Failed to read header size from " << path_.value();
return false;
}
}
uint32_t size;
if (!base::PickleIterator(base::Pickle::WithData(base::as_byte_span(buf)))
.ReadUInt32(&size)) {
if (!base::PickleIterator(base::Pickle::WithData(buf)).ReadUInt32(&size)) {
LOG(ERROR) << "Failed to parse header size from " << path_.value();
return false;
}
@@ -224,16 +221,14 @@ bool Archive::Init() {
buf.resize(size);
{
electron::ScopedAllowBlockingForElectron allow_blocking;
len = file_.ReadAtCurrentPos(buf.data(), buf.size());
}
if (len != static_cast<int>(buf.size())) {
PLOG(ERROR) << "Failed to read header from " << path_.value();
return false;
if (!file_.ReadAtCurrentPosAndCheck(buf)) {
PLOG(ERROR) << "Failed to read header from " << path_.value();
return false;
}
}
std::string header;
if (!base::PickleIterator(base::Pickle::WithData(base::as_byte_span(buf)))
.ReadString(&header)) {
if (!base::PickleIterator(base::Pickle::WithData(buf)).ReadString(&header)) {
LOG(ERROR) << "Failed to parse header from " << path_.value();
return false;
}
@@ -251,9 +246,8 @@ bool Archive::Init() {
// Currently we only support the sha256 algorithm, we can add support for
// more below ensure we read them in preference order from most secure to
// least
if (integrity.value().algorithm != HashAlgorithm::kNone) {
ValidateIntegrityOrDie(header.c_str(), header.length(),
integrity.value());
if (integrity->algorithm != HashAlgorithm::kNone) {
ValidateIntegrityOrDie(base::as_byte_span(header), *integrity);
} else {
LOG(FATAL) << "No eligible hash for validatable asar archive: "
<< RelativePath().value();

View File

@@ -132,25 +132,17 @@ bool ReadFileToString(const base::FilePath& path, std::string* contents) {
return false;
}
if (info.integrity.has_value()) {
ValidateIntegrityOrDie(contents->data(), contents->size(),
info.integrity.value());
}
if (info.integrity)
ValidateIntegrityOrDie(base::as_byte_span(*contents), *info.integrity);
return true;
}
void ValidateIntegrityOrDie(const char* data,
size_t size,
void ValidateIntegrityOrDie(base::span<const uint8_t> input,
const IntegrityPayload& integrity) {
if (integrity.algorithm == HashAlgorithm::kSHA256) {
uint8_t hash[crypto::kSHA256Length];
auto hasher = crypto::SecureHash::Create(crypto::SecureHash::SHA256);
hasher->Update(data, size);
hasher->Finish(hash, sizeof(hash));
const std::string hex_hash =
base::ToLowerASCII(base::HexEncode(hash, sizeof(hash)));
base::ToLowerASCII(base::HexEncode(crypto::SHA256Hash(input)));
if (integrity.hash != hex_hash) {
LOG(FATAL) << "Integrity check failed for asar archive ("
<< integrity.hash << " vs " << hex_hash << ")";

View File

@@ -8,6 +8,8 @@
#include <memory>
#include <string>
#include "base/containers/span.h"
namespace base {
class FilePath;
}
@@ -29,8 +31,7 @@ bool GetAsarArchivePath(const base::FilePath& full_path,
// Same with base::ReadFileToString but supports asar Archive.
bool ReadFileToString(const base::FilePath& path, std::string* contents);
void ValidateIntegrityOrDie(const char* data,
size_t size,
void ValidateIntegrityOrDie(base::span<const uint8_t> input,
const IntegrityPayload& integrity);
} // namespace asar

View File

@@ -62,21 +62,15 @@ bool ScopedTemporaryFile::InitFromFile(
return false;
electron::ScopedAllowBlockingForElectron allow_blocking;
std::vector<char> buf(size);
int len = src->Read(offset, buf.data(), buf.size());
if (len != static_cast<int>(size))
std::vector<uint8_t> buf(size);
if (!src->ReadAndCheck(offset, buf))
return false;
if (integrity.has_value()) {
ValidateIntegrityOrDie(buf.data(), buf.size(), integrity.value());
}
if (integrity)
ValidateIntegrityOrDie(buf, *integrity);
base::File dest(path_, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
if (!dest.IsValid())
return false;
return dest.WriteAtCurrentPos(buf.data(), buf.size()) ==
static_cast<int>(size);
return dest.IsValid() && dest.WriteAtCurrentPosAndCheck(buf);
}
} // namespace asar

View File

@@ -253,7 +253,7 @@ bool Converter<net::HttpRequestHeaders>::FromV8(v8::Isolate* isolate,
namespace {
class ChunkedDataPipeReadableStream
class ChunkedDataPipeReadableStream final
: public gin::Wrappable<ChunkedDataPipeReadableStream> {
public:
static gin::Handle<ChunkedDataPipeReadableStream> Create(

View File

@@ -23,8 +23,8 @@ class ObjectTemplate;
namespace gin_helper::internal {
class Event : public gin::Wrappable<Event>,
public gin_helper::Constructible<Event> {
class Event final : public gin::Wrappable<Event>,
public gin_helper::Constructible<Event> {
public:
// gin_helper::Constructible
static gin::Handle<Event> New(v8::Isolate* isolate);

View File

@@ -13,15 +13,14 @@ v8::Local<v8::Value> CallMethodWithArgs(v8::Isolate* isolate,
v8::Local<v8::Object> obj,
const char* method,
ValueVector* args) {
// An active node::Environment is required for node::MakeCallback.
std::unique_ptr<node::CallbackScope> callback_scope;
if (node::Environment::GetCurrent(isolate)) {
v8::HandleScope handle_scope(isolate);
callback_scope = std::make_unique<node::CallbackScope>(
isolate, v8::Object::New(isolate), node::async_context{0, 0});
} else {
return v8::Boolean::New(isolate, false);
}
v8::EscapableHandleScope handle_scope{isolate};
// CallbackScope and MakeCallback both require an active node::Environment
if (!node::Environment::GetCurrent(isolate))
return handle_scope.Escape(v8::Boolean::New(isolate, false));
node::CallbackScope callback_scope{isolate, v8::Object::New(isolate),
node::async_context{0, 0}};
// Perform microtask checkpoint after running JavaScript.
gin_helper::MicrotasksScope microtasks_scope{
@@ -36,11 +35,10 @@ v8::Local<v8::Value> CallMethodWithArgs(v8::Isolate* isolate,
// of MakeCallback will be empty and therefore ToLocal will be false, in this
// case we need to return "false" as that indicates that the event emitter did
// not handle the event
v8::Local<v8::Value> localRet;
if (ret.ToLocal(&localRet))
return localRet;
if (v8::Local<v8::Value> localRet; ret.ToLocal(&localRet))
return handle_scope.Escape(localRet);
return v8::Boolean::New(isolate, false);
return handle_scope.Escape(v8::Boolean::New(isolate, false));
}
} // namespace gin_helper::internal

View File

@@ -30,35 +30,38 @@ PromiseBase& PromiseBase::operator=(PromiseBase&&) = default;
v8::Maybe<bool> PromiseBase::Reject() {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = GetContext();
gin_helper::MicrotasksScope microtasks_scope{
isolate(), GetContext()->GetMicrotaskQueue(), false,
isolate(), context->GetMicrotaskQueue(), false,
v8::MicrotasksScope::kRunMicrotasks};
v8::Context::Scope context_scope(GetContext());
v8::Context::Scope context_scope(context);
return GetInner()->Reject(GetContext(), v8::Undefined(isolate()));
return GetInner()->Reject(context, v8::Undefined(isolate()));
}
v8::Maybe<bool> PromiseBase::Reject(v8::Local<v8::Value> except) {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = GetContext();
gin_helper::MicrotasksScope microtasks_scope{
isolate(), GetContext()->GetMicrotaskQueue(), false,
isolate(), context->GetMicrotaskQueue(), false,
v8::MicrotasksScope::kRunMicrotasks};
v8::Context::Scope context_scope(GetContext());
v8::Context::Scope context_scope(context);
return GetInner()->Reject(GetContext(), except);
return GetInner()->Reject(context, except);
}
v8::Maybe<bool> PromiseBase::RejectWithErrorMessage(
const std::string_view message) {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = GetContext();
gin_helper::MicrotasksScope microtasks_scope{
isolate(), GetContext()->GetMicrotaskQueue(), false,
isolate(), context->GetMicrotaskQueue(), false,
v8::MicrotasksScope::kRunMicrotasks};
v8::Context::Scope context_scope(GetContext());
v8::Context::Scope context_scope(context);
v8::Local<v8::Value> error =
v8::Exception::Error(gin::StringToV8(isolate(), message));
return GetInner()->Reject(GetContext(), (error));
return GetInner()->Reject(context, (error));
}
v8::Local<v8::Context> PromiseBase::GetContext() const {
@@ -95,12 +98,13 @@ v8::Local<v8::Promise> Promise<void>::ResolvedPromise(v8::Isolate* isolate) {
v8::Maybe<bool> Promise<void>::Resolve() {
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = GetContext();
gin_helper::MicrotasksScope microtasks_scope{
isolate(), GetContext()->GetMicrotaskQueue(), false,
isolate(), context->GetMicrotaskQueue(), false,
v8::MicrotasksScope::kRunMicrotasks};
v8::Context::Scope context_scope(GetContext());
v8::Context::Scope context_scope(context);
return GetInner()->Resolve(GetContext(), v8::Undefined(isolate()));
return GetInner()->Resolve(context, v8::Undefined(isolate()));
}
} // namespace gin_helper

View File

@@ -123,13 +123,13 @@ class Promise : public PromiseBase {
v8::Maybe<bool> Resolve(const RT& value) {
gin_helper::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = GetContext();
gin_helper::MicrotasksScope microtasks_scope{
isolate(), GetContext()->GetMicrotaskQueue(), false,
isolate(), context->GetMicrotaskQueue(), false,
v8::MicrotasksScope::kRunMicrotasks};
v8::Context::Scope context_scope(GetContext());
v8::Context::Scope context_scope(context);
return GetInner()->Resolve(GetContext(),
gin::ConvertToV8(isolate(), value));
return GetInner()->Resolve(context, gin::ConvertToV8(isolate(), value));
}
template <typename... ResolveType>
@@ -144,12 +144,13 @@ class Promise : public PromiseBase {
"promises resolve type");
gin_helper::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
v8::Context::Scope context_scope(GetContext());
v8::Local<v8::Context> context = GetContext();
v8::Context::Scope context_scope(context);
v8::Local<v8::Value> value = gin::ConvertToV8(isolate(), std::move(cb));
v8::Local<v8::Function> handler = value.As<v8::Function>();
return GetHandle()->Then(GetContext(), handler);
return GetHandle()->Then(context, handler);
}
};

View File

@@ -15,6 +15,7 @@
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/memory/weak_ptr.h"
#include "base/types/to_address.h"
#include "gin/public/context_holder.h"
#include "gin/public/gin_embedders.h"
#include "uv.h" // NOLINT(build/include_directory)
@@ -58,11 +59,34 @@ template <typename T,
std::is_same<T, uv_udp_t>::value>::type* = nullptr>
class UvHandle {
public:
UvHandle() : t_(new T) {}
UvHandle() : t_{new T} {}
~UvHandle() { reset(); }
explicit UvHandle(UvHandle&& that) {
t_ = that.t_;
that.t_ = nullptr;
}
UvHandle& operator=(UvHandle&& that) {
reset();
t_ = that.t_;
that.t_ = nullptr;
return *this;
}
UvHandle(const UvHandle&) = delete;
UvHandle& operator=(const UvHandle&) = delete;
T* get() { return t_; }
T* operator->() { return t_; }
const T* get() const { return t_; }
const T* operator->() const { return t_; }
uv_handle_t* handle() { return reinterpret_cast<uv_handle_t*>(t_); }
// compare by handle pointer address
auto operator<=>(const UvHandle& that) const = default;
void reset() {
auto* h = handle();
if (h != nullptr) {
@@ -80,6 +104,16 @@ class UvHandle {
RAW_PTR_EXCLUSION T* t_ = {};
};
// Helper for comparing UvHandles and raw uv pointers, e.g. as map keys
struct UvHandleCompare {
using is_transparent = void;
template <typename U, typename V>
bool operator()(U const& u, V const& v) const {
return base::to_address(u) < base::to_address(v);
}
};
class NodeBindings {
public:
enum class BrowserEnvironment { kBrowser, kRenderer, kUtility, kWorker };

View File

@@ -23,6 +23,7 @@
#include "base/posix/eintr_wrapper.h"
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/run_loop.h"
#include "base/strings/escape.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
@@ -270,8 +271,21 @@ std::string GetErrorDescription(int error_code) {
bool XDGUtil(const std::vector<std::string>& argv,
const base::FilePath& working_directory,
const bool wait_for_exit,
const bool focus_launched_process,
platform_util::OpenCallback callback) {
base::LaunchOptions options;
if (focus_launched_process) {
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
base::RepeatingClosure quit_loop = run_loop.QuitClosure();
base::nix::CreateLaunchOptionsWithXdgActivation(base::BindOnce(
[](base::RepeatingClosure quit_loop, base::LaunchOptions* options_out,
base::LaunchOptions options) {
*options_out = std::move(options);
std::move(quit_loop).Run();
},
std::move(quit_loop), &options));
run_loop.Run();
}
options.current_directory = working_directory;
options.allow_new_privs = true;
// xdg-open can fall back on mailcap which eventually might plumb through
@@ -303,11 +317,12 @@ bool XDGOpen(const base::FilePath& working_directory,
const bool wait_for_exit,
platform_util::OpenCallback callback) {
return XDGUtil({"xdg-open", path}, working_directory, wait_for_exit,
std::move(callback));
/*focus_launched_process=*/true, std::move(callback));
}
bool XDGEmail(const std::string& email, const bool wait_for_exit) {
return XDGUtil({"xdg-email", email}, base::FilePath(), wait_for_exit,
/*focus_launched_process=*/true,
platform_util::OpenCallback());
}
@@ -376,7 +391,8 @@ bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail) {
argv = {"gio", "trash", filename};
}
return XDGUtil(argv, base::FilePath(), true, platform_util::OpenCallback());
return XDGUtil(argv, base::FilePath(), true, /*focus_launched_process=*/false,
platform_util::OpenCallback());
}
namespace internal {

View File

@@ -49,10 +49,12 @@ class V8Serializer : public v8::ValueSerializer::Delegate {
}
DCHECK(wrote_value);
std::pair<uint8_t*, size_t> buffer = serializer_.Release();
DCHECK_EQ(buffer.first, data_.data());
out->encoded_message = base::make_span(buffer.first, buffer.second);
const auto [data_bytes, data_len] = serializer_.Release();
DCHECK_EQ(std::data(data_), data_bytes);
DCHECK_GE(std::size(data_), data_len);
data_.resize(data_len);
out->owned_encoded_message = std::move(data_);
out->encoded_message = out->owned_encoded_message;
out->sender_agent_cluster_id =
blink::WebMessagePort::GetEmbedderAgentClusterID();

View File

@@ -12,37 +12,22 @@ namespace electron::api::context_bridge {
ObjectCache::ObjectCache() = default;
ObjectCache::~ObjectCache() = default;
void ObjectCache::CacheProxiedObject(v8::Local<v8::Value> from,
void ObjectCache::CacheProxiedObject(const v8::Local<v8::Value> from,
v8::Local<v8::Value> proxy_value) {
if (from->IsObject() && !from->IsNullOrUndefined()) {
auto obj = from.As<v8::Object>();
int hash = obj->GetIdentityHash();
proxy_map_[hash].emplace_front(from, proxy_value);
}
if (from->IsObject() && !from->IsNullOrUndefined())
proxy_map_.insert_or_assign(from.As<v8::Object>(), proxy_value);
}
v8::MaybeLocal<v8::Value> ObjectCache::GetCachedProxiedObject(
v8::Local<v8::Value> from) const {
const v8::Local<v8::Value> from) const {
if (!from->IsObject() || from->IsNullOrUndefined())
return v8::MaybeLocal<v8::Value>();
return {};
auto obj = from.As<v8::Object>();
int hash = obj->GetIdentityHash();
auto iter = proxy_map_.find(hash);
if (iter == proxy_map_.end())
return v8::MaybeLocal<v8::Value>();
const auto iter = proxy_map_.find(from.As<v8::Object>());
if (iter == proxy_map_.end() || iter->second.IsEmpty())
return {};
auto& list = iter->second;
for (const auto& pair : list) {
auto from_cmp = pair.first;
if (from_cmp == from) {
if (pair.second.IsEmpty())
return v8::MaybeLocal<v8::Value>();
return pair.second;
}
}
return v8::MaybeLocal<v8::Value>();
return iter->second;
}
} // namespace electron::api::context_bridge

View File

@@ -5,19 +5,18 @@
#ifndef ELECTRON_SHELL_RENDERER_API_CONTEXT_BRIDGE_OBJECT_CACHE_H_
#define ELECTRON_SHELL_RENDERER_API_CONTEXT_BRIDGE_OBJECT_CACHE_H_
#include <forward_list>
#include <unordered_map>
#include <utility>
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_observer.h"
#include "shell/renderer/electron_render_frame_observer.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "v8/include/v8-local-handle.h"
#include "v8/include/v8-object.h"
namespace electron::api::context_bridge {
using ObjectCachePair = std::pair<v8::Local<v8::Value>, v8::Local<v8::Value>>;
/**
* NB: This is designed for context_bridge. Beware using it elsewhere!
* Since it's a v8::Local-to-v8::Local cache, be careful to destroy it
* before destroying the HandleScope that keeps the locals alive.
*/
class ObjectCache final {
public:
ObjectCache();
@@ -29,8 +28,15 @@ class ObjectCache final {
v8::Local<v8::Value> from) const;
private:
// object_identity ==> [from_value, proxy_value]
std::unordered_map<int, std::forward_list<ObjectCachePair>> proxy_map_;
struct Hash {
std::size_t operator()(const v8::Local<v8::Object>& obj) const {
return obj->GetIdentityHash();
}
};
// from_object ==> proxy_value
std::unordered_map<v8::Local<v8::Object>, v8::Local<v8::Value>, Hash>
proxy_map_;
};
} // namespace electron::api::context_bridge

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