mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
28 Commits
v33.0.0-al
...
v33.0.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18bd295873 | ||
|
|
748f293400 | ||
|
|
7a04a77ed0 | ||
|
|
810a6d6ef4 | ||
|
|
27aea84411 | ||
|
|
2aa2611f76 | ||
|
|
57aeb9dfc6 | ||
|
|
454218bd2b | ||
|
|
c56673f83d | ||
|
|
87fc32f0aa | ||
|
|
c7380437aa | ||
|
|
511dece7ff | ||
|
|
12ce546ded | ||
|
|
67da1f2f9f | ||
|
|
8d9ed52c72 | ||
|
|
8d76b8dba9 | ||
|
|
6f68a40430 | ||
|
|
cf8a97a924 | ||
|
|
37a7cb2c11 | ||
|
|
d92770a275 | ||
|
|
60264f30d8 | ||
|
|
b31a4dcdf3 | ||
|
|
1d3200d7da | ||
|
|
bf10a437bc | ||
|
|
60247e685d | ||
|
|
5d340577b6 | ||
|
|
40316df338 | ||
|
|
e92eb210bb |
18
.github/actions/build-electron/action.yml
vendored
18
.github/actions/build-electron/action.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -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
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<<(
|
||||
@@ -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
|
||||
|
||||
42
patches/node/build_compile_with_c_20_support.patch
Normal file
42
patches/node/build_compile_with_c_20_support.patch
Normal 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++',
|
||||
},
|
||||
}],
|
||||
@@ -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'
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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&)>;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
17
shell/browser/api/electron_api_desktop_capturer_mac.mm
Normal file
17
shell/browser/api/electron_api_desktop_capturer_mac.mm
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -36,7 +36,7 @@ class BadgeManagerFactory : public BrowserContextKeyedServiceFactory {
|
||||
~BadgeManagerFactory() override;
|
||||
|
||||
// BrowserContextKeyedServiceFactory
|
||||
KeyedService* BuildServiceInstanceFor(
|
||||
std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
|
||||
content::BrowserContext* context) const override;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 << ")";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user